fluentdの複数実行

最近fluentdを触り始めたwyukawaです。こんにちは。

今回は最近やったことについて書いてみたいと思います。まあ正確にいうと僕がやったというよりほとんど別の人がやったわけですが忘れないうちにメモっておきます。

もともとやっていたことはfluent-plugin-webhdfs経由でHDFSに書くとともにfluent-plugin-flowcounterを使ってトラフィックを計算しfluent-plugin-growthforecast経由でGrowthForecastにリクエストをなげてグラフ化するというものです。黄金パターンですね。

fluentdは内部的にはinput→buffer→outputというふうに処理が分割されています。
buffer溢れが起きると下記のようなエラーを吐きます。今回僕が経験したのはこのエラーです。

emit transaction failed  error="queue size exceeds limit"
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/buffer.rb:180:in `block in emit'
 /usr/local/lib/ruby/1.9.1/monitor.rb:211:in `mon_synchronize'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/buffer.rb:166:in `emit'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/output.rb:517:in `block in emit'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/event.rb:52:in `call'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/event.rb:52:in `each'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/output.rb:507:in `emit'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/output.rb:35:in `next'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/plugin/out_copy.rb:67:in `emit'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/match.rb:38:in `emit'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/engine.rb:113:in `emit_stream'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/engine.rb:98:in `emit'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/plugin/in_forward.rb:130:in `on_message'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/plugin/in_forward.rb:174:in `feed_each'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/plugin/in_forward.rb:174:in `on_read_msgpack'
 /usr/local/lib/ruby/gems/1.9.1/gems/cool.io-1.1.0/lib/cool.io/io.rb:108:in `on_readable'
 /usr/local/lib/ruby/gems/1.9.1/gems/cool.io-1.1.0/lib/cool.io/io.rb:170:in `on_readable'
 /usr/local/lib/ruby/gems/1.9.1/gems/cool.io-1.1.0/lib/cool.io/loop.rb:96:in `run_once'
 /usr/local/lib/ruby/gems/1.9.1/gems/cool.io-1.1.0/lib/cool.io/loop.rb:96:in `run'
 /usr/local/lib/ruby/gems/1.9.1/gems/fluentd-0.10.30/lib/fluent/plugin/in_forward.rb:77:in `run'

チャンクの数であるbuffer_queue_limitを増やしても解決しないという状況でした。
まあドカンと増やせばもしかしたら解決したのかもしれませんが使えるメモリ使用量にも上限がありますからね。

結論からいうとこの問題はfluentdを複数動かすことで解決しました。

もともとfluentdを1つで動かしておりpeak時でなくてもCPU使用率が80%程度ある状態でした。
その状態からoutputの処理が詰まっている、つまりHDFSヘの書き込みがボトルネックになりbuffer内のqueueがdequeされずにbuffer溢れが起きているのではないかと予想したわけです。予想したのは僕じゃないですけどねw
そこでfluentdを複数実行して解決したというわけです。

fluentdの複数実行のやり方ですが、fluentdの設定ファイルを複数用意してポート番号がかぶらないようにします。

アプリ側は下記のように感じでラウンドロビンでfluentdに書き込むようにします。以下の例では3個のfluentdが起動しています。本当は1つのfluentdに書き込んで後で分散させるほうがいいんでしょうけど暫定的にこうしています。

        fluentd_0 = Fluent::Logger::FluentLogger.new(...)
        fluentd_1 = Fluent::Logger::FluentLogger.new(...)
        fluentd_2 = Fluent::Logger::FluentLogger.new(...)
        fluentds = [fluentd_0, fluentd_1, fluentd_2]
        idx=0
        loop {
                …
                fluentds[idx].post(…)
                idx = (idx + 1) % fluentds.length
                ...
        }

また複数のfluentdがfluent-plugin-webhdfs経由で同一ファイルに書き込まないように下記のような感じでポート番号を使って一意になるようにします。${uuid:random}を使ってランダムなファイル名になるようにしてもいいかも。

  path /log/access/%Y%m%d/${hostname}_{ポート番号}.log

fluent-plugin-growthforecastの設定ではsectionのところでポート番号を使って一意になるようにします。

section   traffic_${hostname}_{ポート番号}

fluentdの起動数を増やす場合にあちこちファイルをいじるのはアレなので実際に使うfluentd.confは構成管理対象外にしてテンプレートだけ用意して下記のように置換して使うようにしました。

while read port
do
    cat fluentd.confのテンプレート | sed -e "s/<<PORT>>/${port}/" > …/fluentd-${port}.conf
    fluentd -c …/fluentd-${port}.conf ...
done < ポート番号を記述したファイル

GrowthForecastでは1台のマシンがさばくトラフィックを知りたいと思うので、例えばfluentdを3つ同時起動して以下のようなsectionにしたとするとこの3つを合計して表示するようにします。スタックというやつですね。

  • traffic_host1_24224
  • traffic_host1_24225
  • traffic_host1_24226

そうするとこんな感じになります。

1台のマシンだけじゃなくて全マシンがさばいているトラフィックを知りたい場合は以下のように全fluentdインスタンス分だけGrowthForecastに設定します。複合グラフの複合グラフみたいなことができればこの部分は省力化されますが現状はないようなのでこうします。

  • traffic_host1_24224
  • traffic_host1_24225
  • traffic_host1_24226
  • traffic_host2_24224
  • traffic_host2_24225
  • traffic_host2_24226

そうするとこんな感じになります。



以上。