ECSのログ事情について
AWSのECSによるコンテナ運用では、ログの出力がデフォルトでCloudWatch
に限定されています。
これはECSのログドライバがawslogs
になっているということになりますが、実用面では力不足であることが否めません。
S3やElasticsearchにログを流すには?Datadogなどのサードパーティツールにログを流すには?
解決方法として、AWSの公式でFireLens
という方法が紹介されています。
FireLensはfluentbit
のコンテナをサイドカーとして起動し、ログの収集をfluentbit
の機能で実行するというものです。
(fluentd
も選択できます)
実際に、AWSコンソールのECSのタスク定義を設定する画面には以下のような設定項目があります。
FireLensの統合を有効にする
を選択すると、自動で906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest
のコンテナがサイドカーとして追加されるようになります。
このコンテナはAWSの公式コンテナです。
当記事では、このコンテナを自身でカスタマイズしたものを適用する一例を紹介します。
コンテナ定義
それではコンテナ定義がどのようになるかTerraformで見てみましょう。
data "template_file" "default" {
template = <<EOF
[
{
"image": "${var.aws-account-id}.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-sample/go:latest",
"name": "go",
"essential": false,
"logConfiguration": {
"logDriver": "awsfirelens"
},
"portMappings": [],
"cpu": 64,
"memoryReservation": 128
},
{
"image": "${var.aws-account-id}.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-sample/nginx:latest",
"name": "nginx",
"essential": true,
"logConfiguration": {
"logDriver": "awsfirelens"
},
"portMappings": [
{
"hostPort": 80,
"protocol": "tcp",
"containerPort": 80
}
],
"cpu": 64,
"memoryReservation": 128
},
{
"image": "${var.aws-account-id}.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-sample/fluentbit:latest",
"name": "log_router",
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/firelens-sample",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
},
"firelensConfiguration": {
"type": "fluentbit",
"options": {
"config-file-type": "file",
"config-file-value": "/fluent-bit/etc/fluent-bit_custom.conf"
}
},
"portMappings": [],
"cpu": 128,
"memoryReservation": 256
}
]
EOF
}
Goのコンテナ、Nginxのコンテナ、fluentbitのコンテナを定義しました。
以下を確認してください。
"logConfiguration": {
"logDriver": "awsfirelens"
}
logDriverをawsfirelens
としています。
なお、fluentbitコンテナ自体のログドライバはawslogs
としてください。
そして注目すべき定義はこちらです。
"firelensConfiguration": {
"type": "fluentbit",
"options": {
"config-file-type": "file",
"config-file-value": "/fluent-bit/etc/fluent-bit_custom.conf"
}
}
config-file-valueを指定しています。
この/fluent-bit/etc/fluent-bit_custom.conf
に独自カスタマイズしたfluentbitの設定を記載しましょう。
fluentbitの設定
よくある設定
サンプルコードをもとに進めます。
先ほどの/fluent-bit/etc/fluent-bit_custom.conf
を記載していきましょう。
[SERVICE]
Flush 1
Grace 30
Log_Level info
[OUTPUT]
Name cloudwatch
Match *
log_key log
region ap-northeast-1
log_group_name /ecs/firelens-sample
log_stream_name container
[OUTPUT]
Name firehose
Match *
delivery_stream my-firehose
region ap-northeast-1
OUTPUTにcloudwatch
とfirehose
を指定していることに注目してください。
後述しますが、amazon/aws-for-fluent-bit
のコンテナをベースに進めますのでデフォルトで上記のプラグインがインストールされた状態になっています。
さて、この設定でcloudwatch
とfirehose
にログが出力されるようになりますが、一点、Match *
に注目しましょう。
これはFireLensが収集したログを全て出力されるようになっており、
例えばDataLakeのようなログの流し方であれば問題ありませんが、もう少し整形してから流したいというニーズがあるかもしれません。
そこでParser
を使って、ログをコンテナごとのCloudWatchストリームに分けるということをしてみましょう。
Parser
先ほどのconfを以下のようにしました。
[SERVICE]
Flush 1
Grace 30
Log_Level info
Streams_File stream_processor.conf
Parsers_File parser.conf
[FILTER]
Name parser
Match *
Key_Name container_name
Parser container
Reserve_Data true
[OUTPUT]
Name cloudwatch
Match combine.nginx
log_key log
region ap-northeast-1
log_group_name /ecs/firelens-sample
log_stream_name nginx
[OUTPUT]
Name cloudwatch
Match combine.go
log_key log
region ap-northeast-1
log_group_name /ecs/firelens-sample
log_stream_name go
[OUTPUT]
Name firehose
Match container
delivery_stream my-firehose
region ap-northeast-1
以下の箇所でログのパースをおこないます。
[FILTER]
Name parser
Match *
Key_Name container_name
Parser container
Reserve_Data true
さてここで、そもそもFireLens
からはどのようなログが収集されているのでしょうか。
当然、自身で流したアプリケーション用のログ、Nginxのアクセスログなどをイメージしますが
実はそのほかのメタ情報を付与した状態で収集しています。
以下を確認してください。
{
"container_id": "abcde12345",
"container_name": "/ecs-firelens-sample-1-nginx-xxxyyyzzz",
"ecs_cluster": "arn:aws:ecs:ap-northeast-1:1234567890123:cluster/firelens-sample",
"ecs_task_arn": "arn:aws:ecs:ap-northeast-1:1234567890123:task/aaa-bbb-ccc-ddd-eee",
"ecs_task_definition": "firelens-sample:1",
"log": "10.0.0.0 - - [14/Mar/2020:10:00:00 +0000] \"GET /healthcheck HTTP/1.1\" 200 0 \"-\" \"ELB-HealthChecker/2.0\" \"-\"",
"source": "stdout"
}
firelensが収集するログの例です。log
というキーが出力されるログですが、それ以外にタスク定義の情報やコンテナ名などもあります。
今回はこれらの情報でパースをしてみます。
container_name
をパースしてnginx
という部分を抜き出します。
Goコンテナの場合はgo
という文字を抜き出します。
[PARSER]
Name container
Format regex
Regex ^\/(?<task_name>(ecs-firelens-sample))-(?<task_revision>\d+)-(?<container_name>.+)-(?<target_name>.+)$
正規表現で抜き出してみました。以下のようにパースされました。
{
"task_name": "ecs-firelens-sample",
"task_revision": 1,
"container_name": "nginx",
"target_name": "xxxyyyzzz",
}
container_name
が「nginx」となっています。
これでコンテナ別にログの出力先を分けるということができそうです。
もちろん、ログそのものに対してパースをしてログを振り分けることができれば様々な要件にも対応できるかと思います。
いろいろなパターンを考慮して要件に沿った設計を考えてみてください。
Stream Processor
ログの種類別にタグを振り分けることができます。
先ほどのcontainer_name
「nginx」「go」をそれぞれ「combine.nginx」「combine.go」というタグを付与してみましょう。
[STREAM_TASK]
Name nginx
Exec CREATE STREAM nginx WITH (tag='combine.nginx') AS SELECT log FROM TAG:'*-firelens-*' WHERE container_name = 'nginx';
[STREAM_TASK]
Name go
Exec CREATE STREAM go WITH (tag='combine.go') AS SELECT log FROM TAG:'*-firelens-*' WHERE container_name = 'go';
# すべてのログ
[STREAM_TASK]
Name container
Exec CREATE STREAM container WITH (tag='container') AS SELECT * FROM TAG:'*-firelens-*';
SQLのような形式で記載します。
FireLensのログは元々*-firelens-*
にマッチするタグが付与されています。
それにWHERE container_name = 'nginx'
などとすることでnginxの新しいタグを追加できます。
追加したタグは、[OUTPUT]
セクションで指定しましょう。
[OUTPUT]
Name cloudwatch
Match combine.nginx
log_key log
region ap-northeast-1
log_group_name /ecs/firelens-sample
log_stream_name nginx
Docker定義
続いてDocker定義です。
amazon/aws-for-fluent-bit
コンテナをベースに使います。
FROM amazon/aws-for-fluent-bit:2.1.1
COPY ./docker/fluentbit/fluent-bit_custom.conf /fluent-bit/etc/fluent-bit_custom.conf
COPY ./docker/fluentbit/stream_processor.conf /fluent-bit/etc/stream_processor.conf
COPY ./docker/fluentbit/parser.conf /fluent-bit/etc/parser.conf
これでECRにプッシュすれば立派なログ収集コンテナとして活躍できるでしょう。
$ aws ecr get-login-password | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com
$ docker build -t firelens-sample-fluentbit:latest -f docker/fluentbit/Dockerfile .
$ docker tag firelens-sample-fluentbit:latest ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-sample/fluentbit:latest
$ docker push ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/firelens-sample/fluentbit:latest
おわり
fluentbitを使ったログ収集用コンテナのカスタマイズについて解説しました。
誰かの参考になれば幸いです。