
ECS サイドカーで特定ログを CloudWatch に分離出力する方法

IT技術

はじめに
今回は AWS ECS において、アプリケーションの通常ログとは別に特定のログファイルを別の CloudWatch Logs グループ に出力する「サイドカー構成」について紹介します。
ログの分離出力は、開発・運用時のトラブルシューティングやログ保守・可視化の観点で非常に有用です。今回は、Railsで外部サービスとの連携ログをexternal_service.log
に出力し、これをサイドカーのFluent BitコンテナでCloudWatch Logsに送信する構成を作ってみました。
サイドカーとは?どんな時に使う?
サイドカーコンテナとは、メインのアプリケーションコンテナと一緒に ECS タスク内で起動する補助的なコンテナのことです。
以下のようなケースで使われることが多いです:
- アプリとは別のログやメトリクスを処理したい
- 定期的なバッチ処理やクリーンアップ処理を並列で実行したい
- セキュリティ用の監視やエージェントを分離したい
- Proxy / Sidecar Pattern(Istio など)でのマイクロサービス連携
メインコンテナのサポート的な役割ですね!並走して走るのでサイドカー。
サイドカーのコストは?
ECS Fargate では、1 タスクに複数コンテナを定義できますが、課金対象はタスク数そのものです!
つまり、、、、
- サイドカーコンテナを追加してもタスク数が増えない
- 現在の CPU / メモリに余裕があるなら追加コストは基本的に発生しない
という点も嬉しいポイントです。
今回の実装だとログ転送量次第では CloudWatch コストが少し増える可能性はあります。とはいえ通常の運用では 数百円/月 程度で収まるケースがほとんどです。
やりたいことを整理
- 通常ログは STDOUT → CloudWatch(デフォルト設定)
- 外部連携ログは
external_service.log
に出力 - Fluent Bit がそれを監視し CloudWatch に送信
ECS タスク定義の変更
Terraformで記述していきます!
Fluent Bitサイドカーについて
今回はFluent BitをサイドカーとしてECSタスクに追加することで、特定ログファイル(external_service.log)をCloudWatch Logsに転送する構成をとります。
Fluent Bitは軽量なログ転送エージェントで、AWS公式が提供するイメージamazon/aws-for-fluent-bit:stable
を使うことで、CloudWatch Logsへの出力がすぐに可能です。
ちなみに自分が少しハマったポイントでもあるのですが、latest
タグではCloudWatch出力に失敗するケースがありました。CloudWatchプラグインが含まれる安定版(stable)を明示的に指定するのが安心です。
まずはCloudWatch Logsのロググループを作成します。
1resource "aws_cloudwatch_log_group" "fluent_bit" {
2 name = "/ecs/external-service-log"
3 retention_in_days = 30
4}
続いて、ECSタスク定義でFluent Bitをサイドカーとして追加します。
最終的なタスク定義(Terraform)
1resource "aws_ecs_task_definition" "ecs_task_definition" {
2 family = var.name
3 execution_role_arn = var.ecs_role_arn
4 task_role_arn = var.ecs_task_role_arn
5 network_mode = "awsvpc"
6 cpu = var.cpu_unit
7 memory = var.memory_mb
8 requires_compatibilities = ["FARGATE"]
9
10 container_definitions = <<CONTAINERS
11[
12 {
13 "name": "${var.name}",
14 "image": "${var.ecr_repository_url}:latest",
15 "portMappings": [
16 {
17 "hostPort": ${var.port},
18 "containerPort": ${var.port}
19 }
20 ],
21 "logConfiguration": {
22 "logDriver": "awslogs",
23 "options": {
24 "awslogs-group": "${var.cloud_watch_name}",
25 "awslogs-region": "ap-northeast-1",
26 "awslogs-stream-prefix": "${var.name}"
27 }
28 },
29 "mountPoints": [
30 {
31 "sourceVolume": "shared-logs",
32 "containerPath": "/shared-logs"
33 }
34 ]
35 },
36 {
37 "name": "fluent-bit",
38 "image": "amazon/aws-for-fluent-bit:stable",
39 "logConfiguration": {
40 "logDriver": "awslogs",
41 "options": {
42 "awslogs-group": "${var.external_log_group}",
43 "awslogs-region": "ap-northeast-1",
44 "awslogs-stream-prefix": "fluentbit"
45 }
46 },
47 "mountPoints": [
48 {
49 "sourceVolume": "shared-logs",
50 "containerPath": "/shared-logs"
51 }
52 ],
53 "command": [
54 "/fluent-bit/bin/fluent-bit",
55 "-i", "tail",
56 "-p", "path=/shared-logs/external_service.log",
57 "-p", "tag=external_service",
58 "-p", "read_from_head=false",
59 "-p", "db=/fluent-bit/tail-external-service.db",
60 "-o", "cloudwatch_logs",
61 "-p", "region=ap-northeast-1",
62 "-p", "log_group_name=${var.external_log_group}",
63 "-p", "log_stream_prefix=${var.env}-external-",
64 "-p", "auto_create_group=true"
65 ],
66 "essential": false
67 }
68]
69CONTAINERS
70
71 volume {
72 name = "shared-logs"
73 }
74}
Fluent Bitのcommand解説
Fluent Bitのcommandでは、tail
プラグインでログファイルを監視し、CloudWatch Logsに送信する設定をしています。
ポイントとしてはread_from_head=false
で、コンテナ起動以降のログのみを対象にする という設定です。これにより、過去の古いログを大量に取り込むことを防げます。
カスタムイメージで設定ファイルを使う方法(応用編)
今回紹介した方法ではcommand
にFluent Bitの設定を直接書いていますが、カスタムイメージを使う方法もあります!それぞれのメリット・デメリットです。
- メリット
- 設定ファイルを細かく制御できる
- 長い設定を
command
にベタ書きしなくて済む
- デメリット
- Dockerfileとfluent-bit.confを自前で管理する必要がある
Dockerfileの例
1FROM amazon/aws-for-fluent-bit:stable
2COPY fluent-bit.conf /fluent-bit/etc/fluent-bit.conf
fluent-bit.confの例
1[INPUT]
2 Name tail
3 Path /shared-logs/external_service.log
4 Tag external_service
5 DB /fluent-bit/tail-external-service.db
6 Refresh_Interval 5
7 Read_from_Head true
8
9[OUTPUT]
10 Name cloudwatch_logs
11 Match external_service
12 region ap-northeast-1
13 log_group_name ${EXTERNAL_LOG_GROUP}
14 log_stream_prefix ${ENV_NAME}-external-
15 auto_create_group true
ECRにビルド&プッシュ
1docker build -t YOUR_ECR_URL/fluent-bit:latest .
2docker push YOUR_ECR_URL/fluent-bit:latest
Terraform側(環境変数を渡す)
1{
2 "name": "fluent-bit",
3 "image": "${var.ecr_fluent_bit_repository_url}:latest",
4 "environment": [
5 {
6 "name": "SF_LOG_GROUP",
7 "value": "${var.sf_cloud_watch_name}"
8 }
9 ]
10}
どっちを選ぶ?
項目 | commandベース | カスタムイメージ |
---|---|---|
設定の柔軟性 | △ 少し大変 | ◎ かなり柔軟 |
実装のシンプルさ | ◎ 手軽に始められる | △ 管理コストあり |
Terraformの見やすさ | △ commandが長くなりがち | ◎ スッキリ |
今回は簡易的な構成のためcommandベースにしましたが、今後Fluent Bitによるフィルタやルール追加、複数ログ対応が必要になってきたら、このカスタムイメージ方式が有効だと思います!
Rails側の設定
Rails側ではRestforce
のログ出力を環境によって切り替えます。
config/initializers/external_service_logger.rb
1ExternalService.log = true
2ExternalService.configure do |config|
3 if Rails.env.production?
4 config.logger = Logger.new("/shared-logs/external_service.log", shift_age = 0, shift_size = 10 * 1024 * 1024)
5 else
6 config.logger = Logger.new("log/external_service.log")
7 end
8 config.log_level = :debug
9end
Fluent Bitはログファイルをtailしているため、ログファイルが肥大化しないようにshift_sizeを設定しましょう。
Dockerfileの変更
Fluent Bitサイドカーとログを共有するため、RailsアプリのDockerfileでも/shared-logs
ディレクトリを作成しておく必要があります。
1# Fluent Bitサイドカーと共有するログディレクトリの作成
2RUN mkdir -p /shared-logs && chown -R nobody:nogroup /shared-logs
結果の確認
CloudWatch Logsにロググループ/ecs/external-service-log
が作成され、/shared-logs/external_service.log
に出力された外部サービスのログが確認できるようになります。
CloudWatch上では次のような形で確認できます:
/aws/ecs/your-service-name
:アプリログ(デフォルト)/aws/ecs/your-env-external
:external_service.logのみ(分離)
まとめ
今回は、ECSのサイドカー構成を使って、特定ログファイルをCloudWatch Logsに分離出力する方法を紹介しました。
アプリケーションが複数の外部サービスと連携するようになると、通常ログと分離してログを管理したくなる場面が増えてきます。今回のような構成を取り入れることで、障害調査やモニタリング、ログ保守が格段にやりやすくなります。
また、ECSタスク数を増やすことなく、低コストでログ基盤を柔軟に強化できる点も大きなメリットです。アプリケーション側の修正も最低限で済み、既存サービスへの影響も最小限に抑えることができます。
今後もログ可視化や集約のニーズが高まる中で、こうした柔軟なアーキテクチャの選択肢を持っておくことは、インフラ設計の幅を広げる上で非常に有効だと感じています!
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ

趣味は映画を見ることです。