Fluentdのバッファリングで抑えておくべき大事なポイント

4 min read読了の目安(約3900字

概要

Fluentdで障害設計をする上でバッファリングの概念は切っても切り離せません。

本記事では、ドキュメントだけでは拾いきれないものも踏まえ、

Fluentdのバッファリングで抑えておくべき情報を体系的にまとめます。

バッファリングとは?

Fluentdではログをバッファリングしてまとめて送信するための仕組みが用意されています。

これは下記のような用途に用いることができます。

  • 送信先がダウンしていたときに一時的に保管しておく
  • 送信先のキャパシティに合わせて送信流量を制限する

Fluentdにはメモリ上、もしくは永続化ディスク上にバッファを保管しておく仕組みが用意されています。

バッファの構造

バッファの構造は下記のようになっています。


引用: https://docs.fluentd.org/buffer

Output Pluginごとに一つバッファ領域を持っており「stage」と「queue」という2つのフェーズを持ちます。

まずログが取り込まれるときは、「stage」へと書き込まれ、
これが成功することでInput/Filter Pluginの処理は完了とみなされます。

※例えば、forward pluginのrequire_ack_responseを用いる場合、Aggregator側で、受け取ったログがstageに書き込まれたタイミングでACKを返します。

そして特定のタイミングでqueueにenqueueされ、順番にPluginが扱う送信先に送信されていきます。

stageの構造

stageに書き込まれる時、ログはchunkという単位でグルーピングされてまとめられます。
このchunkは、chunk keyという指定されたkeyをもとに、同じ値を持つグループでまとめられます。
chunk keyには、時間、タグ、特定のレコードを指定することができます。

stageからenqueueする処理

Fluentdのプロセスは、1つ専用のスレッドを作り、
そのスレッドで、stageにいるchunkをすべて走査し、
条件にあてはまるchunkをenqueueしていくという処理をintervalごとにループして繰り返し実行しています。

この辺りの処理

flush modeについて

stageからqueueに追加されるときの動作には、4つのモードがあります。

これらはflush_modeと呼ばれ、bufferのconfigで指定可能です。

flushと呼ばれていますが、この文脈ではstageからqueueへの追加されるまでの処理を「flush」としています。

lazy mode

時刻と期間をベースにchunkを分けてflushしていくmodeです。

timekeyパラメータに指定されている期間と時刻をもとにchunkを分割します。

例えばtimekeyに1hを指定した場合、
12:00 ~ 12:59の間のログ,
13:00 ~ 13:59の間のログのようにグループ分けされ、1hごとにenqueueされます。

interval mode

flush intervalごとにenqueueしていくmodeです。

lazyと違って時刻の考慮はなく、シンプルに一定時間ごとにenqueueします。

flush_invervalという名前なのでわかりにくいですが、ログの送信先に送信されるまでのintervalではなく、あくまでもenqueueされるまでのintervalです。

immediate mode

chunkに書き込まれた瞬間にenqueueされるモードになります。

default mode

defaultでは、timekeyが指定されていればlazy、そうでなければintervalで実行されます。

chunkがenqueueされるタイミング

chunkがenqueueされるタイミングは2つあります。

  1. flush intervalもしくはtimekeyの時間ごとのタイミング
  2. chunk sizeが指定したサイズ以上になったタイミング

buffering parametersのように、chunkはsize limitやrecord数を指定でき、その制限を超えたタイミングでenqueueされます。

Buffer Overflowした時の挙動

buffering parametersに書かれているように、bufferの領域にはサイズ上限が設けられています。

これ以上のサイズのデータがたまるとBuffer Overflow Errorを起こします。

これが発生した時、どういう挙動になるかはInput Pluginのハンドリング次第になります。

よく使われるtail pluginの場合は、Buffer Overflowが収まるまで何度も同じ行の読み込みを繰り返し(1秒ごと、もしくはファイルの変更ごと)、成功するまではpos fileの更新を止めます。

この辺りの処理

設定次第ですが、ログがrotateされる前にバッファが復旧される必要があります。

あまりにもBuffer Overflowが発生する場合は、スループットが追いついていないのでスケールアップするなり、バッファサイズの見直しなりが必要になります。

ちなみにBuffer Overflow ErrorはRecoverableなErrorなため、secondaryなどをバックアップ先として指定していてもこのケースでは使用されません。

queueのサイズが制限に達した時の挙動

enqueueされたchunkの数が、queued_chunks_limit_sizeで指定された数に達している場合、chunkの数がはけるまでenqueueされません。

しかしデータが失われるわけではなく、前述したループ処理で、単にenqueueが見送られるだけになります。

この辺りの処理

ただflush_at_shutdownをtrueにしている場合、shutdown時のenqueue処理ではqueued_chunks_limit_sizeは無視されます。

ログの送信が失敗したときの挙動

ここに書かれている通り、成功するまで、もしくはリトライの制限に引っかかるまでリトライを行います。

リトライにはバックオフがあり、retry_max_intervalに達するまでリトライの間隔が延び続けます。

回数制限をつけたり、無限にリトライするように指定することができます。

また、この間にもstageにはどんどん書き込みが入るのでバッファの使用量には注意が必要です。

※prometheus形式のmetrics exportを有効にしている場合、使用可能な残りのバッファサイズを監視できます

まとめ

バッファを使うにあたって、知っておくべき概念と、細かい挙動についてまとめました。

コードを読まないと分からない点もふくめてまとめたので、KubernetesでFluentdの信頼性を担保するための3つの観点と合わせて、実際にFluentdをProduction運用にする際に役立てられれば幸いです。