ECSでAWS FireLensを使ってみる
CloudWatch Logsにログを溜め込むのはコスト的に避けたいですよね。CloudWatch LogsにはS3にログをエクスポートする機能がるのですが、なぜか手動実行にしか対応していません。自動的にエクスポートするにはLambdaを定期実行するか、EventBridge Schedulerで定期的にエクスポートすることになります。
EventBridge SchedulerでS3にエクスポートするのがもっとも簡単な方法だと思うのですが、これには考慮する点があります。それはCloudWatch Logsの削除のタイミングとEventBridgeの実行のタイミングが同期していない点です。削除とエクスポートのタイミングで同期していないため、エクスポートより先に削除の処理が走るとその分ログが欠損します。そこで、保存期間よりもエクスポートを実行するタイミングを短く設定することで、エクスポートするログが被ってしまうのですが、安全に運用する方法があります。。。
と色々考えて面倒になってしまったので、S3とCloudWatch Logs両方に出力できるFireLensを使うことにします。
FireLens
FireLensはFluentdやFluent Bitのフロントエンドとして動作します。FluentdやFluent BitではなくFireLensを使う利点は先のサイトにも書かれています。
Fluentd と FluentBit を使用して、ログをどこにでも簡単に送信する方法を必要としているユーザー。 Fluentd と FluentBit のフルパワーを必要としており、タスクのログをこれらのログルーターにパイプするために必要な差別化につながらない重労働は AWS に管理して欲しいユーザー。
FireLensを使うことでFluentdやFluent Bitの運用を気にすることなく、ログの転送先やログのフィルタに注力できます。
試す
ただ試すだけであれば、こちらに記載のタスク定義をECSにデプロイするだけで動作します。
軽く説明するとcontainerDefinitions
の1つ目の要素がFireLensのタスクです。
{
"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"
}
},
「logConfiguration」にはFireLens自体のログが「awslogs(CloudWatch Logs)」に出力されるように設定されています。「firelensConfiguration」でfluentbitを使用するように指定されています。
2つ目の要素にはデモアプリの設定が書かれています。
{
"essential": true,
"image": "httpd",
"name": "app",
"logConfiguration": {
"logDriver": "awsfirelens",
"options": {
"Name": "firehose",
"region": "us-west-2",
"delivery\_stream": "my-stream",
"log-driver-buffer-limit": "2097152"
}
},
"memoryReservation": 100
}
こちらの「logConfiguration」では、「logDriver」に「awsfirelens」がセットされていますので、FireLensに出力されます。「options」にはFireLensがどこに出力するか指定します。今回の場合ですと、「us-west-2」にある「firehose」に出力するように指定されています。
このように、どこに出力するのかなどをタスク定義の中で完結できます。
※ロール・ポリシーに注意
- FireLensのログをawslogsに出力するにはタスク実行ロール(execution_role)にログ出力の権限が必要です。Amazon ECS ログを CloudWatch に送信する - Amazon Elastic Container Service
- FireLensがログを他のサービスに配信するにはタスクロール(task_role)にログ出力の権限が必要です。S3に配信するならs3:PutObject、CloudWatch Logsに配信するならlogs:PutLogEvents…etc
複数の出力先
FireLens(というかFluentdやFluent Bit)は複数の出力先を指定できます。が、タスク定義から設定できるのは1つの出力先だけです。複数の出力先を設定したい場合は設定ファイルを別途S3やコンテナ内に用意します。
FirelensConfiguration - Amazon Elastic Container Service
S3に置いておいて、起動時に読み込んでくれるなら、これはこれでいいのですが
Tasks hosted on AWS Fargate only support the file configuration file type.
Fargateには対応していないようです。悲しい・・・
と、諦めかけていたのですが、色々検索してみるとinit-
プレフィックスのタグがあるコンテナは起動時にS3を確認してくれるそうで。ありがたや
GitHub - aws/aws-for-fluent-bit: The source of the amazon/aws-for-fluent-bit container image
使えるタグはこちらから確認できます。
https://gallery.ecr.aws/aws-observability/aws-for-fluent-bit
設定方法は以下の通り
{
"name": "log\_router",
"image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:init-2.32.4",
"cpu": 0,
"memoryReservation": 50,
"portMappings": \[\],
"essential": true,
"environment": \[
{
"name": "aws\_fluent\_bit\_init\_s3\_1",
"value": "arn:aws:s3:::example-poc-fluent-bit-conf/extra.conf"
}
\],
"mountPoints": \[\],
"volumesFrom": \[\],
"user": "0",
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "firelens-container",
"awslogs-create-group": "true",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "firelens"
}
},
"systemControls": \[\],
"firelensConfiguration": {
"type": "fluentbit"
}
},
firelensのイメージを変更し、environmentで「aws_fluent_bit_init_s3_1」というキーにS3に置いてある設定ファイルまでのARNを指定しています。
S3に置いてあるextra.confは以下
[OUTPUT] Name cloudwatch_logs Match ** region ap-northeast-1 log_key log log_group_name firelens-container log_stream_prefix httpd/ auto_create_group true
[OUTPUT] Name s3 Match ** bucket example-poc-ecs-logs region ap-northeast-1 total_file_size 250M compression gzip s3_key_format /httpd/%Y/%m/%d/%H_%M.gz static_file_path On
appの内容は以下の通り
{
"name": "app",
"image": "httpd",
"cpu": 0,
"memoryReservation": 100,
"portMappings": \[
{
"containerPort": 80,
"hostPort": 80,
"protocol": "tcp"
}
\],
"essential": true,
"environment": \[\],
"mountPoints": \[\],
"volumesFrom": \[\],
"logConfiguration": {
"logDriver": "awsfirelens"
},
"systemControls": \[\]
}
appは単に「logConfiguration」のoptionsを削除しました。
task_roleやexecution_roleの設定がうまくいっていてextra.confの置き場も間違いなければ、FireLensのタスクのログにはこのようなものが流れてきます。
設定が間違っているとAccess Deniedや403なども文字がでてくるでしょう。
コード
あーだこーだ書いたところでコードを見るのが1番理解が早いと思ったので、コードを用意しました。
こちらを実行するとAWSに動作環境一式が作成されます。
まとめ
AWSの公式ドキュメントではFargateはfileしか対応していないと書いてあって一瞬途方にくれましたが、aws-for-fluent-bitのREADME.mdにはinit-
プレフィックスタグのコンテナだとS3からダウンロード可能というのを見つけられて大変助かりました。
FluentdやFluent Bitは慣れないとちょっと難しく感じるかもしれないですが、FireLensで簡単に導入できるので、まだ使ったことがない人は触ってみるといいかもしれません。