こんにちは、サイドカーより助手席に乗りたいお姉さんです。運転免許は持っていません。
ECS Fargate で、メインのコンテナ + FireLens(Fluent Bit) のサイドカー構成で、別アカウントの CloudWatch にログを出力してみました。
FireLens とは
FireLens とは、ECSのコンテナのログを、様々な分析ツール(CloudWatch, Amazon Kinesis Data Firehose, サードパーティ製ツールなど)へ転送できる仕組みです。
コンテナ内の複数箇所に出力されたログをまとめて転送したり、逆に1つのログを複数箇所に転送したりできるため、利便性が高いです。
FireLens を用いたログ出力の基本は公式ブログに載っていますので、そちらをご参照ください。
https://aws.amazon.com/jp/blogs/news/announcing-firelens-a-new-way-to-manage-container-logs/
Fluent Bitとは
Fluent Bit は、システムのログを収集・処理してくれるツールで、非常に軽量です。
FireLens で、コンテナのログドライバーとして使うことができます。
詳細は、公式ドキュメントをご参照ください。
https://docs.fluentbit.io/manual
別アカウントへのログ転送
ECSで Fluent Bit を動かす際は CloudWatch への転送用のプラグインを使うのですが、ドキュメントを見てみると、設定の中に role_arn という項目があります。
https://github.com/aws/amazon-cloudwatch-logs-for-fluent-bit/blob/mainline/README.md#plugin-options
role_arn
: ARN of an IAM role to assume (for cross account access).
for cross account access
ということは、別のアカウントへも転送できるんだな~、と思ったのですが、それ以上の説明はどこにも無く、やり方が分からない!!ということでやってみました。
転送先アカウントの設定
まず、転送先のアカウントで必要な設定をしていきます。
IAMロールの作成
「転送元のアカウントから、このアカウントの CloudWatch にログを出力していいよー!」というロールを作ります。
以下の要素を含めます。
- 信頼されたエンティティ( AssumeRolePolicyDocument )に、転送元アカウントの AssumeRole の許可を追加
- Principal に設定されたアカウントに、このロールの権限を与えてOKだよ~という設定です
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::${転送元アカウントのID}:root" }, "Action": "sts:AssumeRole" } ] } |
- CloudWatch logs への書き込み許可をするポリシーをアタッチ
- ロググループやログストリームの作成を許可します
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowLogGroup", "Effect": "Allow", "Action": [ "logs:DescribeLogStreams", "logs:CreateLogGroup" ], "Resource": "arn:aws:logs:*:${転送先アカウントのID}:log-group:*" }, { "Sid": "AllowLogStream", "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:${転送先アカウントのID}:log-group:*:log-stream:*" } ] } |
転送元アカウントの設定
転送元アカウントの設定もしていきます。
ECSタスクロールの作成
先ほど設定した転送先アカウントのロールを、ECSタスクが引き受けられるように、転送元でもタスクロールに AssumeRole を許可するポリシーをアタッチします。
1 2 3 4 5 6 7 8 9 10 11 |
{ "Version": "2012-10-17", "Statement": [ { "Sid": "allowAssumeRole", "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::${転送先アカウントのID}:role/${転送先アカウントで作成したロール名}" } ] } |
ECS タスク定義の作成
タスク定義を作ります。
今回は、プレーンなhttpdコンテナを立ち上げて、そのログを別アカウントへ転送します。
以下の値を設定していきます。それ以外はデフォルトでOKです。
- タスクロール
- 先ほど作成したタスクロールを選択
- コンテナ
- イメージURIに
httpd
を入力 - 名前は何でもOK
- イメージURIに
- ログ収集
AWS FireLens 経由でカスタム送信先にログをエクスポート
を選択- オプションを設定(キーに、ドキュメントに記載されたオプションの項目を入れる)
Name
: どこに転送するか。今回は cloudwatch です。region
: ロググループを作成するリージョンlog_group_name
,log_stream_name
: ロググループ名、ログストリーム名。ECSタスクのIDなどを動的に入れることができます。( https://github.com/aws/amazon-cloudwatch-logs-for-fluent-bit?tab=readme-ov-file#templating-log-group-and-stream-names )auto_create_group
: trueにすると、設定した名前のロググループが存在しない場合に自動で作成してくれます。role_arn
: ここに転送先のアカウントで作成したロールのarnを設定します!!!!arn:aws:iam::${転送先アカウントのID}:role/${転送先アカウントで作成したロール名}
- コンソールで見るとこんな感じ
- コンソールで FireLens の設定をすると、自動的に2つ目のコンテナ( Fluent Bit が動くコンテナ)の設定の入力欄が現れますが、今回はデフォルトのままいじりませんでした。
最終的にできたタスク定義がこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
{ "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:${転送元アカウントのID}:task-definition/fluentbit-test:1", "containerDefinitions": [ { "name": "httpd", "image": "httpd", "cpu": 0, "portMappings": [], "essential": true, "environment": [], "environmentFiles": [], "mountPoints": [], "volumesFrom": [], "ulimits": [], "logConfiguration": { "logDriver": "awsfirelens", "options": { "log_group_name": "/ecs/httpd", "log_stream_name": "$(ecs_task_id)", "region": "ap-northeast-1", "role_arn": "arn:aws:iam::${転送先アカウントのID}:role/${転送先アカウントで作成したロール名}", "auto_create_group": "true", "Name": "cloudwatch" }, "secretOptions": [] }, "healthCheck": { "command": [ "CMD-SHELL", "date" ], "interval": 30, "timeout": 5, "retries": 3 }, "systemControls": [] }, { "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": "ap-northeast-1", "awslogs-stream-prefix": "firelens" }, "secretOptions": [] }, "systemControls": [], "firelensConfiguration": { "type": "fluentbit" } } ], "family": "fluentbit-test", "taskRoleArn": "arn:aws:iam::${転送元アカウントのID}:role/ecs-task-role-fluent-bit-test", "executionRoleArn": "arn:aws:iam::${転送元アカウントのID}:role/ecsTaskExecutionRole", "networkMode": "awsvpc", "revision": 5, "volumes": [], "status": "ACTIVE", "requiresAttributes": [ { "name": "ecs.capability.execution-role-awslogs" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.28" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.21" }, { "name": "com.amazonaws.ecs.capability.logging-driver.awsfirelens" }, { "name": "com.amazonaws.ecs.capability.task-iam-role" }, { "name": "ecs.capability.container-health-check" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" }, { "name": "ecs.capability.task-eni" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.29" }, { "name": "com.amazonaws.ecs.capability.logging-driver.awslogs" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.24" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19" }, { "name": "ecs.capability.firelens.fluentbit" } ], "placementConstraints": [], "compatibilities": [ "EC2", "FARGATE" ], "requiresCompatibilities": [ "FARGATE" ], "cpu": "1024", "memory": "3072", "runtimePlatform": { "cpuArchitecture": "X86_64", "operatingSystemFamily": "LINUX" }, "registeredAt": "2024-08-26T02:19:06.916Z", "registeredBy": "himitsu", "tags": [] } |
タスクを起動して、ログが転送されることを確認
転送元アカウントで、先ほど作ったタスク定義でコンテナを上げます。
特にアクセス制限も必要無いので、デフォルトVPCに適当なECSクラスターを作成し、そこにタスクを1つ起動させました。
起動後しばらく待つと、コンソールのログのタブに Fluent Bit のログが出てきます。
/ecs/httpd っていうロググループに、${タスクID}っていうログストリームを作ったよ~とかそんなログが出ています。
では、転送先アカウントの方も見てみます。
コンソールから、 CloudWatch のロググループを見てみると、 /ecs/httpd があります。
ロググループを開いてみると、さっきのタスクIDでログストリームが作成されています。
ログイベントはこんな感じで出ています。
1つのログイベントに、ログのメッセージだけでなく、コンテナIDやタスクのarnまで出ているという親切設計✨
まとめ
Fluent Bit のオプションの role_arn に転送先アカウントのロールを設定することで、別アカウントへのログの転送ができました。
今回は標準出力を CloudWatch へ転送するという単純な構成でしたが、もっとカスタムした構成( Fluent Bit の設定ファイルをS3に置いたり、自前で Fluent Bit のコンテナを用意したり)でも別アカウントへの転送は可能なので、是非やってみてください^ヮ^