タグによってforward先を一意にしつつ負荷分散したい時に使えるかもしれないfluent-plugin-hash-forward #fluentd

そろそろ fluentd 触ろうかと思ってはや 1 年近くが経とうとしている今日この頃。ふと構成を色々考えてたんですが、ひとつ気になる問題がありました。

forward とか roundrobin とかでログの転送先をいろんなサーバにすることがあると思うのですが、単純な count up 以外の集約を行おうとすると、サーバ(正確には flunetd のインスタンス)が別れてるとちょっと面倒ですよね。例えば、アクセスログから 1 分辺りのステータスコードによって datacounter するとして、それを出力してるサーバ毎にやりたいと思った時に、一つのサーバからの出力がラウンドロビンされていろんな fluentd に分かれていると、ちょっと厳しい。

また、例えば 1 サーバで in_forward の受け口は 1 つにしつつ、ローカルに別プロセスでいくつも fluentd を上げてそれらにロードバランスしようかと思った時にも、単純にラウンドロビンしちゃうと、せっかく 1 サーバに集めたのに処理がプロセスで別れてしまって悲しい。

なにができればいいのかなぁと考えると、例えばアクセスログの処理用の worker サーバをいくつか横に並べた時に、「accesslog.server1」とタグがあったら、worker1 に、「accesslog.server2」とタグがあったら worker2 に、みたいな感じでタグによって一意に決まると嬉しそう。

ただ、こんな単純なルールだとタグの種類だけ worker を準備しないといけないわけで、もうちょっと賢くやりたい。というわけで、タグを murmurhash で int に変換して、それを worker の数で剰余を取ってバランスするといいかなと思って、fluent-plugin-hash-forward というプラグインを試しに書いてみました。

仕組みとしては tagomoris さんの fluent-plugin-forest を大いに参考にさせて頂いています(というかコピペしてます><)。config で指定したサーバの数だけ out_forward(server は 1 つのみ)のインスタンスを作成して、emit の中で tag を murmurhash してどのサーバに振るかを一意に決めています。

config は以下の様な感じで、見てもらえばわかる通り out_forward っぽいです。<server>の部分と add/remove_prefix 以外は実際内部的に生成する out_forward にそのまま渡されます。

<match pattern>
  type hash_forward
  add_prefix hoge
  flush_interval 1s

  <server>
    host 192.168.1.3
    port 24224
  </server>
  <server>
    host 192.168.1.4
    port 24224
  </server>

  <secondary>
    type file
    path /var/log/fluent/forward-failed
  </secondary>
</match>

こんな感じで server を並べて書いてあげると tag の文字列によって良い感じに負荷分散してくれると思います。使いどころがあるのかどうかは定かではないですが、興味ある方がいればインプット頂ければと思います!