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で簡単に導入できるので、まだ使ったことがない人は触ってみるといいかもしれません。