読者です 読者をやめる 読者になる 読者になる

Fluentdの知られていない6つのこと

Fluentdの知られていない6つのこと

本当に知られていないかはわからないです。 公式にはあまり説明されていなかったり調べてもなかなか見つからないことが多いと個人的に思ったものを集めました。 機能や言葉の細かい説明は省いているのである程度使っている人が対象です。

out_copyはshallow copy

Fluentdで最初に使うであろうビルドインされているout_copyプラグインですが、実はデフォルトではメッセージをdeep copyしないため意図しない結果になることがあります。

<match test>
  type copy
  <store>
    type record_modifier
    tag test.aa
    foo bar
  </store>
  <store>
    type retag
    tag test.bb
  </store>
</match>

<match test.{aa,bb}>
  type stdout
</match>

例えばこのような設定の場合、test.aa のみに foo=bar が追加されることが期待されます。 しかし実際には test.bb にも foo=bar が追加されてしまいます。 これはout_copyプラグインがshallow copyになっているためです。

この問題を解決するためのトリッキーな方法としてメッセージを加工するプラグインは最後に持ってくるなどの方法があったりしますが、処理する順番が今後も記述順のままである保証もないので微妙なところです。

現最新版の v0.10.39 ではout_copyプラグインでdeep copyを行う deep_copy が追加されました。 デフォルトではoffになっているので、必要に応じて on にしましょう。

TCP Heartbeat

Fluentdの肝であるout_forwardは接続先の生存確認(heartbeat)のために定期的にパケットを送信しています。 このパケットの送信はUDPで行われているため、環境によってはTCPは開いてるけどUDPは閉じてるということがあり、別の環境では動いたのにこの環境では動かない理由がわからない…などということがあったりします。

実はこの生存確認の方法も heartbeat_type オプションで指定することが可能で、:tcp とすることでTCPによる確認が可能になります。 自分は最近まで知らなかったですが、結構前から実装されているので気軽に使っても良さそうです。

secondaryは実はなんでも使える

公式のドキュメントでは out_forwardプラグイン のオプションとして説明されている secondary オプションですが、細かい説明もないのでデフォルトのままなんとなく使っているということも多いと思います。しかし、 この secondary オプションは、使いこなせると更に強固なシステムを構築することもできるので、ぜひ知っておいた方が良いです。

まず、 secondary オプションは out_forwardプラグイン以外にも使うことができます。 もともとこのオプションの目的が"BufferedOutputプラグインでチャンクに入りきらなくなったときにどうするか"というもののため、BufferedOutputプラグインであれば全てのプラグインで使うことができます。 out_forwardに限らず、詰まる可能性があるところでは使っておいた方が良いでしょう。

次に、 secondary オプションで指定できるtypeは file だけではありません。 secondaryで指定する設定はOutputプラグインの設定と全く同じなので、実はどんなプラグインでも書くことはできます。つまりtypeにfileを指定した場合は out_fileプラグインの全てのオプションを使うことができます。 ただし、内部の実装を完全に理解している人以外はout_file以外を使うべきではありません。 その理由はBufferedOutputのバッファの扱い方がプラグインにより異なるため、組み合わせによってはうまく動作しません。というよりほとんどの場合そのまま使えるプラグインはないでしょう。

このあたりはFluentd本体を改善することでなんとかなりそうですがまだ先は長そうです。

out_fileのログに対してシンボリックリンクを生成する

out_fileプラグインを使って最初に驚くことがログファイル名です。 オプションでフォーマットを指定できるので、例えば time_slice_format %Y%m%d のように指定したら foo.20130928.log とかになるんだろうなーと思ったら、実際には foo.20130928.b4e76e103eebf8c2f とか謎のsuffixがついて困るということがあります。

大抵のログローテーション付きログの仕組みにあるように、Fluentdにも最新のログファイルに対するシンボリックリンクを貼る機能があります。 path オプションと同様に symlink_path オプションでシンボリックリンクのパスを指定すると常に最新のファイルを差し続けるくれます。 ただし、実装の仕様上一時的にシンボリックリンク先がなくなることがことがあります。その場合も新しいログができると自動的にリンク先を変えてくれます。 シンボリックリンクを参照するときには tail -F で見るとよいでしょう。

こちらも最新版の v0.10.39 から利用することができます。

num_threadsで並列処理数を指定できる

BufferedOutputを並列に行うためのオプションとしてnum_threadsがあります。 ただし、このオプションは誰も使っていない可能性すらあります。

BufferedOutputプラグインで num_threads を指定することで、writeの処理を並列に行うことができます。実装としてはキューにたまった処理を並列に取り出して各スレッドで処理するというようになっていて、見た目上は問題無さそうです。今のところ自分ではまだ試していないです。

ソースコードを読んでいて少し気になったところとしては、try_flushでキューが空じゃないとエンキューしないので、chunk_limitを頻繁に超えるようなものじゃないと複数スレッドあってもデキューできないような気がしました。

BufferedOutputをバルク処理以外に使うのは微妙

BufferedOutputの特徴としてリクエストのバッファリングと再送機能によってリクエストの送信を保証できるということが挙げられます。 よって、失敗する可能性のある処理は全てBufferedOutputで行いたいと思います。

BufferedOutputは溜まったリクエストを一括で処理することを想定している(ように思える)ので、個別のリクエストを処理するようなプラグインを書くのは注意が必要です。 その理由は再送処理の粒度がチャンク単位となっているからです。 5つのリクエストがバッファに溜まっている時に5回のリクエストに分けて送信するとします。この時に、2つ目目までは成功したけど3つ目で失敗して再送処理をFluentdに任せると、失敗した3つめからではなく最初のリクエストからやりなおしになります。 よって、リクエストを個別に行う場合は冪等性が保証されている必要があります

この問題を解決しようと汎用的なOutputクラスを作ろうとしてましたが、中の仕組みを保ったまま作るのが意外と難しいので止まってます。 誰か作ってください!

さいごに

Fluentdは使うのが簡単で機能も十分にありますが、やはり使っていく上でプラグインを書いたり安全性を確認したりすると内部の実装を知る必要が出てきます。 実装はそれほど複雑ではなく行数も短いためすぐに読めると思います。 読んでみると改善すべき点も意外といっぱいあったりするので、使っている人は読んで貢献していきましょう。

記事に間違い等ありましたらご連絡下さい。