Open38

FireLens for Amazon ECS の設定について調べる

ピン留めされたアイテム
snakasnaka

モチベーション

  • 現在 ECS で稼働しているアプリケーションはログを CloudWatch Logs に出力している
  • それとは別に特定のログを S3 に出力したい
  • CloudWatch Agent をサイドカーコンテナとして立てて CloudWatch Logs 経由で Firehose 使うという手もあるが、コスト的に高くつく&構成も無駄に複雑化してしまう
  • FireLens であればシンプルな構成で実現できそう
snakasnaka

FireLens は Fluentd / FluentBit と連携してログを特定のサービス等に転送する仕組みを簡単に構築できる Amazon ECS の機能
正式名称は「FireLens for Amazon ECS」

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/using_firelens.html

snakasnaka

構成として、S3にカスタム設定ファイルを配置する方法を取る場合、以下に注意する必要がある

Amazon S3 でホストされているカスタム設定ファイルを使用する場合は、タスク実行 IAM ロールに s3:GetObject アクセス許可が含まれている必要があります。

snakasnaka

FireLens のタスク定義の例

Log Router コンテナ (FluentBit 自身) のログと、Log Router の設定が別のコンテナに記述するようになってたりと分かりづらい。( Fluent Bit / Fluentd を FireLens という機能として中途半端に抽象化していることが原因かもしれない )

タスク定義の例
{
  "family": "firelens-example-firehose",
  "taskRoleArn": "arn:aws:iam::123456789012:role/ecs_task_iam_role",
  "containerDefinitions": [
    {
            "name": "log_router",
            "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:stable",
            "cpu": 0,
            "memoryReservation": 51,
            "portMappings": [],
            "essential": true,
            "environment": [],
            "mountPoints": [],
            "volumesFrom": [],
            "user": "0",
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/ecs/ecs-aws-firelens-sidecar-container",
                    "mode": "non-blocking",
                    "awslogs-create-group": "true",
                    "max-buffer-size": "25m",
                    "awslogs-region": "us-east-1",
                    "awslogs-stream-prefix": "firelens"
                },
                "secretOptions": []
            },
            "systemControls": [],
            "firelensConfiguration": {
                "type": "fluentbit"
            }
        },
    {
      "essential": true,
      "image": "public.ecr.aws/docker/library/httpd:latest",
      "name": "app",
      "logConfiguration": {
        "logDriver": "awsfirelens",
        "options": {
          "Name": "firehose",
          "region": "us-west-2",
          "delivery_stream": "my-stream",
          "log-driver-buffer-limit": "2097152"
        }
      },
      "memoryReservation": 100
    }
  ]
}

snakasnaka

カスタム設定ファイル

上記の例では、タスク定義(JSON)から動的に Log Route 用の設定ファイルを生成する方式だが、カスタム設定ファイルとして事前にファイルを用意しておくこともできる。

その場合、タスク定義の logConfiguration の以下のキーを使用する

{
  "containerDefinitions": [
    {
      "essential": true,
      "image": "906394416424.dkr.ecr.us-west-2.amazonaws.com/aws-for-fluent-bit:stable",
      "name": "log_router",
      "firelensConfiguration": {
        "type": "fluentbit",
        "options": {
          "config-file-type": "s3 | file",
          "config-file-value": "arn:aws:s3:::amzn-s3-demo-bucket/fluent.conf | filepath"
        }
      }
    }
  ]
}
snakasnaka
  • config-file-type
    • カスタム設定ファイルのソースの場所。使用できるオプションは、s3 または file です。
  • config-file-value
    • カスタム設定ファイルのソース。
    • s3 設定ファイルタイプを使用する場合、設定ファイルの値は Amazon S3 バケットとファイルの完全な ARN です。
    • file 設定ファイルタイプを使用する場合、設定ファイルのこの値は、コンテナイメージ内、またはそのコンテナにマウントされているボリューム上に存在する設定ファイルへの完全パスです。
snakasnaka

AWS Fargate でホストされるタスクは、file 設定ファイルタイプのみをサポートします。

Fargate だと S3 から読み込むことができないらしい...

snakasnaka
snakasnaka

FireLens によって構築されるロギング環境の構成イメージ


引用元: https://aws.amazon.com/jp/blogs/news/under-the-hood-firelens-for-amazon-ecs-tasks/

Application コンテナのログは、 以下2つの方法で FireLens (Fluent Bit 等) コンテナに送信される

  • 標準出力ログ: Fluentd Docker ログドライバを介して Unix ソケット経由
    • TCP ソケットよりパフォーマンスに優れる
  • Fluent Forward Protocol メッセージ (Fluent Logger ライブラリを使用) : TCPソケット経由
    • アプリケーションコードで、タグを付けた形でログを送出することができる
snakasnaka

FluentBit などの設定ファイルは、タスク定義の内容を元に FireLens によって生成される。
ユーザー独自の設定ファイルは include ディレクティブを使用して、FireLens が生成した設定ファイルにインポートされる

https://docs.fluentbit.io/manual/administration/configuring-fluent-bit/classic-mode/configuration-file#config_include_file-1

設定ファイルは Log Router コンテナの以下の path にマウントされる

  • Fluent Bit: /fluent-bit/etc/fluent-bit.conf
snakasnaka

FireLens のユースケースごとの設定例

https://github.com/aws-samples/amazon-ecs-firelens-examples

snakasnaka

別のやりかたもあるらしい

https://github.com/aws-samples/amazon-ecs-firelens-examples/tree/mainline/examples/fluent-bit/multi-config-support

この設定では、

  • Application コンテナログ(標準出力) → CloudWatch Logs
    • 設定格納場所: Task定義(JSON)の app コンテナの logConfiguration
  • Application コンテナの /logs/app.log → S3
    • 設定格納場所: S3 バケット (Log Router コンテナの環境変数に設定)

という形でログの出力先を分けることができる。

これが求めていた形に一番近いかも

ログのルーティング設定が2個所に分かれるという点が複雑だが

snakasnaka

Init process と multi-config について

https://github.com/aws/aws-for-fluent-bit/blob/mainline/use_cases/init-process-for-fluent-bit/

  • Fluent Bit イメージのタグとして init-xxx と指定することで Init process を利用できる
    • aws-for-fluent-bit:init-latest or aws-for-fluent-bit:init-2.27.0 のように
  • 環境変数として設定ファイルの場所を指定することができる
    "environment": [
         {
             "name": "aws_fluent_bit_init_s3_1",
             "value": "arn:aws:s3:::yourBucket/aaaaa.conf"
         },
         {
             "name": "aws_fluent_bit_init_s3_2",
             "value": "arn:aws:s3:::yourBucket/bbbbb.conf"
         },
         {
             "name": "aws_fluent_bit_init_file_1",
             "value": "/ecs/s3.conf"
         }
     ]
    
  • Init process として S3 バケットから設定ファイルを読み込む場合、Taskロールに以下の権限が必要
    {
          "Version": "2012-10-17",
          "Statement": [
              {
                  "Effect": "Allow",
                  "Action": [
                      "s3:GetObject",
                      "s3:GetBucketLocation"
                  ],
                  "Resource": "*"
              }
          ]
    }
    
snakasnaka

Init process の動作

  1. ECS Task Metadata から情報を取得し、環境変数にセットする
  2. aws_fluent_bit_init_s3_, aws_fluent_bit_init_file_ という prefix つきの環境変数から値を取り出し、指定の場所から設定ファイルを読み込む
  3. タスク定義から Fluent Bit の設定ファイルを生成、@INCLUDE ディレクティブで前述の設定ファイルを読み込ませる
  4. 読み込ませるファイルが Parser 設定の場合は特殊な処理を行う... (今回不要なのでスルー)
snakasnaka

Init process による起動コマンドの変更

Init process を有効化 ( Log router コンテナに init-xxx イメージを利用する ) すると、 Fluent Bit の起動コマンドが変化する

  • 設定ファイルは /init/fluent-bit-init.conf を読み込むようになる
    • /init/fluent-bit-init.conf は以下の設定を読み込む
      • /fluent-bit/etc/fluent-bit.conf FireLens が Task定義から生成する従来の設定ファイル
      • /init/fluent-bit-init-s3-files/your-filter.conf ユーザーが S3 に配置した設定ファイル

例)

@INCLUDE /fluent-bit/etc/fluent-bit.conf
@INCLUDE /init/fluent-bit-init-s3-files/your-filter.conf
@INCLUDE /init/fluent-bit-init-s3-files/your-s3-output.conf
snakasnaka

json parser が使えない?

以下のように /path/to/audit-*.log を JSON parser で読み込もうとしたら

[INPUT]
    Name              tail
    Path              /path/to/*.log
    Parser            json
    Tag               audit
    Refresh_Interval  1
    Rotate_Wait       30
    Skip_Long_Lines   On
    DB                /var/log/flb_audit.db

以下のようなエラーが出ていた

[error] [input:tail:tail.3] parser 'json' is not registered
snakasnaka

Parse したいログは

{
    "timestamp": "2025-09-11T15:46:18.182+09:00",
    "record": {
        "auditable": {
            "id": 123,
            "type": "FooBar"
        },
        "user": null,
        "action": "create",
        "changes": "....",
        "remote_address": null,
        "request_uuid": null
    }
}
snakasnaka

上記を元に Parser 設定は

[PARSER]
    Name        audit_json
    Format      json
    Time_Key    timestamp
    Time_Format %Y-%m-%dT%H:%M:%S.%L%z
    Time_Keep   On
    Decode_Field_As json record
snakasnaka

Input でその Parser を利用するようにしたらエラーは消えた

[INPUT]
    Name              tail
    Path              /path/to/*.log
-    Parser            json
+    Parser            audit_json
    Tag               audit
    Refresh_Interval  1
    Rotate_Wait       30
    Skip_Long_Lines   On
    DB                /var/log/flb_audit.db
snakasnaka

デバッグのため Fluent Bit のログに出力してみる

stdout フィルターは処理レコードを標準出力に表示する

[FILTER]
    Name              stdout
    Match             audit
snakasnaka

S3 にアップした設定が init process で読み込まれていない?

https://github.com/aws/aws-for-fluent-bit/tree/mainline/use_cases/init-process-for-fluent-bit

After init process has processed:
fluent-bit-init.conf (new main config file generated by init process, will used to invoke Fluent Bit, path inside the image:/init/fluent-bit-init.conf)

@INCLUDE /fluent-bit/etc/fluent-bit.conf
@INCLUDE /init/fluent-bit-init-s3-files/your-filter.conf
@INCLUDE /init/fluent-bit-init-s3-files/your-s3-output.conf

このように記述あり、 s3 にアップしたファイルが INCLUDE されるはずだが... どうも読み込まれてなさそう

snakasnaka

Log Router コンテナに入って /init/fluent-bit-init.conf ファイルを確認してみるが @INCLUDE による s3 からダウンロードしたファイルの読み込みが入っていない

# cat fluent-bit-init.conf
@INCLUDE /fluent-bit/etc/fluent-bit.conf