2022年3月31日にFluentdの最新版となるv1.14.6をリリースしました。
クリアコードはFluentdの開発に参加し、リリースを含めたメンテナンス作業を行っています。
今回はv1.14.6のリリースについて、主なポイントを紹介します。
Fluentd v1.14.6の最新動向
リトライのバグ修正
Fluentdのリトライ機能、皆さん活用されているでしょうか?
リトライは、 https://docs.fluentd.org/output#control-retrying で記載されているように、多くの設定を持ち、様々な都合に合わせて使えます。
今回、デフォルトのretry_typeであるexponential_backoffについて、リトライの発生間隔と回数、およびセカンダリへの移行タイミングの計算に間違いがあることが判明し、それらを修正しました。
複雑な内容になるため、まずはリトライの正しい仕様をご説明し、その後で今回の変更点についてご紹介します。
リトライの正しい仕様
exponential_backoffのリトライでは、リトライの発生間隔が指数関数的に増加していきます。デフォルトの設定では最初のOutput処理1に失敗すると、1秒後にリトライを行い、それも失敗するとさらに2秒後にリトライを行い、次はさらに4秒後、次はさらに8秒後、...、というように2の倍々で間隔が増えていきます。最初のOutput処理に失敗した時点から数えると、1秒後、3秒後、7秒後、15秒後、...、というタイミングで、成功するまでリトライを繰り返すことになります2。n回目のリトライ3は1つ前の処理から2^(n-1)秒後に発生するので、最初の処理から数えると、等比級数の総和である2^n - 1秒後が発生タイミングになります。
デフォルトではretry_timeoutの値である72時間が経過するまでリトライを繰り返します。この場合、18回目のリトライが約73時間(262143秒)後に発生する計算となり、72時間をオーバーします。このようにオーバーする場合は、計算通りの時間ではなく、上限値の時間(この場合は72時間)に最後のリトライを行います。よって18回目のリトライを最初の処理からちょうど72時間後に行い、それも失敗した場合はリトライを諦めてそのデータが失われます。このようにデータが失われることを回避するためには、セカンダリを設定するか、retry_foreverをtrueに設定します4。
このリトライの動作を調整する主な設定値についても少し触れておきます。retry_max_timesでリトライの最大回数を設定できます。この場合、この設定による回数制限とretry_timeoutによる時間制限のどちらか一方をオーバーするまでリトライを行います。また、retry_max_intervalでリトライの間隔に上限を設定することができます。指数関数的に大きくなるリトライ間隔ですが、長くても1日に1回はリトライしてほしい、といった場合もあるでしょう。そういった場合にこれを3600秒などに設定することができます。
最後に、重要な機能であるセカンダリとリトライの関係についてご説明します。セカンダリを設定した場合、retry_secondary_thresholdの割合に基づいたタイミングでセカンダリへ移行します。デフォルトでこの値は0.8であり、セカンダリを設定しなかった場合にリトライがタイムアウトしていた時間(最後のリトライが行われたはずの時間)、の8割のポイントでセカンダリへ移行します。ちょうどこのポイントでセカンダリ出力を行い、万が一それも失敗する場合は間隔をリセットしてセカンダリへのリトライを続けます。この場合、残りの2割の時間でセカンダリのリトライを続けることになります。
変更点
間違いは3つありました。
- 発生間隔
- デフォルトでリトライの各間隔が1,2,4,8,...,秒であるべきところが、各間隔ではなく最初の処理から数えて1,2,4,8,...,秒後に行う計算になっていました。n回目のリトライは
2^n - 1秒後に行うべきですが、2^(n-1)秒後に行っていました。
- デフォルトでリトライの各間隔が1,2,4,8,...,秒であるべきところが、各間隔ではなく最初の処理から数えて1,2,4,8,...,秒後に行う計算になっていました。n回目のリトライは
- 発生回数
- リトライの最大回数(
retry_max_times)を設定している場合、その最大回数より1回多くリトライを実行していました。
- リトライの最大回数(
- セカンダリへの移行タイミング
- 移行の割合(
retry_secondary_threshold)は、最後のリトライを行うはずだった時間に対して適用するべきですが、最後の1回前のリトライ時間に対して適用しており、想定よりも早く移行が発生していました。
- 移行の割合(
本リリースにより、これらをすべて修正しました。以下は具体的な動作比較になります。
リトライの最大回数(retry_max_times)を10回に設定した場合
| リトライ回数 | 従来の経過時間 | 本リリースにおける経過時間 |
|---|---|---|
| 1th | 1s | 1s |
| 2th | 2s | 3s |
| 3th | 4s | 7s |
| 4th | 8s | 15s |
| 5th | 16s | 31s |
| 6th | 32s | 63s |
| 7th | 64s | 127s |
| 8th | 128s | 255s |
| 9th | 256s | 511s |
| 10th | 512s | 1023s |
| 11th | 1024s | 実行されない |
加えてセカンダリを設定した場合
| リトライ回数 | 従来の経過時間 | 本リリースにおける経過時間 |
|---|---|---|
| 1回目 | 1s | 1s |
| 2回目 | 2s | 3s |
| 3回目 | 4s | 7s |
| 4回目 | 8s | 15s |
| 5回目 | 16s | 31s |
| 6回目 | 32s | 63s |
| 7回目 | 64s | 127s |
| 8回目 | 128s | 255s |
| 9回目 | 256s | 511s |
| 10回目 | 409s (セカンダリ) | 818s (セカンダリ) |
| 11回目 | 410s (セカンダリ) | 実行されない |
TCP, TLSサーバーの機能を持つInputプラグインにlinger_timeoutオプションを追加
serverプラグインヘルパーは、プラグインにUDP, TCP, TLSのサーバー機能を提供します。代表的なものでは、in_udp, in_tcp, in_http, in_syslog, in_forwardなどのプラグインがこの機能を利用しています。
従来この機能において、サーバー側が通信を切断する際に待機時間が発生するのを防止するため、強制的に切断を行う仕様になっていました。特にUNIX系のOSでは、FluentdがTCPの切断時にFINを送信せず、RSTをいきなり送信していました。
今回transportセクションの設定にlinger_timeout設定が追加され、これを設定することでこの挙動を変更できるようになりました5。
下のようにlinger_timeoutに1などの正の値を設定することで、TCP接続の切断時に確実にFINを送信させることができます。
<source>
@type tcp
...
<parse>
...
</parse>
<transport tcp>
linger_timeout 1
</transport>
</source>
その他
- rpc_endpointにIPv6も設定できるようになりました。
@ERRORラベルに渡されるレコードに、Filterプラグインによる内容の変更が反映されない問題を修正しました。- fluentdコマンドに
--umaskオプションが追加され、--no-supervisorで起動するFluentdにumask値をセットできるようになりました6。
まとめ
今回の記事では、Fluentd v1.14.6について最新情報をお届けしました。
最新版を使ってみて、何か気になる点があればぜひGitHubで開発チームまでフィードバックをお寄せください!
-
紛らわしいですが、本章で最初のOutput処理とは、1回目のOutput処理、つまりリトライはまだ0回の時点のことを指します。 ↩
-
デフォルトでretry_randomizeが
trueのため、実際は多少ランダムに変動します。 ↩ -
紛らわしいですが、本章でn回目のリトライとは、n+1回目のOutput処理のことを指します。1回目のリトライとは、2回目のOutput処理のことです。 ↩
-
この場合は、Buffer設定で
total_limit_sizeやoverflow_actionを適切に設定しておきましょう。 ↩ -
ソケット設定の1つである
SO_LINGERの設定に用いられます: https://man7.org/linux/man-pages/man7/socket.7.html ↩ -
supervisorも含めて起動する場合は、その環境のumask値を用います。 ↩