AWS FargateでFireLensを使って同じログを3箇所に送ってみた

カスタムログルーティングにおいてはFireLens力よりもFluent Bit力が大切だった。
2021.02.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS Fargateのログ管理でFireLens(Fluent Bit)を利用して複数のログ保存先を設定してみる。

  • アプリケーションが標準出力に出力したログをCloudWatch Logsと、S3バケットに保存してみます。
  • Firehose経由せずS3バケットへ直接送るFluent Bitのプラグインもあったのでついでに試しました。
  • Fluent Bitの設定ファイルはFluent BitのDockerイメージに同梱しました。

Icons made by Freepik from www.flaticon.com

検証環境

項目 バージョン
aws-for-fluent-bit 2.10.1
Fluent Bit 1.6.10
Fargate platform 1.4.0

Fluent BitとFluentd

Firelensで起動するログルーティングのコンテナは同じタスク内にサイドカーとして起動します。 軽量なコンテナの方が嬉しいのでFluent Bitを採用しました。

参考: Fluent Bit による集中コンテナロギング | Amazon Web Services ブログ

設定ファイル込みのイメージ作成

同じログ内容を合計3箇所の出力先へ送るシンプルなFluent Bitの設定を作るところからはじめます。Fluent Bitの設定次第で特定のログであればCloudWatch Logsへ、それ以外はS3バケットへ送信も可能です。Fluent Bitのドキュメントまで読みきれなかったので細かい設定は断念。

Fluent Bitの設定ファイル作成

各プラグインの設定はFluent Bitドキュメントを参考に出力先を設定しました。

S3へ直接する保存する場合はドキュメント内で注意事項として触れられています。Fargateのような永続ストレージがない実行環境でFluent Bitを起動する場合、Fluent Bitのコンテナが停止するとログを消失する可能性があります。高頻度でログを送信するのを推奨されています。とあるため、従来どおりKinesis Data Firehoseを経由でS3へ送信する方が安全な構成と言えるでしょう。ただ、S3用のプラグインを試したいので設定します。

extra.conf

[OUTPUT]
    Name   cloudwatch
    Match  *
    region us-east-1
    log_group_name /ecs/logs/fluentbit-dev-ecs-group
    log_stream_name from-fluentbit
    auto_create_group true

[OUTPUT]
    Name   firehose
    Match  *
    region us-east-1
    delivery_stream fluentbit-dev-delivery-stream

[OUTPUT]
    Name s3
    Match *
    region us-east-1
    bucket fluentbit-dev-directs3
    total_file_size 1M
    upload_timeout 1m

参考:

2021/9/6追記
プラグイン指定方法は以下のリンクもご確認ください

DockerfileとECRへイメージアップロード

ECS EC2の場合、S3にFluent Bitの設定ファイルを置いておけば設定ファイルを読み込んでくれる機能がサポートされています。しかし、Fargateはその機能がサポートされていません。そのため、Fluent Bitのコンテナ内に設定ファイルを入れておきます。

Icons made by Freepik from www.flaticon.com

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

FireLens 設定を使用するタスク定義の作成 - Amazon ECS

とは言え、Fluent Bit設定のファイルをイメージ内にコピーするだけです。Fluent Bitコンテナの設定ファイル読み込み指定はECSのタスク定義の設定で行います。ベースのイメージはAmazonが配布しているAWS用のプラグインが同梱されたFluent Bitのイメージを利用しています。

Dockerfile

FROM amazon/aws-for-fluent-bit:2.10.1
COPY ./extra.conf /fluent-bit/etc/extra.conf

設定ファイル込みのイメージをECRへプッシュします。FireLensで起動するサイドカーコンテナの準備完了です。

docker build -t fluentbit-dev-my-firelens .
docker tag fluentbit-dev-my-firelens:latest [AccountID].dkr.ecr.us-east-1.amazonaws.com/fluentbit-dev-my-firelens:latest
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin [AccountID].dkr.ecr.us-east-1.amazonaws.com
docker push [AccountID].dkr.ecr.us-east-1.amazonaws.com/fluentbit-dev-my-firelens:latest

参考:

FireLens有効化とタスク定義の編集

Fargateの構築については省略します。

FireLens有効

FireLensの統合を有効にチェックを入れます。イメージはECRにアップした設定ファイル込みのFluent Bitのイメージを指定します。

有効化するとlog_routerコンテナが追加されます。

タスク定義の編集

必要なログ設定箇所がドキュメントななめ読みだと頭に入ってこなかったのでまとめると私の理解ではこうなりました。

Icons made by Freepik from www.flaticon.com
  • アプリコンテナ(キャプチャの例だとfluentbit-dev-container)のログドライバーはawsfirelensを指定
  • FireLensのFluent Bitコンテナ(log_router)のログドライバーはawslogsを指定
    • Fluent Bitのトラブル対応のためCloudWatch Logs(awslogs)にログを送ります
    • logConfigurationと似ているfirelensConfigurationの項目に設定を追加します

アプリコンテナ設定

awslogsからawsfirelensに変更します。

変更後の該当箇所。optionsがnullか、optionsの項目が存在しないことを確認しましょう。管理コンソールからの設定だとデフォルトの設定値が残存していることがあります。

            "logConfiguration": {
                "logDriver": "awsfirelens",
                "secretOptions": null,
                "options": null
            },

optionsに空欄があったときのエラー内容

FireLnesコンテナ設定

awslogsを指定して、CloudWatch Logsのロググループなど設定を追加します。

変更後の該当箇所

            "logConfiguration": {
                "logDriver": "awslogs",
                "secretOptions": null,
                "options": {
                    "awslogs-group": "/ecs/logs/fluentbit-dev-ecs-group",
                    "awslogs-region": "us-east-1",
                    "awslogs-stream-prefix": "from-log-router"
                }
            },

Fluent Bitの設定ファイルの指定はタスク定義のJSONファイルを直接編集します。

firelensConfigurationの項目を探します。

            "firelensConfiguration": {
                "type": "fluentbit"
            },

変更後の該当箇所

  • "type": "fluentbit"末尾の,の付け忘れに注意
  • optionsでイメージに格納した自前の設定ファイルのパスを指定
            "firelensConfiguration": {
                "type": "fluentbit",
                "options": {
                    "config-file-type": "file",
                    "config-file-value": "/fluent-bit/etc/extra.conf"
                }
            },

検証時のタスク定義全文

設定箇所の確認にお使いください。

折りたたみ
{
  "ipcMode": null,
  "executionRoleArn": "arn:aws:iam::[AccountID]:role/fluentbit-dev-ECSTaskExecutionRolePolicy",
  "containerDefinitions": [
    {
      "dnsSearchDomains": [],
      "environmentFiles": null,
      "logConfiguration": {
        "logDriver": "awsfirelens",
        "secretOptions": null,
        "options": null
      },
      "entryPoint": [],
      "portMappings": [
        {
          "hostPort": 80,
          "protocol": "tcp",
          "containerPort": 80
        }
      ],
      "command": [],
      "linuxParameters": null,
      "cpu": 0,
      "environment": [],
      "resourceRequirements": null,
      "ulimits": null,
      "dnsServers": [],
      "mountPoints": [],
      "workingDirectory": null,
      "secrets": null,
      "dockerSecurityOptions": [],
      "memory": null,
      "memoryReservation": null,
      "volumesFrom": [],
      "stopTimeout": null,
      "image": "[AccountID].dkr.ecr.us-east-1.amazonaws.com/fluentbit-dev-sample-server:latest",
      "startTimeout": null,
      "firelensConfiguration": null,
      "dependsOn": null,
      "disableNetworking": null,
      "interactive": null,
      "healthCheck": null,
      "essential": true,
      "links": [],
      "hostname": null,
      "extraHosts": null,
      "pseudoTerminal": null,
      "user": null,
      "readonlyRootFilesystem": null,
      "dockerLabels": null,
      "systemControls": [],
      "privileged": null,
      "name": "fluentbit-dev-container"
    },
    {
      "dnsSearchDomains": null,
      "environmentFiles": null,
      "logConfiguration": {
        "logDriver": "awslogs",
        "secretOptions": null,
        "options": {
          "awslogs-group": "/ecs/logs/fluentbit-dev-ecs-group",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "from-log-router"
        }
      },
      "entryPoint": null,
      "portMappings": [],
      "command": null,
      "linuxParameters": null,
      "cpu": 0,
      "environment": [],
      "resourceRequirements": null,
      "ulimits": null,
      "dnsServers": null,
      "mountPoints": [],
      "workingDirectory": null,
      "secrets": null,
      "dockerSecurityOptions": null,
      "memory": null,
      "memoryReservation": null,
      "volumesFrom": [],
      "stopTimeout": null,
      "image": "[AccountID].dkr.ecr.us-east-1.amazonaws.com/fluentbit-dev-my-firelens:latest",
      "startTimeout": null,
      "firelensConfiguration": {
        "type": "fluentbit",
        "options": {
          "config-file-type": "file",
          "config-file-value": "/fluent-bit/etc/extra.conf"
        }
      },
      "dependsOn": null,
      "disableNetworking": null,
      "interactive": null,
      "healthCheck": null,
      "essential": true,
      "links": null,
      "hostname": null,
      "extraHosts": null,
      "pseudoTerminal": null,
      "user": "0",
      "readonlyRootFilesystem": null,
      "dockerLabels": null,
      "systemControls": null,
      "privileged": null,
      "name": "log_router"
    }
  ],
  "placementConstraints": [],
  "memory": "512",
  "taskRoleArn": "arn:aws:iam::[AccountID]:role/fluentbit-dev-ECSTaskExecutionRolePolicy",
  "compatibilities": [
    "EC2",
    "FARGATE"
  ],
  "taskDefinitionArn": "arn:aws:ecs:us-east-1:[AccountID]:task-definition/fluentbit-dev-task:19",
  "family": "fluentbit-dev-task",
  "requiresAttributes": [
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.execution-role-awslogs"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.ecr-auth"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.firelens.fluentbit"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.firelens.options.config.file"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.logging-driver.awsfirelens"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.task-iam-role"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.execution-role-ecr-pull"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.task-eni"
    }
  ],
  "pidMode": null,
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "networkMode": "awsvpc",
  "cpu": "256",
  "revision": 19,
  "status": "ACTIVE",
  "inferenceAccelerators": null,
  "proxyConfiguration": null,
  "volumes": []
}

タスクロールの追加

タスク実行ロールではなくタスクロールにログ送信先リソースへアクセスできるポリシーを追加します。

手抜きでフルアクセスのポリシーを追加しました。

手を抜かない例

FireLens サイドカー起動

サービス更新してタスク起動し直します。 log_routerコンテナも起動してきました。

ログ確認

FireLensからCloudWatch Logsへ

Fluent Bitコンテナ自身はawslogs指定でCloudWatch Logsへ保存設定しました。S3へアップロードしましたという大量のログを確認できました。高頻度で送信するようにしたためほぼこのログで埋まっています。Fluent Bitコンテナのトラブル調査はCloudwatch Logsからできます。

[2021/02/14 11:12:52] [ info] [output:s3:s3.2] Successfully uploaded object /fluent-bit-logs/fluentbit-dev-container-firelens-af27785192944632a76e3d1696c42686/2021/02/14/11/11/47-objectNj3AEbw5
[2021/02/14 11:14:22] [ info] [output:s3:s3.2] Successfully uploaded object /fluent-bit-logs/fluentbit-dev-container-firelens-af27785192944632a76e3d1696c42686/2021/02/14/11/13/17-objectyjr40CIG

アプリから各種保存先へ

アプリコンテナはELBの後ろにある文字列を返すだけのWEBサーバです。

$ curl http://fluentbit-dev-elb-586101502.us-east-1.elb.amazonaws.com/
"Hello, Abashiri"

まず、CloudWatch LogsにはELBのヘルスチェックのリクエストログを確認できました。Fluent BitからCloudWatch Logsへのルーティングは成功していました。

{
    "container_id": "af27785192944632a76e3d1696c42686-3097909514",
    "container_name": "fluentbit-dev-container",
    "ecs_cluster": "fluentbit-dev-cluster",
    "ecs_task_arn": "arn:aws:ecs:us-east-1:[AccountID]:task/fluentbit-dev-cluster/af27785192944632a76e3d1696c42686",
    "ecs_task_definition": "fluentbit-dev-task:19",
    "log": "{\"time\":\"2021-02-14T11:18:44.214087349Z\",\"id\":\"\",\"remote_ip\":\"10.1.2.37\",\"host\":\"10.1.17.31\",\"method\":\"GET\",\"uri\":\"/\",\"user_agent\":\"ELB-HealthChecker/2.0\",\"status\":200,\"error\":\"\",\"latency\":40220,\"latency_human\":\"40.22µs\",\"bytes_in\":0,\"bytes_out\":18}",
    "source": "stdout"
}

次にKinesis Data Firehose経由のS3バケット内の様子です。ダウンロードして確認すると同様にELBのヘルスチェックログを確認できました。

{"container_id":"af27785192944632a76e3d1696c42686-3097909514","container_name":"fluentbit-dev-container","ecs_cluster":"fluentbit-dev-cluster","ecs_task_arn":"arn:aws:ecs:us-east-1:[AccountID]:task/fluentbit-dev-cluster/af27785192944632a76e3d1696c42686","ecs_task_definition":"fluentbit-dev-task:19","log":"{\"time\":\"2021-02-14T11:30:14.682585288Z\",\"id\":\"\",\"remote_ip\":\"10.1.2.37\",\"host\":\"10.1.17.31\",\"method\":\"GET\",\"uri\":\"/\",\"user_agent\":\"ELB-HealthChecker/2.0\",\"status\":200,\"error\":\"\",\"latency\":51727,\"latency_human\":\"51.727µs\",\"bytes_in\":0,\"bytes_out\":18}","source":"stdout"}
{"container_id":"af27785192944632a76e3d1696c42686-3097909514","container_name":"fluentbit-dev-container","ecs_cluster":"fluentbit-dev-cluster","ecs_task_arn":"arn:aws:ecs:us-east-1:[AccountID]:task/fluentbit-dev-cluster/af27785192944632a76e3d1696c42686","ecs_task_definition":"fluentbit-dev-task:19","log":"{\"time\":\"2021-02-14T11:30:44.408751519Z\",\"id\":\"\",\"remote_ip\":\"10.1.1.178\",\"host\":\"10.1.17.31\",\"method\":\"GET\",\"uri\":\"/\",\"user_agent\":\"ELB-HealthChecker/2.0\",\"status\":200,\"error\":\"\",\"latency\":37581,\"latency_human\":\"37.581µs\",\"bytes_in\":0,\"bytes_out\":18}","source":"stdout"}

最後は直接S3バケットにログを送った様子です。オブジェクトまでのパスが深いのと、ログフォーマットが今まで多少異なっていました。

{"date":"2021-02-14T11:15:43.721428Z","container_name":"fluentbit-dev-container","source":"stdout","log":"{\"time\":\"2021-02-14T11:15:43.72122498Z\",\"id\":\"\",\"remote_ip\":\"10.1.1.178\",\"host\":\"10.1.17.31\",\"method\":\"GET\",\"uri\":\"/\",\"user_agent\":\"ELB-HealthChecker/2.0\",\"status\":200,\"error\":\"\",\"latency\":39110,\"latency_human\":\"39.11\u00b5s\",\"bytes_in\":0,\"bytes_out\":18}","container_id":"af27785192944632a76e3d1696c42686-3097909514","ecs_cluster":"fluentbit-dev-cluster","ecs_task_arn":"arn:aws:ecs:us-east-1:[AccountID]:task/fluentbit-dev-cluster/af27785192944632a76e3d1696c42686","ecs_task_definition":"fluentbit-dev-task:19"}
{"date":"2021-02-14T11:15:44.109974Z","source":"stdout","log":"{\"time\":\"2021-02-14T11:15:44.109820687Z\",\"id\":\"\",\"remote_ip\":\"10.1.2.37\",\"host\":\"10.1.17.31\",\"method\":\"GET\",\"uri\":\"/\",\"user_agent\":\"ELB-HealthChecker/2.0\",\"status\":200,\"error\":\"\",\"latency\":39215,\"latency_human\":\"39.215\u00b5s\",\"bytes_in\":0,\"bytes_out\":18}","container_id":"af27785192944632a76e3d1696c42686-3097909514","container_name":"fluentbit-dev-container","ecs_cluster":"fluentbit-dev-cluster","ecs_task_arn":"arn:aws:ecs:us-east-1:[AccountID]:task/fluentbit-dev-cluster/af27785192944632a76e3d1696c42686","ecs_task_definition":"fluentbit-dev-task:19"}

おわりに

FireLensはFluent BitまたはFluentdを起動するのに便利な機能であって、ログのルーティングはFluent Bit、Fluentdの設定次第ということがわかりました。設定ファイルはS3に置いておけるようになると自前イメージの管理の手間減って助かりますね。

参考