FireLens(Fluent Bit)でエラーログだけはCloudWatch Logsへ、すべてのログはS3バケットへ保存を実現する設定例

Fluent Bit の rewrite_tag でうまくやれば Parser なしでもなんとかできます。
2021.12.30

本記事ではFargateで起動するFireLens(Fluent Bit)のログルーティングについて以下の設定例を紹介します。Parserは使わず設定ファイル1つでやってみます。

  • エラーログは CloudWatch Logs へ保存
  • ELBのヘルスチェックログを除くすべてのログは S3バケットへ保存
  • ELBのヘルスチェックログは削除

Icons made by Freepik from www.flaticon.com

検証環境

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の設定ファイルを作成しました。

extra.conf

[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_definitionlogキーの情報が欲しいです。

同様に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

エラーのログしかロググループには存在していません。

errorログ

{
    "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"
}

4xx系

{
    "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か月前に検証したことが時間に追われてブログに書けず仕舞いで今さらになって再度環境を作成してまとめました。よい復習にはなりましたけど、鉄は熱い打ちに打ちたい人なので試したらすぐアウトプットした方が効率よいですね。

参考