2022.10.15  

【ECS】fluent bitでAPPのログをCloudWatchとS3へ同時に流す

AWS,  Docker    

AWS ECS(Fargate)で動作しているAPPのログをCloudWatchLogsとS3の2箇所に流す方法についてのメモ書きです。

構成は次のようなイメージです。

ECS ─> awsfirelens ─> fluentbit  ─> CloudWatchLogs
                                                  └───>  Kinesis Data Firehose  ─> S3

ECSタスクの定義

FARGATEで動作するECSタスクを以下のように定義します。

nameにはコンテナの名前を設定します。
ログの出力元となるAPPのコンテナ名をappとしています。
また、APPログを受け取り、各AWSサービスにログを流すコンテナ名をlog_routerとします。

{
  "containerDefinitions": [
    {
      "name": "app",
      "cpu": 0,
      "environment": [],
      "essential": true,
      "image": "123456789123.dkr.ecr.ap-northeast-1.amazonaws.com/dev/APP",
      "linuxParameters": {
        "initProcessEnabled": true
      },
      "logConfiguration": {
        "logDriver": "awsfirelens",
        "options": null,
        "secretOptions": null
      },
      "portMappings": [{"containerPort": 8000, "hostPort": 8000, "protocol": "tcp"}
      ]
    },
    {
      "name": "log_router",
      "cpu": 0,
      "user": "0",
      "environment": [],
      "essential": true,
      "firelensConfiguration": {
        "options": {
          "config-file-type": "file",
          "config-file-value": "/fluent-bit/etc/extra.conf"
        },
        "type": "fluentbit"
      },
      "image": "123456789123.dkr.ecr.ap-northeast-1.amazonaws.com/dev/aws-fluentbit:latest",
      "mountPoints": [],
      "portMappings": [],
      "volumesFrom": []
    }
  ],
  "family": "app-task-definition",
  "requiresCompatibilities": ["FARGATE"],
  "networkMode": "awsvpc",
  "cpu": "512",
  "memory": "1024",
  "executionRoleArn": "arn:aws:iam::123456789123:role/ecs-task-execution-role",
  "taskRoleArn": "arn:aws:iam::123456789123:role/ecs-api-task-role",
  "placementConstraints": [],
  "runtimePlatform": {
    "cpuArchitecture": "X86_64",
    "operatingSystemFamily": "LINUX"
  },
  "tags": [],
  "volumes": []
}

appコンテナには、awsfirelensモジュールを設定します。

FireLens とは ECSで使用できるモジュールで、Fluentd やFluent Bit と連携することで、ECSから出力されるログを AWS のサービスや AWS パートナーネットワークの宛先にルーティングし、ログを保存、分析することができます。

FireLensは次の箇所で設定しています。

      "name": "app",
       # 中略
      "logConfiguration": {
        "logDriver": "awsfirelens",
        "options": null,
        "secretOptions": null
      },

ログ出力先が1箇所であれば、optionsの設定でログ出力先のAWSサービスを指定できますが、今回は2箇所に出力したいので、別の設定ファイルで出力先を設定します。

※未検証ですが、optionsで指定した設定がlog_routerであるfluentbitの/fluent-bit/etc/fluent-bit.confファイルの設定に反映されていそうです。

次にFireLensから流れたログをfluentbit(コンテナのlog_router)に渡すための設定を確認します。

      "name": "log_router",
       # 中略
      "firelensConfiguration": {
        "options": {
          "config-file-type": "file",
          "config-file-value": "/fluent-bit/etc/extra.conf"
        },
        "type": "fluentbit"
      },
    "image": "123456789123.dkr.ecr.ap-northeast-1.amazonaws.com/dev/aws-fluentbit:latest",

firelensConfigurationを設定することで、コンテナのイメージ内にあるファイル/fluent-bit/etc/extra.confをfluent-bit.confの追加設定ファイルとして読み込ませることができます。

fluent-bit.confはログの出力先サービスを設定できるファイルとなります。

ここで設定したextra.confを読み込めるようにするには、AWS専用のfluent-bitのコンテナイメージaws-for-fluent-bitを若干修正して、そのイメージをECRにアップロードする必要があります。

上記のimageの設定はそのイメージをECRから読み込んだものとなります。

IAM権限

タスク定義で設定しているロール"taskRoleArn": "arn:aws:iam::123456789123:role/ecs-api-task-role"に、CloudWatchlogsとFirehoseにアクセスするための権限を付与する必要があます。

CloudWatchLogsFullAccess (AWS管理ポリシー)
AmazonS3FullAccess(AWS管理ポリシー)
FirehosePutRecord (カスタムポリシー)

# FirehosePutRecord ポリシーの内容
      {
          "Version": "2012-10-17",
          "Statement": [
              {
                  "Effect": "Allow",
                  "Action": [
                      "firehose:PutRecordBatch"
                  ],
                  "Resource": "*"
              }
          ]
      }

コンテナイメージaws-for-fluent-bitの修正

コンテナイメージaws-for-fluent-bitを修正して、ログ送信先のAWSサービスを設定します。

まず、適当なプロジェクトを作成して、次のようにファイルを作成します。

.
├── Dockerfile
└── extra.conf

Dockerfileは次のように設定します。

aws-for-fluent-bitのイメージをDocker hubから読み込んで、そのイメージ上にextra.confを配置するだけというシンプルな内容です。

# Dockerfile
FROM amazon/aws-for-fluent-bit:2.28.2
COPY ./extra.conf /fluent-bit/etc/extra.conf

次にextra.confの設定です。

NameにAWSのサービス名を指定することで、そのサービスにログを流すことができます。
※ここにサービス名を指定することでaws-for-fluent-bit内の対象プラグインを呼び出す仕様となっているようです。

[OUTPUT]
    Name cloudwatch
    Match   **
    region ap-northeast-1
    log_group_name app-logs
    log_stream_prefix app-log-
    auto_create_group true

[OUTPUT]
    Name   firehose
    Match  **
    region ap-northeast-1
    delivery_stream S3-Delivery-Stream-app-logs

cloudwatchについてです。
regionにはログ出力対象のリージョンを指定します。
log_group_nameには、cloudwatchLogsに作成する(もしくは作成済みの)ロググループ名を指定します。

auto_create_groupをtrueにしておくと、指定したロググループ名が存在しなくても、log_group_nameに指定した名前でロググループを作成してくれます。(fluentbitマニュアル

firehoseについては、regionについてはcloudwatchの設定と同様で、delivery_streamについては、すでに作成してある、AWS kinesis の配信ストリームの名前を指定することで、kinesisにログデータを流すことができるようになります。

AWS kinesis 配信ストリームの作成方法についてはこちらのAWSマニュアルが参考になります。

今回の例では、app-logs-bucketというバケットを作成して、配信ストリームに設定します。

余談ですが、AWS kinesisの配信ストリームのS3 バケットプレフィックスを設定すると、S3のフォルダをバケット名/YYYY/MM/DDといった形にして、日付ごとにログローテーションを行うことができます。

下記はその設定例です。

# S3 バケットプレフィックス - オプション
!{timestamp:yyyy/MM/dd}/

# S3 バケットエラー出力プレフィックス - オプション
!{firehose:error-output-type}/!{timestamp:yyyy/MM/dd}/

その他の設定が行いたい場合はこちらのAWSマニュアルが参考になります。

このマニュアルに従って配信ストリームを作成するとおそらくIAMロールが原因でエラーとなります。

その場合はこちらの記事の内容を参考に対応します。【Amazon Kinesis】配信ストリーム作成時のエラー

コンテナイメージをECRにPush

続いて、作成したイメージをECRにpushします。

pushの方法についてはこちらのサイトを参考にさせて頂きました。

まずは以下のようなECRリポジトリを事前に作成します。

作成したECRリポジトリに対して、AWSマネージメントコンソールから<対象リポジトリ> --> 許可 --> ポリシーJSONの編集にアクセスして次の権限を付与してください。

< xxx >の箇所(5行目と8行目)は自身の環境にあったものに書き換えます。

{
    "Version": "2008-10-17",
    "Statement": [
      {
        "Sid": "<任意のSID>",
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::< imageをpullしてきたい環境のaccount-id >:root"
        },
        "Action": [
          "ecr:BatchCheckLayerAvailability",
          "ecr:BatchGetImage",
          "ecr:GetDownloadUrlForLayer"
        ]
      },
    ]
  }

次に、作成したDockerファイルのあるディレクトリで下記ビルドコマンドを実行します。

# ビルド
docker image build -t aws-for-fluent-bit-extra . --no-cache

AWSのCLIにログインして状態で、Dockerにログインするコマンドを実行します。

AWSのcredentialsファイルで個別にプロファイルを管理している場合はaws ecrコマンドのオプションに--profile プロファイル名を追加で設定する必要があります。

コマンド内の--regionの設定値や、アカウントID (下記123456789123の部分)は自身の環境にあったものに修正してください。

# login
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 123456789123.dkr.ecr.ap-northeast-1.amazonaws.com

ECRへプッシュするコンテナイメージのタグ付けを行います。

# タグ付け
docker tag aws-for-fluent-bit-extra:latest 123456789123.dkr.ecr.ap-northeast-1.amazonaws.com/dev/aws-for-fluent-bit-extra:latest

タグ付けが上手くいっていれば次のコマンドでECRにイメージがpushされます。

# push
docker push 123456789123.dkr.ecr.ap-northeast-1.amazonaws.com/dev/aws-for-fluent-bit-extra
# push 成功例
12669cbeacc7: Pushed 
15435cf07f8c: Pushed 
56c5e96f5fe1: Pushed 
b0fc75156d6d: Pushed 
7f30cde3f699: Pushed 
fe810f5902cc: Pushed 
dfd8c046c602: Pushed 
4fc242d58285: Pushed 
latest: digest: sha256:1234560b64c195d742c60bed0444f7abe24f0d0920cc07f02ef5fc1fe524450b size: 4280

pushされたイメージがイメージタグlatestとして登録されています。
新しいタグがpushされると前回latestだったイメージタグは<untagged>に変更されます。

ログ出力確認

一通りの設定は済んだので、最後にログが出力されるか確認します。

最初に設定したECSの定義を元にデプロイを行い、AWS環境で自身のAPPを実行します。

その後下記を確認してログが出力されていればOKです。

CloudWatch --> ロググループ --> app-logs --> ログストリーム にAPPのログが出力されていること。
S3バケットapp-logs-bucketにAPPのログが出力されていること。

コメント
現在コメントはありません。
コメントする
コメント入力

名前 (※ 必須)

メールアドレス (※ 必須 画面には表示されません)

送信