My RSS Reader

Description about Blog Author


ECSでAWS FireLensを使ってみる

CloudWatch Logsにログを溜め込むのはコスト的に避けたいですよね。CloudWatch LogsにはS3にログをエクスポートする機能がるのですが、なぜか手動実行にしか対応していません。自動的にエクスポートするにはLambdaを定期実行するか、EventBridge Schedulerで定期的にエクスポートすることになります。

EventBridge SchedulerでS3にエクスポートするのがもっとも簡単な方法だと思うのですが、これには考慮する点があります。それはCloudWatch Logsの削除のタイミングとEventBridgeの実行のタイミングが同期していない点です。削除とエクスポートのタイミングで同期していないため、エクスポートより先に削除の処理が走るとその分ログが欠損します。そこで、保存期間よりもエクスポートを実行するタイミングを短く設定することで、エクスポートするログが被ってしまうのですが、安全に運用する方法があります。。。

と色々考えて面倒になってしまったので、S3とCloudWatch Logs両方に出力できるFireLensを使うことにします。

FireLens

aws.amazon.com

FireLensはFluentdやFluent Bitのフロントエンドとして動作します。FluentdやFluent BitではなくFireLensを使う利点は先のサイトにも書かれています。

Fluentd と FluentBit を使用して、ログをどこにでも簡単に送信する方法を必要としているユーザー。 Fluentd と FluentBit のフルパワーを必要としており、タスクのログをこれらのログルーターにパイプするために必要な差別化につながらない重労働は AWS に管理して欲しいユーザー。

FireLensを使うことでFluentdやFluent Bitの運用を気にすることなく、ログの転送先やログのフィルタに注力できます。

試す

docs.aws.amazon.com

ただ試すだけであれば、こちらに記載のタスク定義を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番理解が早いと思ったので、コードを用意しました。

github.com

こちらを実行するとAWSに動作環境一式が作成されます。

まとめ

AWSの公式ドキュメントではFargateはfileしか対応していないと書いてあって一瞬途方にくれましたが、aws-for-fluent-bitのREADME.mdにはinit-プレフィックスタグのコンテナだとS3からダウンロード可能というのを見つけられて大変助かりました。

FluentdやFluent Bitは慣れないとちょっと難しく感じるかもしれないですが、FireLensで簡単に導入できるので、まだ使ったことがない人は触ってみるといいかもしれません。

[source]