ククログ

株式会社クリアコード > ククログ > Fluentd v1.14.6リリース -- リトライの修正と細かな動作改善

Fluentd v1.14.6リリース -- リトライの修正と細かな動作改善

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_forevertrueに設定します4

このリトライの動作を調整する主な設定値についても少し触れておきます。retry_max_timesでリトライの最大回数を設定できます。この場合、この設定による回数制限とretry_timeoutによる時間制限のどちらか一方をオーバーするまでリトライを行います。また、retry_max_intervalでリトライの間隔に上限を設定することができます。指数関数的に大きくなるリトライ間隔ですが、長くても1日に1回はリトライしてほしい、といった場合もあるでしょう。そういった場合にこれを3600秒などに設定することができます。

最後に、重要な機能であるセカンダリとリトライの関係についてご説明します。セカンダリを設定した場合、retry_secondary_thresholdの割合に基づいたタイミングでセカンダリへ移行します。デフォルトでこの値は0.8であり、セカンダリを設定しなかった場合にリトライがタイムアウトしていた時間(最後のリトライが行われたはずの時間)、の8割のポイントでセカンダリへ移行します。ちょうどこのポイントでセカンダリ出力を行い、万が一それも失敗する場合は間隔をリセットしてセカンダリへのリトライを続けます。この場合、残りの2割の時間でセカンダリのリトライを続けることになります。

変更点

間違いは3つありました。

  1. 発生間隔
    • デフォルトでリトライの各間隔が1,2,4,8,...,秒であるべきところが、各間隔ではなく最初の処理から数えて1,2,4,8,...,秒後に行う計算になっていました。n回目のリトライは2^n - 1秒後に行うべきですが、2^(n-1)秒後に行っていました。
  2. 発生回数
    • リトライの最大回数(retry_max_times)を設定している場合、その最大回数より1回多くリトライを実行していました。
  3. セカンダリへの移行タイミング
    • 移行の割合(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_timeout1などの正の値を設定することで、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で開発チームまでフィードバックをお寄せください!

  1. 紛らわしいですが、本章で最初のOutput処理とは、1回目のOutput処理、つまりリトライはまだ0回の時点のことを指します。

  2. デフォルトでretry_randomizetrueのため、実際は多少ランダムに変動します。

  3. 紛らわしいですが、本章でn回目のリトライとは、n+1回目のOutput処理のことを指します。1回目のリトライとは、2回目のOutput処理のことです。

  4. この場合は、Buffer設定total_limit_sizeoverflow_actionを適切に設定しておきましょう。

  5. ソケット設定の1つであるSO_LINGERの設定に用いられます: https://man7.org/linux/man-pages/man7/socket.7.html

  6. supervisorも含めて起動する場合は、その環境のumask値を用います。