FireLens(Fluent Bit)CloudWatch Logsの新プラグインを使ったログストリーム作成の注意点

Fluent Bitを使ってCloudWatch LogsへログルーティングするためにCloudWatchプラグインを利用します。新・旧プラグイン2種類が提供されており、新プラグインを利用した場合の注意事項を紹介します。
2021.08.12

ECSのタスク(コンテナ)のログ出力先を変更できるFireLens機能があります。FireLensでFluent Bitを起動し、プラグインを利用し各種ログ保存先へ振り分けます。CloudWatch Logsや、Kinesis Firehose経由S3保存をよく利用されているのではないでしょうか。

そのプラグインは現在、新旧の2種類存在しています。ぱっと見はプラグインの指定方法が微妙に違うくらいです。

詳細は以下のリンクをご確認ください。

本記事ではCloudWatch Logsへログを送信する新旧プラグインについて取り上げます。以下の意味を理解せず「ロググループとログストリームのテンプレーティング?うーん、一般的な設定しかしてないからたぶんヨシ!」で失敗したため、新プラグイン利用時の注意事項を紹介します。

Do you plan to deprecate this older plugin? At this time, we do not. This plugin will continue to be supported. It contains features that have not been ported to the higher performance version. Specifically, templating of log group name and streams. Some users will continue to need the features of this plugin.

引用: aws/amazon-cloudwatch-logs-for-fluent-bit: A Fluent Bit output plugin for CloudWatch Logs

結論

ECSメタデータを利用した変数を使いロググループや、ログストリームを作成したい場合は

  • 旧プラグイン(cloudwatch)しか対応していない
  • 新プラグイン(cloudwatch_logs)は未対応なため変数を使うと期待通りの動きをしない

新プラグインの利用

新プラグインの利用はNamecloudwatch_logsを指定します。

ロググループ作成失敗編

ECSメタデータの変数を利用します。ロググループはECSクラスター名の名前で作成し、ログストリーム名はタスクごとに個別のストリームを作成したいのでタスクIDを末尾に付与します。

extra.conf

[SERVICE]
    Flush 1
    Grace 30

[FILTER]
    Name grep
    Match webapp-firelens*
    Exclude log ^(?=.*ELB-HealthChecker\/2\.0).*$

[OUTPUT]
    Name   cloudwatch_logs
    Match webapp-firelens*
    region ap-northeast-1
    log_group_name /ecs/logs/$(ecs_cluster_name)
    log_stream_name webapp-$(ecs_task_id)
    auto_create_group true

上記設定ファイルをFireLensで起動するFluent Bitのイメージにコピーして検証します。

Dockerfile

FROM public.ecr.aws/aws-observability/aws-for-fluent-bit:2.19.0
COPY ./extra.conf /fluent-bit/etc/extra.conf

実行結果

FireLensのログを確認します。${ecs_cluster_name}の変数を展開できなかったようです。ロググループの作成に失敗しています。

[ info] [output:cloudwatch_logs:cloudwatch_logs.0] Creating log group /ecs/logs/$(ecs_cluster_name)
[error] [output:cloudwatch_logs:cloudwatch_logs.0] Failed to create log group

ログストリーム作成失敗編

ロググループ名は変数を使わずベタ書きに修正しました。ログストリームは引き続き変数を利用します。

extra.conf

[SERVICE]
    Flush 1
    Grace 30

[FILTER]
    Name grep
    Match webapp-firelens*
    Exclude log ^(?=.*ELB-HealthChecker\/2\.0).*$

[OUTPUT]
    Name   cloudwatch_logs
    Match webapp-firelens*
    region ap-northeast-1
    log_group_name /ecs/logs/test-gorup
    log_stream_name webapp-$(ecs_task_id)
    auto_create_group true

実行結果

ログストリーム名が指定したまま(webapp-$(ecs_task_id))の名前で作成されています。変数を展開できてないことがわかりました。

FireLensのログも確認します。そのままwebapp-$(ecs_task_id)の名前でログストリームを作成していることがわかります。

[ info] [output:cloudwatch_logs:cloudwatch_logs.0] Created log group /ecs/logs/test-gorup
[ info] [output:cloudwatch_logs:cloudwatch_logs.0] Creating log stream webapp-$(ecs_task_id) in log group /ecs/logs/test-gorup

旧プラグインを利用

Namecloudwatchを指定(末尾_logsを削除)し、旧プラグインに切り替えます。

成功編

新プラグインのロググループ作成失敗編の設定ファイルと、Nameでプラグイン名指定の違いしかありません。

extra.conf

[SERVICE]
    Flush 1
    Grace 30

[FILTER]
    Name grep
    Match webapp-firelens*
    Exclude log ^(?=.*ELB-HealthChecker\/2\.0).*$

[OUTPUT]
    Name   cloudwatch
    Match webapp-firelens*
    region ap-northeast-1
    log_group_name /ecs/logs/$(ecs_cluster)
    log_stream_name webapp-$(ecs_task_id)
    auto_create_group true

実行結果

期待通りの結果です。旧プラグインで変数を展開してロググループ、ログストリームを作成できました。新プラグインと挙動の違いがわかりました。

time="2021-08-11T09:07:58Z" level=info msg="[cloudwatch 0] Created log group /ecs/logs/arn.aws.ecs.ap-northeast-1.123456789012.cluster/sample-dev-cluster\n"
time="2021-08-11T09:07:58Z" level=info msg="[cloudwatch 0] Created log stream webapp-3edb579771ff40fcb7e536040929a17c in group /ecs/logs/arn.aws.ecs.ap-northeast-1.123456789012.cluster/sample-dev-cluster"

存在しない変数を使った場合の挙動

$(ecs_cluster_name)という未定義の変数を指定してロググループを作成してみます。これに関してはただ気になったから試しただけです。

extra.conf

[SERVICE]
    Flush 1
    Grace 30

[FILTER]
    Name grep
    Match webapp-firelens*
    Exclude log ^(?=.*ELB-HealthChecker\/2\.0).*$

[OUTPUT]
    Name   cloudwatch
    Match webapp-firelens*
    region ap-northeast-1
    log_group_name /ecs/logs/$(ecs_cluster_name)
    log_stream_name webapp-$(ecs_task_id)
    auto_create_group true

実行結果

fluentbit-defaultの名前でロググループが作成されました。ログストリーム名は指定通りに作成されています。新プラグイン同様にロググループ作成できないエラーを期待していました。新旧プラグインではこういったところにも違いありました。

FireLensのログを確認します。

AWS for Fluent Bit Container Image Version 2.19.0time="2021-08-11T08:58:49Z" level=info msg="[cloudwatch 0] plugin parameter log_group_name = '/ecs/logs/$(ecs_cluster_name)'"
time="2021-08-11T08:58:49Z" level=info msg="[cloudwatch 0] plugin parameter default_log_group_name = 'fluentbit-default'"

古いプラグインが提供され続けている理由とは

初心に帰り説明を読み直します。

templating of log group name and streamsがリンクになっています。リンク先を確認すると「用意されている変数名はなんだっけな?」とよく確認するページでした。そして、これが「一部ユーザは引き続きこの機能を必要とするでしょう」という文に繋がるわけですね。

そうか、みんなECSメタデータの変数使っていなかったのか...本当にそうなの?

CloudWatch Logsへログを送信する場合、復数のFireLens(Fluent Bit)コンテナから単一のログストリームに送るとスロットリングエラーを起こす可能性が高くなります。タスクIDかなにかを利用しユニークなログストリームを作成し、タスク(アプリケーションコンテナ)ごとに個別のログストリームにログを送信して回避したいところだと思います。

もしかしたら、私が知らないだけで他の回避方法があるかもしれません。少なくとも用意されている変数が新プラグインでは使えないことがわかりました。(ちゃんと説明されていました)

2021/9/17追記
他の回避方法は以下のリンクをご確認ください

まとめ

新旧どちらのプラグインを使えばよいのかの判斷基準として、ECSメタデータを利用した変数旧プラグイン(cloudwatch)でしか利用できない。変数を使わないで設定で済むなら新プラグイン(cloudwatch_logs)の方が高性能なので採用を検討したい。

おわりに

なにげなく新プラグインを利用してたときはログストリーム名にタスクIDを付与せず、単一のログストリームでテストしていたので気づきませんでした。まじめに設定しようと旧プラグインの設定ファイルを参考にプラグイン名(Name)だけを新プラグイン名に書き換えて実行したときに気づきました。タスクIDなどの変数以外でユニークなログストリーム名を生成できるならよかったのですが、他の方法が思いつかなかったです。

どなたかの参考になれば幸いです。