Amazon Web Services ブログ
AWSLogs コンテナログドライバーのノンブロッキングモードによるログ損失の防止
この記事は Preventing log loss with non-blocking mode in the AWSLogs container log driver (記事公開日: 2023 年 8 月 3 日) を翻訳したものです。
Introduction
可観測性の向上とトラブルシューティングのために、コンテナログをコンピューティングプラットフォームから、ログ集約サーバーに転送することをお勧めします。実際には、ログサーバーが到達不能になったり、ログを受け入れられなくなる場合があります。ログサーバーの障害に対するアーキテクチャ設計には、トレードオフがあります。サービス所有者は、次の点を検討する必要があります。
- アプリケーションは、トラフィックへの応答 (または作業の実行) を停止し、ログ集約サーバーが復旧するのを待つべきでしょうか? (正確な監査ログがサービスの可用性よりも優先されますか?)
- アプリケーションは、ログサーバーがバッファを使い切る前に復旧することを期待してログをバッファリングしながらトラフィックに対応し続けるべきでしょうか? ログ送信先が利用できないレアケースにおいてログが失われるリスクを受け入れるべきでしょうか?
コンテナのログドライバーでは、このトレードオフは上記 1 の考慮事項に対して「ブロッキング」の設定パラメータ、2 の考慮事項に対して「ノンブロッキング」の設定パラメータで実装されています。AWS ブログの「Choosing container logging options to avoid backpressure」では、Rob Charlton がこのトレードオフを探求し、AWSLogs コンテナログドライバーのデフォルトの「ブロッキング」モードでアプリケーションがどのように動作するかをテストする方法を説明しています。
この記事では、「ノンブロッキング」 について詳しく説明し、AWSLogs ログドライバーを使用したログ損失の試験結果を示します。
ソリューションの概要
AWSLogs ドライバーのモード
Amazon Elastic Container Service (Amazon ECS) では、AWSLogs ログドライバーがコンテナの stdout と stderr からログをキャプチャし、Amazon CloudWatch Logs に PutLogEvents API 経由でアップロードします。このログドライバーは、モード設定をサポートしており、次のように構成できます。
- ブロッキング (デフォルト): ログを Amazon CloudWatch に即座に送信できない場合、コンテナコードから stdout または stderr への書き込み呼び出しがブロックされ、コードの実行が停止します。アプリケーションのロギングスレッドがブロックされるため、アプリケーションが機能しなくなり、ヘルスチェックの失敗やタスクの終了につながる可能性があります。必要なロググループまたはログストリームを作成できない場合、コンテナの起動に失敗します。
- ノンブロッキング: ログを Amazon CloudWatch に即座に送信できない場合、max-buffer-size 設定で構成されたインメモリバッファに格納されます。バッファを使い切ると、ログが失われます。コンテナコードから stdout または stderr への書き込み呼び出しはブロックされず、即座に実行されます。Amazon ECS on Amazon Elastic Compute Cloud (Amazon EC2) では、必要なロググループまたはログストリームを作成できない場合でも、コンテナの起動は失敗しません。AWS Fargate 上の Amazon ECS では、構成されたモードに関係なく、ロググループまたはログストリームを作成できない場合、コンテナの起動は必ず失敗します。
デフォルトのブロッキングモードから、ノンブロッキングモードへの変更を検討するべきか?
デフォルトのブロッキングモードではアプリケーションの可用性リスクがあるため、サービス所有者はノンブロッキングモードに切り替えることを検討する必要があります。その場合、次のような疑問が生じます。
- max-buffer-size はどのように選択すべきでしょうか? デフォルトの 1 MB サイズでログの損失を防げますか?
- ノンブロッキングモードを使うと、高レートでログを出力するアプリケーションにてログの損失が発生しますか?
これらの質問に答えるため、AWS チームはノンブロッキングモードで AWSLogs ドライバー上でスケールしたログの取り込みテストを実行しました。
推奨される max-buffer-size の値はどれですか?
ノンブロッキングモードを選択する場合、このテストから推奨される Amazon ECS タスク定義の設定は以下のとおりです。
バッファのサイズを決定する変数は何ですか?
最大バッファサイズに影響を与える主な変数は、アプリケーションがデータを出力する頻度とログのスループットです。
CloudWatch Metrics の IncomingBytes メトリクスを使用して、ロググループへの取り込みレートを追跡します。すべてのコンテナがほぼ同じレートで送信すると想定すると、ロググループの取り込みレートをコンテナの数で割ることで、個々のコンテナのレートが分かります。
各コンテナからのログのスループットを多めに見積もることをお勧めします。ログ出力は、特にインシデント発生時に一時的に急増する可能性があります。可能であれば、負荷テストや最近のインシデント時のスループットを計算してください。スループットのバーストに対応するため、1 分以下の時間間隔でのピークログ出力レートを使用してください。
テストでわかったことは何ですか?
この記事で説明した結果はパフォーマンスを保証するものではないことにご注意ください。AWS チームが実施したテストの結果を単に共有しているだけです。
ログ集約サーバーが利用可能で正常な場合の主な所見は次のとおりです。
- max-buffer-size が 4 MB 以上の場合、コンテナからの出力ログレートが 2 MB/s 以下であれば、ログの損失は発生しません。
- max-buffer-size が 25 MB 以上の場合、コンテナからの出力ログレートが 5 MB/s 以下であれば、ログの損失は発生しません。
- 6 MB/s を超えると、AWSLogs ドライバーのパフォーマンスは予測可能性と一貫性が低くなります。たとえば、100 MB のバッファと 7 MB/s のテストで異常値による失敗が発生しました。6 MB/s 以上 (持続的またはバースト) でログを出力する場合、時折ログ損失を防ぐことができない可能性があります。
- Amazon EC2 起動タイプと AWS Fargate 起動タイプの Amazon ECS で、結果は同様です。
このドキュメントでは、テスト結果の簡単な要約を示します。起動タイプとログサイズ別に分けられた完全なベンチマーク結果、分析、データは、GitHub で確認できます。
テストはどのように実行されましたか?
ベンチマークに使用したコードは GitHub で確認できます。Amazon EC2 のテストは Docker バージョン v20.10.25 で実行しました。AWS Fargate のテストは プラットフォームバージョン 1.4 で実行しました。
各ログ損失テストは、AWSLogs ドライバーを使用して 1 GB のログデータを Amazon CloudWatch Logs に送信する Amazon ECS タスクで実行しました。そのタスクは次に Amazon CloudWatch Logs をクエリして、すべてのログイベントを取得し、受信したログイベントの数をチェックします。各ログメッセージには、予測可能なシーケンス番号である一意の ID が付いています。1 KB と 250 KB のサイズの単一ログメッセージでテストが実行されました。
数千回のテストを実行し、有意な統計分析を行うための十分なデータを取得しました。
バッファを使い切り、ログが失われているかどうかを知ることができますか?
残念ながら、AWSLogs ログドライバーでは、ノンブロッキングモードのバッファによって失われたログを確認することはできません。Docker デーモンは、ログの損失が発生した際にログステートメントやメトリクスを出力しません。ログ損失メトリクスの提案については、GitHub にコメントしてください。
バッファサイズはアプリケーションで使用可能なメモリにどのように影響しますか?
max-buffer-size 設定は、Go スライス内のメッセージのバイトサイズを制御します。ただし、Go はガベージコレクションされる言語なので、直接メモリ使用量を制約するわけではありません。ある 1 つのテストスイートでは、キューの実際のサイズは平均すると非常に小さく、一般的に 500 KB 未満であることが分かりました。バッファサイズは、レイテンシの期間や、ログのスループットの増加時に一時的に制限値まで上がります。つまり、バッファによって使用されるメモリは瞬間ごとに大きく変動し、Go のガベージコレクションのため、実際のメモリ使用量が設定サイズを超える可能性があります。
コンピューティングプラットフォームはバッファサイズに影響しますか?
テストでは、Amazon EC2 と AWS Fargate の両方で起動した Amazon ECS タスクの結果が同様であることがわかりました。
リージョン間でログを送信する場合、ノンブロッキングモードは安全ですか?
AWSLogs ドライバーは、テストタスクと同じリージョンの Amazon CloudWatch API にログを送信する場合、CloudWatch への接続の待ち時間が短いため、はるかに高い速度で一貫してアップロードできます。リージョン間でのログアップロードは信頼性が低くなります。さらに、リージョンの分離というベストプラクティスに反します。リージョン間のログプッシュはネットワークコストも高くなります。
テスト結果
この記事で説明した結果はパフォーマンスを保証するものではありません。私たちは単に実行したテストの結果を共有しているだけです。コンピューティングプラットフォーム (AWS Fargate と Amazon EC2) やログメッセージサイズなどの次元別の完全なデータテーブルについては、GitHub をご覧ください。
リージョン内テスト実行の概要
以下は、約 17,000 回のリージョン内テスト実行のヒートマップ概要です。シェーディングされたボックス内のパーセント注釈は、最悪のテスト実行でのログ損失のパーセンテージです。赤の濃い色ほど、ログ損失が大きかったことを示しています。ログ出力レートが 2 MB/s 未満のテスト実行では、ログ損失は発生しませんでした。
リージョン間テスト実行の概要
タスクは us-west-2 で実行され、us-east-1 の Amazon CloudWatch にアップロードされました。
結果は、リージョン間のログアップロードは信頼性が低く、ログの損失を防ぐためにはるかに大きなバッファサイズが必要であることを示しています。
まとめ
この記事では、以下のことを学びました:
- コンテナログドライバーのブロッキングとノンブロッキングにおいて、アプリケーションの可用性とログ損失のトレードオフがあります。
- max-buffer-size の異なる値でノンブロッキングモードの AWSLogs ドライバーの動作を確認します。
- クロスリージョンのログアップロードは推奨されず、ノンブロッキングではログ損失のリスクが高くなります。
- コンテナごとのログ出力レートを確認する方法を説明しました。
- AWSLogs ドライバーのノンブロッキングモードではログ損失を監視することはできません。
アプリケーションの可用性とログ損失のトレードオフを検討する際、ユースケースがブロッキングモードまたはノンブロッキングモードのどちらを必要とするかを決定する必要があります。トレードオフとしてアプリケーション側の可用性を選択する場合、ノンブロッキングモードの AWSLogs ドライバーまたは他のログ収集ソリューションのどちらを選択しますか? Fluent Bit と FireLens などの他のほとんどのログ収集ソリューションは、ログのバッファは有限ですがトレードオフとしてアプリケーション側をブロックしません。ただし、ログの損失を防ぐためのチューニングや監視が容易なソリューションもあります。ノンブロッキングモードの AWSLogs ドライバーを選択する場合、コンテナごとのログ出力レートに応じて、max-buffer-size の値をリスク許容範囲内に設定する必要があります。GitHub の完全なテスト結果を慎重に確認することをお勧めします。結果に基づき、max-buffer-size には 25m を推奨し、すべてのログアップロードがリージョン内であることを確認してください。リージョン間のログプッシュは非常に信頼性が低いためです。
翻訳はソリューションアーキテクトの加治が担当しました。原文はこちらです。