fluent-plugin-amplifier-filter released!
流れてくるメッセージから指定したフィールドの値を定数倍して再emitする fluent-plugin-amplifier-filter をリリースした。
tagomoris/fluent-plugin-amplifier-filter · GitHub
fluent-plugin-amplifier-filter | RubyGems.org | your community gem host
なんに使うものかというと、以前作った fluent-plugin-sampling-filter と組み合わせるため。具体的に書くと以下のような構造になる。
- 監視系の fluentd は基本的に統計的な処理しかしない
- 全件データはいらないから sampling-filter を通した一部のログだけを流したい
- datacounter の出力を使って監視とかやりたい
- datacounter はパーセンテージとレート(msg/sec)を出す
- 監視やるときにサンプリングレートのことを考えたくない
- 流量の多いサービスと少ないサービスでは sampling-filter のサンプリングレートを変えてある
- あまりにサンプル数が少ないと適切な統計量が取れなくなるので
- しかし監視を設定するときに、サンプリングレートまで考慮したレートを閾値に指定したり値を読ませたりするのは混乱を招く
このような動機があり、監視系で流量を抑えるためにサンプリングはしたい、しかし指標値は実メッセージ数ベースで出したい(近似値でよい)、という目的を満たすためにこのようなプラグインを書いた。
だいたい以下のような感じで使ってる。
# 全件流れてる側のFluentd <match hoge.**> type copy <store> # 全件に必要な処理 </store> <store> type sampling-filter interval 10 add_prefix sampled.10 </store> </match> # 別のタグで sampled.100 などもある <match sampled.**> type forward # 監視系に送る </match>
監視側では以下のように。(まだ実際には設定してないパターンもあるけど、だいたいこんな感じで使う、という話)
<source> type forward </source> <match sampled.10.*> type route remove_prefix sampled.10 <route {service1,service2}> add_tag_prefix referer_count.10 copy </route> <route service2> add_tag_prefix url_user_register_count.10 copy </route> # 以下チェックしたい個別の内容ごとに add_tag_prefix + copy して流してやる <route *> add_tag_prefix httpstatus_count.10 copy </route> </match> <match sampled.100.*> # sampled.10 と同様に書いていく </match> <match referer_count.10> type datacounter tag 10.datacount.referer aggregate tag input_tag_remove_prefix referer_count.10 count_key referer pattern1 yahoo ^https?://[-.a-zA-Z0-9]\.yahoo\.co(m|\.jp)(/|$) # 以下パターン </match> # 以下監視内容毎に datacounter を tag RATE.datacount.HOGE のようなタグ出力で記述 <match 10.datacount.*> type amplifier_filter ratio 10 remove_prefix 10 key_pattern .*_(rate|count)$ </match> <match 100.datacount.*> type amplifier_filter ratio 100 remove_prefix 100 key_pattern .*_(rate|count)$ </match> <match datacount.*> # 以下 GrowthForecast などへの出力 </match>
このように SAMPLING_RATE.tag のようなタグにしておくことで、最後の方で一括して amplifier-filter にかけて実レート値への変換をやっておける。便利。なお監視項目への振り分けは https://twitter.com/#!/frsyuki/statuses/171795899297697792:out_route を使ってる。たいへん便利。
こんな感じで、負荷を抑えつつさくさくと監視をあれこれやりたいなあ、という人にはたいへん向いているのではないかと思う。
fluent-plugin-forest released!
現状のfluentdでは、タグを動的に扱う方法がいまいち無い。具体的に言うと設定項目にタグに応じて変化するような指定をしたい場合、タグごとに分けて書くしかない。例えば out_file で出力先ファイル名をタグに応じてつけたい場合、タグの数だけ match 節を書く必要がある。
<match hoge> type file path /var/log/hoge.log </match> <match pos> type pos path /var/log/pos.log </match> # 以下いっぱい
これには極めて簡単にわかる範囲で、ふたつの大きな問題がある。
- 多数のタグを扱う場合、設定ファイル全体のボリュームが肥大化して管理コストが増大する(品質が低下する)
- 新しく扱うタグが増える場合、設定ファイルの更新と適用が必要となり、管理コストが増大する
既に手元でこの問題に悩まされていて、HoopServerに書き込みに行くfluentd*1の設定ファイルの行数が既に18タグ分、417行にわたっていてかなり末期的な症状を呈していた。大部分コピペなんだけどところどころ違うのがたちが悪い。
ぶっちゃけ、このように書いたらあとは適当にfluentdがやってくれたりすると嬉しい。
<match *> type file path /var/log/__TAG__.log </match>
無いのなら、作ってしまえ、プラグイン
fluent-plugin-forest
ということで fluent-plugin-forest を作った。とりあえずまる1日動かしたところ問題なさそうなのでrubygems.orgにリリースし、手元では全台に配布済。
tagomoris/fluent-plugin-forest · GitHub
fluent-plugin-forest | RubyGems.org | your community gem host
これを使うことで417行あった設定ファイルが55行まで圧縮され、しかもタグが増えても再起動も要らず、タグごとの設定の違いもたいへん分かりやすいという天国的な状況に! ヤッタネ!
ちなみに上の単純な out_file のパターンだとこのようになる。
<match *> type forest subtype file <template> path /var/log/__TAG__.log </template> </match>
どう使えるか
fluent-plugin-forestは以下のように動作する。
- タグひとつにつき output plugin のインスタンスをひとつ生成する
- 設定中の任意の場所で __TAG__ というプレースホルダを使用できる
- この部分はタグ文字列に置き換えられた設定として output plugin に渡される
- なおここには remove_prefix / add_prefix が評価された後のタグが入る
- 設定では template セクションと case セクションを使用できる
- template セクションは全タグ文字列に対して適用する設定内容
- case セクションは case hoge.** のようにパターンを記述でき、そのパターンにマッチする場合だけ適用する設定内容
- 現状 server セクションや store セクションなどを用いる output plugin について、そのサブセクション内だけを置き換えるような書き方はできない
このあたりを理解して、自分の手元の実例を挙げるとこうなる。まず従来の設定内容。sourceや最後のforwardまわりの細かい設定は省いてある。
# original configurations <source> type forward </source> <match converted.serviceX> type copy <store> # Hoop server への書き込み、流量が多いのでノードごとにファイルを分けるもので NODENAME には実際にはfluentd動作ノード名がサーバごとに書かれる type hoop hoop_server hoop.server.local:14000 path /hoop/log/%Y%m%d/serviceX-%Y%m%d-%H.NODENAME.log username hoopuser flush_interval 60s output_include_time false output_include_tag false output_data_type attr:hhmmss,vhost,path,method,status,bytes,duration,referer,rhost,userlabel,agent,FLAG,status_redirection,status_errors,rhost_internal,suffix_miscfile,suffix_imagefile,agent_bot add_newline true </store> <store> # 監視用サンプリング設定 type sampling_filter interval 100 remove_prefix converted add_prefix sampled.100 </store> </match> <match converted.serviceY> # 流量の少ないサービス、path指定NODENAMEがないこと、サンプリングレートが 10 であることのみ異なる type copy <store> type hoop hoop_server hoop.server.local:14000 path /hoop/log/%Y%m%d/serviceY-%Y%m%d-%H.log username hoopuser flush_interval 60s output_include_time false output_include_tag false output_data_type attr:hhmmss,vhost,path,method,status,bytes,duration,referer,rhost,userlabel,agent,FLAG,status_redirection,status_errors,rhost_internal,suffix_miscfile,suffix_imagefile,agent_bot add_newline true </store> <store> type sampling_filter interval 10 remove_prefix converted add_prefix sampled.10 </store> </match> # 以下16回(!)くりかえし <match sampled.**> type forward <server> host watcher01.local </server> <server> host watcher02.local </server> </match>
眺めるだけでアタマが痛いですね!!!!! これを fluent-plugin-forest を使うことによりこれだけに圧縮した。
<source> type forward </source> <match converted.*> type copy <store> type forest subtype hoop remove_prefix converted <template> hoop_server hoop.server.local:14000 username edge-dev flush_interval 60s output_include_time false output_include_tag false output_data_type attr:hhmmss,vhost,path,method,status,bytes,duration,referer,rhost,userlabel,agent,FLAG,status_redirection,status_errors,rhost_internal,suffix_miscfile,suffix_imagefile,agent_bot add_newline true </template> <case {serviceX,serviceZ}> path /hoop/log/%Y%m%d/__TAG__-%Y%m%d-%H.NODENAME.log </case> <case *> path /hoop/log/%Y%m%d/__TAG__-%Y%m%d-%H.log </case> </store> <store> type forest subtype sampling_filter remove_prefix converted <case {serviceA,serviceX}> interval 100 add_prefix sampled.100 </case> <case *> interval 10 add_prefix sampled.10 </case> </store> </match> <match sampled.**> type forward <server> host watcher01.local </server> <server> host watcher02.local </server> </match>
これで全部。どうよこの画期的にわかりやすい(?)設定! これでタグが増えてもデフォルトルールに従うだけなら設定変更はいらないし、メンテナンスも極めて簡単になって言うことなしですね!!!!!