FireLens(Fluent Bit)でエラーログだけはCloudWatch Logsへ、すべてのログはS3バケットへ保存を実現する設定例
本記事ではFargateで起動するFireLens(Fluent Bit)のログルーティングについて以下の設定例を紹介します。Parserは使わず設定ファイル1つでやってみます。
- エラーログは CloudWatch Logs へ保存
- ELBのヘルスチェックログを除くすべてのログは S3バケットへ保存
- ELBのヘルスチェックログは削除
検証環境
Product | Version |
---|---|
aws-for-fluent-bit | 2.21.1 |
Fluent Bit | 1.8.9 |
Fargate platform | 1.4.0 |
本検証利用したFargateのCloudFormationテンプレート、FireLensのDockerfileなどは以下に置いてあります。タスクロールなどは必要に応じて確認してください。
設定ファイル
以下のFluent Bitの設定ファイルを作成しました。
[SERVICE] Flush 1 Grace 30 # ELBヘルスチェックログ除外 [FILTER] Name grep Match *-firelens-* Exclude log ^(?=.*ELB-HealthChecker\/2\.0).*$ # エラーログにタグ付け [FILTER] Name rewrite_tag Match *-firelens-* Rule $log (emerg|alert|crit|error|\s4\d{2}\s|\s5\d{2}\s) error-$container_id false # errorタグの一部キーを削除 [FILTER] Name record_modifier Match error-* Remove_key container_id Remove_key container_name Remove_key ecs_cluster Remove_key ecs_task_arn Remove_key source # errorタグをCloudWatch Logsへ [OUTPUT] Name cloudwatch_logs Match error-* region ap-northeast-1 log_group_name /sample5-dev-web-error-logs log_stream_prefix fluentbit- auto_create_group true log_retention_days 30 # ELBのヘルスチェック以外の全ログはFirehose経由S3へ [OUTPUT] Name kinesis_firehose Match * region ap-northeast-1 delivery_stream sample5-dev-firelens-deliverystream
エラーログのみ CloudWatch Lgos出力の説明
特定エラーログだけにタグを付け直して、再度Fluent Bitのデータパイプラインに流すのがポイントです。データパイプラインはログルーティングする上で重要な概念です。
Matchの条件は*-firelens-*
のタグを引っ掛けます。よくわからない引っ掛け方に見えますが、アプリケーションコンテナから出力されるデフォルト形式に合わせています。
Container standard out logs are tagged with <container name>-firelens-<task ID>
rewrite_tagを使いログにerror
, alert
などの文字列や、4xx
, 5xx
系のエラーを示す文字列が含まれていたらerror-[Container ID]のタグに置き換えます。そして、新しくタグ付けされたレコードはFluent Bitのパイプラインの最初に戻ります。
# エラーログにタグ付け [FILTER] Name rewrite_tag Match *-firelens-* Rule $log (emerg|alert|crit|error|\s4\d{2}\s|\s5\d{2}\s) error-$container_id false
error-hogeとタグ付けされたレコードはFluent Bitのバイプラインの最初から入り直すと、今度は[OUTPUT]
のerror-*
にマッチし CloudWatch Logs へ送信されていきます。
# errorタグをCloudWatch Logsへ [OUTPUT] Name cloudwatch_logs Match error-* region ap-northeast-1 log_group_name /sample5-dev-web-error-logs log_stream_prefix fluentbit- auto_create_group true log_retention_days 30
ログに出力不要項目の削除
設定検証にあたり試行錯誤していたためタスク定義のバージョンをログ内に欲していました。要はecs_task_definition
とlog
キーの情報が欲しいです。
同様にerror-*
でマッチしたレコードをRemove_key
で必要のないキーを削除します。ログ本文のlog
キーと、タスク定義のバージョンが含まれているecs_task_definition
キーを残してそれ以外を削除しています。
# errorタグの一部キーを削除 [FILTER] Name record_modifier Match error-* Remove_key container_id Remove_key container_name Remove_key ecs_cluster Remove_key ecs_task_arn Remove_key source
cloudwatch_logsプラグインのlog_key
を利用しても似たことは実現できます。log_key
で指定した1つのキーのみがCloudWatch Lgosへ出力されます。ログの本文が格納されているlog
キーを指定すれば、ログ本文のみをCloudWatch Logsへ出力できます。
cloudwatch_logsプラグインのlog_key
については以前に紹介している記事がございますので以下をご参照ください。
複数のキー情報をCloudWatch Logsへ送信したい場合は今回ご紹介したRemove_key
の方法をご検討ください。
すべてのログをS3バケットに保存の説明
ELBのヘルスチェックログはフィルターされているため、Firehose用のプラグインを利用してKinesis Firehose経由でS3バケットを保存しています。Fluent BitからはあくまでもFirehoseへ送信しているだけです。
Firehoseの設定から保存先のS3バケットを指定する必要があります。
# ELBのヘルスチェック以外の全ログはFirehose経由S3へ [OUTPUT] Name kinesis_firehose Match * region ap-northeast-1 delivery_stream sample5-dev-firelens-deliverystream
ELBヘルスチェックログ除外の説明
以前に紹介している記事がございますので以下をご参照ください。
ログ出力結果確認
エラーログのみ出力される CloudWatch Logs と、ELBのヘルスチェックログを除くすべてのログが保存される S3バケットを確認します。
ELBにアクセスするとアプリケーションコンテナはNginxのデフォルトページが表示されるだけとなっています。
CloudWatch Logs
エラーのログしかロググループには存在していません。
{ "log": "2021/12/29 04:52:11 [error] 32#32: *1025 open() \"/usr/share/nginx/html/robots.txt\" failed (2: No such file or directory), client: 10.0.2.134, server: localhost, request: \"GET /robots.txt HTTP/1.1\", host: \"52.199.234.210\"", "ecs_task_definition": "sample5-dev-web-taskdefinition:9" }
{ "log": "10.0.2.134 - - [29/Dec/2021:04:52:11 +0000] \"GET /robots.txt HTTP/1.1\" 404 555 \"-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE\" \"183.136.225.9\"", "ecs_task_definition": "sample5-dev-web-taskdefinition:9" }
S3バケット
保存されているログをダウンロードして確認します。
ステータスコード 200のログを確認できます。またELBヘルスチェックと思わしきログはないです。
{"container_name":"web","source":"stdout","log":"10.0.1.65 - - [29/Dec/2021:12:49:54 +0000] \"GET / HTTP/1.1\" 200 615 \"-\" \"Mozilla/5.0 zgrab/0.x\" \"66.240.236.116\"","container_id":"490e0f0581c54bea84662d97e4ec4a01-265927825","ecs_cluster":"sample5-dev-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:123456789012:task/sample5-dev-cluster/490e0f0581c54bea84662d97e4ec4a01","ecs_task_definition":"sample5-dev-web-taskdefinition:9"} {"container_id":"490e0f0581c54bea84662d97e4ec4a01-265927825","container_name":"web","source":"stdout","log":"10.0.2.134 - - [29/Dec/2021:12:52:29 +0000] \"GET / HTTP/1.1\" 200 615 \"-\" \"-\" \"64.62.197.182\"","ecs_cluster":"sample5-dev-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:123456789012:task/sample5-dev-cluster/490e0f0581c54bea84662d97e4ec4a01","ecs_task_definition":"sample5-dev-web-taskdefinition:9"}
error
ログと、ステータスコード 404のログも確認できました。
{"log":"2021/12/29 10:55:04 [error] 33#33: *185 open() \"/usr/share/nginx/html/aaa\" failed (2: No such file or directory), client: 10.0.2.134, server: localhost, request: \"GET /aaa HTTP/1.1\", host: \"sample5-dev-elb-1596891960.ap-northeast-1.elb.amazonaws.com\"","ecs_task_definition":"sample5-dev-web-taskdefinition:9"} {"log":"10.0.2.134 - - [29/Dec/2021:10:55:04 +0000] \"GET /aaa HTTP/1.1\" 404 555 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36\" \"14.9.145.160\"","ecs_task_definition":"sample5-dev-web-taskdefinition:9"}
おわりに
Parserを使わなくてもrewrite_tag
を使いこなせば設定ファイル1つで多少複雑なログルーティングに対応できそうです。
CloudWatch Logsに送信したログは必要に応じてサブスクリプションフィルターや、メトリクスフィルターでメール、Slack通知などアラームを設定しておくとログ監視として機能します。
サブスクリプションフィルター
メトリクスフィルター
2か月前に検証したことが時間に追われてブログに書けず仕舞いで今さらになって再度環境を作成してまとめました。よい復習にはなりましたけど、鉄は熱い打ちに打ちたい人なので試したらすぐアウトプットした方が効率よいですね。