はじめに

ECS on Fargate上で動作しているApacheのログをVirtual Hostごとかつログ種別(アクセス、エラー)ごとにCloudWatch Logsのログストリームへ出力します。
このログ出力をFireLensを使用し実装しましたので行った設定をまとめます。

  • ミドルウェアのバージョン
    • aws-for-fluent-bit:2.32
    • httpd:2.4

ECSタスク定義

コンテナごとに必要な設定をJSONで抜き出して記載します。

Apacheコンテナ

ログ出力にFireLensを使用するように設定します。
出力するログストリームなどはfluentbitのカスタム設定で指定しますのでここではログドライバーの設定のみとしています。

{
  "essential": true,
  "image": "httpd",
  "name": "httpd",
  "memoryReservation": 100
  "logConfiguration": {
    "logDriver": "awsfirelens",
    "options": {}
  }
}

fluentbitコンテナ

カスタム設定を入れたいのでサイドカーとしてAWSが提供しているイメージをベースにカスタムイメージを作成しています。
カスタム設定ファイルの配置先にS3とfile(コンテナ内の設定ファイル)を指定できますが、Fargateはfileしか選択できないのでご注意ください。
config-file-valueでカスタム設定ファイルの場所を指定することによってコンテナ作成時/fluent-bit/etc/fluent-bit.confにカスタム設定ファイルをINCLUDEするような設定が追加されます。
fluentbitコンテナ自体のログはawslogsでCloudWatch Logsへ出力させるように設定しました

{
    "name": "log_router",
    "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/[ECRリポジトリ名]/fluentbit:[Image ID]",
    "cpu": 0,
    "memoryReservation": 51,
    "portMappings": [],
    "essential": true,
    "environment": [],
    "mountPoints": [],
    "volumesFrom": [],
    "user": "0",
    "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
            "awslogs-group": "/aws/ecs/ecs-aws-firelens-sidecar-container/fluentbit",
            "mode": "non-blocking",
            "max-buffer-size": "25m",
            "awslogs-region": "ap-northeast-1",
            "awslogs-stream-prefix": "ecs"
        },
        "secretOptions": []
    },
    "systemControls": [],
    "firelensConfiguration": {
        "type": "fluentbit",
        "options": {
            "config-file-type": "file",
            "config-file-value": "/fluent-bit/etc/fluent-bit-custom.conf"
        }
    }
}

ミドルウェア設定

Apache

/etc/httpd/httpd.confにログ設定を記載します。
どのVirtual Hostのログなのかを識別させるためにアクセス、エラーログのフォーマットを修正しました。
ここでは先頭にVirtual HostのServerNameを表示させるように設定し、標準出力にログを出力するように設定しています。

アクセスログ

LogFormat "%v: %h %l %u %t \"%r\" %>s %b" common
CustomLog /proc/self/fd/1 common

エラーログ

ErrorLogFormat "%v: [%{u}t] [%-m:%l] [pid %P:tid %T] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
ErrorLog /proc/self/fd/2

fluentbit

/fluent-bit/etc/fluent-bit-custom.confにログルーティングの設定を記載します。
INPUTは/fluent-bit/etc/fluent-bit.confで設定されているためここでは割愛します。

[FILTER]
    Name rewrite_tag
    Match httpd-firelens*
    Rule $log ^xxx.example.com.*$ httpd/xxx.example.com false

[OUTPUT]
    Name                cloudwatch
    Match               httpd/xxx.example.com
    region              ap-northeast-1
    log_group_name      /aws/ecs/ecs-aws-httpd-container
    log_stream_name     $(tag)/$(source)/$(ecs_task_id)

[OUTPUT]
    Name                cloudwatch
    Match               httpd-firelens*
    region              ap-northeast-1
    log_group_name      /aws/ecs/ecs-aws-httpd-container
    log_stream_name     httpd/default/$(source)/$(ecs_task_id)

各セクション以下のように設定しましまた。

  • FILTER

デフォルトでは[コンテナ名]-firelens-*というタグでログが送られてくるため、Virtual Hostのログだった場合それがわかるようにタグを修正する必要があります。
そのためここから送られてくるログ本文を確認し、先頭がVirtual HostのServerName(ここではxxx.example.com)だったらfluentbitのタグを修正するように設定しました。

  • OUTPUT

ECSのタスク定義でawsfirelensを指定するとコンテナのログもfluentbit経由でログが送られるためMatchでVirtual Hostのログかそれ以外のコンテナログかで出力先のログストリームを分けました。
Virtual Hostのログの場合はFILTERでタグを書き換えているため、書き換えたタグと一致したら指定のロググループ、ログストリームにログを出力するように設定しています。
それ以外はデフォルトタグ([コンテナ名]-firelens-*)に一致したものをdefaultとしてVirtual Hostのログと同じロググループに出力するように設定しています。

ログストリームで記載されている$()はメタデータになり、それぞれ以下が入力されます
$(ecs_task_id)はAWSから用意されているメタデータで他にもフォーマットが用意されていますので出力したいロググループ名やログストリーム名に合わせて使用してください。

ECSのメタデータ

$(tag): fluentbitのタグ
$(source): Apacheから送られてくるログに含まれるメタデータ(stdoutかstderrになる)
$(ecs_task_id): ECSのタスクID

ログ出力確認

CloudWatch Logsの画面へ移動しデフォルト、Virtual Hostのログがstdout、stderrごとに出力されていることを確認できました。

出力されるログのサンプルは以下の通りとなります。
ログのフォーマットはfluentbitで指定されているため、もし不要なメタデータを表示させたくない場合はタスク定義のfirelensConfigurationenable-ecs-log-metadataをfalseにすることで消し込みができます。
またもしログフォーマットをログ本文のみにしたい場合はOUTPUTlog_keyを設定することで指定したフィールドのみ出力できます

fluentbit – CloudWatch Output

{
    "container_id": "[コンテナID]",
    "container_name": "ecs-aws-firelens-sidecar-container",
    "log": "xxx.example.com: 127.0.0.1 - - [14/Aug/2024:17:00:00 +0000] "GET /index.html HTTP/1.1" 200 31",
    "source": "stderr"
}

さいごに

コンテナごとのログ出力であれば通常のawslogsドライバーを使用すると思いますが、細かくログルーティングが必要な場合はfluentbitを使用するのがよいと思いました。
fluentbitの設定によっては表示させるログのカスタマイズなどもできますので同じ様なログルーティングの実装を行う方の助けになればと思います。