はじめに
EC2環境からECS Fargateに移行する際、ログ管理が課題になることがあります。EC2では各種ログが個別ファイルに出力されていたものが、Fargateでは標準出力に統合するのが一般的ですが、「ログが混在して見づらい」という問題が発生します。
本記事では、Fluentdサイドカーパターンを使用してRailsアプリの各種ログを分離管理する方法を解説します。
前提条件
- Railsアプリケーション(今回はstagingログなど複数種類のログを出力)
- AWS ECS Fargate
- CloudWatch Logs
現状の課題
EC2環境でのログ構成
ubuntu@ip-10-0-100-119:~$ ll /var/www/fundoor/staging/current/log/ | grep -E "*.log$"
-rw-r--r-- 1 ubuntu ubuntu 177426 Sep 24 16:39 faraday.log
-rw-rw-r-- 1 ubuntu ubuntu 471678 Sep 24 16:39 newrelic_agent.log
-rw-rw-r-- 1 ubuntu ubuntu 0 Sep 6 2024 production.log
-rw-rw-r-- 1 ubuntu ubuntu 211508180 Sep 24 16:39 sidekiq.log
-rw-rw-r-- 1 ubuntu ubuntu 18211708 Sep 24 16:42 staging.log
Fargateでの問題点
- 一時ストレージの制限: Fargateは20-200GBの一時ストレージのみ、タスク終了時に消失
- ログの混在: 標準出力に全て出力すると、Rails、Sidekiq、New Relicなどのログが混ざる
- 分析の困難: ログ種別での絞り込みが難しい
解決方法:Fluentdサイドカーパターン
アーキテクチャ概要
[Railsコンテナ] → ログファイル出力 → [共有ボリューム] → [Fluentdコンテナ] → CloudWatch Logs
├─ /rails/staging
├─ /sidekiq/worker
├─ /newrelic/agent
└─ /faraday/http
メリット
- ✅ 既存のログ構造を維持
- ✅ ログ種別ごとに分離された管理
- ✅ CloudWatch Logsでの高度な分析・検索が可能
- ✅ EFS不要でコスト削減
実装手順
1. タスク定義の設定
{
"family": "rails-app",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::account:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::account:role/ecsTaskRole",
"volumes": [
{
"name": "logs-volume"
}
],
"containerDefinitions": [
{
"name": "rails-app",
"image": "your-rails-app:latest",
"mountPoints": [
{
"sourceVolume": "logs-volume",
"containerPath": "/var/www/app/log"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/rails-app/stdout",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "rails"
}
}
},
{
"name": "fluentd",
"image": "fluent/fluentd:v1.16-debian-1",
"mountPoints": [
{
"sourceVolume": "logs-volume",
"containerPath": "/var/log/app"
}
],
"environment": [
{
"name": "FLUENTD_CONF",
"value": "fluent.conf"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/rails-app/fluentd",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "fluentd"
}
}
}
]
}
2. Fluentd設定ファイル (fluent.conf)
<system>
log_level info
</system>
# Rails staging.log
<source>
@type tail
path /var/log/app/staging.log
pos_file /var/log/fluentd/staging.log.pos
tag rails.staging
format multiline
format_firstline /^\[.*\]/
format1 /^\[(?<time>.*)\] (?<level>\w+) -- : (?<message>.*)/
time_format %Y-%m-%d %H:%M:%S
</source>
# Sidekiq sidekiq.log
<source>
@type tail
path /var/log/app/sidekiq.log
pos_file /var/log/fluentd/sidekiq.log.pos
tag sidekiq.worker
format json
time_key timestamp
time_format %Y-%m-%dT%H:%M:%S.%LZ
</source>
# New Relic newrelic_agent.log
<source>
@type tail
path /var/log/app/newrelic_agent.log
pos_file /var/log/fluentd/newrelic.log.pos
tag newrelic.agent
format /^\[(?<time>.*)\] (?<level>\w+) : (?<message>.*)/
time_format %m/%d/%y %H:%M:%S
</source>
# Faraday faraday.log
<source>
@type tail
path /var/log/app/faraday.log
pos_file /var/log/fluentd/faraday.log.pos
tag http.faraday
format /^(?<method>\w+) (?<url>.*) - (?<status>\d+) - (?<time>.*)/
</source>
# Rails staging.logをCloudWatch Logsに送信
<match rails.staging>
@type cloudwatch_logs
log_group_name /ecs/rails-app/rails
log_stream_name staging-#{ENV['HOSTNAME']}
region ap-northeast-1
auto_create_stream true
</match>
# Sidekiq logをCloudWatch Logsに送信
<match sidekiq.worker>
@type cloudwatch_logs
log_group_name /ecs/rails-app/sidekiq
log_stream_name worker-#{ENV['HOSTNAME']}
region ap-northeast-1
auto_create_stream true
</match>
# New Relic logをCloudWatch Logsに送信
<match newrelic.agent>
@type cloudwatch_logs
log_group_name /ecs/rails-app/newrelic
log_stream_name agent-#{ENV['HOSTNAME']}
region ap-northeast-1
auto_create_stream true
</match>
# Faraday logをCloudWatch Logsに送信
<match http.faraday>
@type cloudwatch_logs
log_group_name /ecs/rails-app/faraday
log_stream_name http-#{ENV['HOSTNAME']}
region ap-northeast-1
auto_create_stream true
</match>
3. IAMポリシーの設定
taskRoleに以下のポリシーをアタッチ:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": "arn:aws:logs:ap-northeast-1:*:log-group:/ecs/rails-app/*"
}
]
}
4. Dockerfileへの設定追加
Railsアプリケーションのコンテナにfluentd設定を含める場合:
FROM ruby:3.1
# アプリケーションのセットアップ
WORKDIR /var/www/app
COPY . .
RUN bundle install
# Fluentd設定ファイルをコピー
COPY fluent.conf /fluentd/etc/fluent.conf
# ログディレクトリの作成
RUN mkdir -p /var/www/app/log
RUN mkdir -p /var/log/fluentd
CMD ["rails", "server"]
動作確認
デプロイ後の確認
- ECSタスクの起動確認
aws ecs list-tasks --cluster your-cluster-name --service-name your-service-name
- CloudWatch Logsグループの確認
aws logs describe-log-groups --log-group-name-prefix "/ecs/rails-app"
-
各ログストリームにデータが流れているか確認
-
/ecs/rails-app/rails- Rails application logs -
/ecs/rails-app/sidekiq- Background job logs -
/ecs/rails-app/newrelic- New Relic agent logs -
/ecs/rails-app/faraday- HTTP client logs
-
CloudWatch Logs Insightsでの分析例
# Railsエラーログの検索
fields @timestamp, level, message
| filter @logGroup = "/ecs/rails-app/rails"
| filter level = "ERROR"
| sort @timestamp desc
| limit 100
# Sidekiqジョブの実行時間分析
fields @timestamp, duration, job_class
| filter @logGroup = "/ecs/rails-app/sidekiq"
| filter ispresent(duration)
| stats avg(duration) by job_class
注意点とベストプラクティス
パフォーマンスの考慮
- リソース配分: Fluentdコンテナにも適切なCPU・メモリを割り当て
- バッファリング: 大量ログの場合はFluentdのバッファ設定を調整
- ログローテーション: アプリケーション側でのログローテーション設定
コスト最適化
- ログ保持期間: CloudWatch Logsの保持期間を用途に応じて設定
- 不要ログの削除: デバッグログなど本番で不要なログは無効化
- ログレベル: 本番環境では適切なログレベルに設定
モニタリング
- Fluentdの監視: Fluentdコンテナ自体の監視設定
- ログ送信の遅延: CloudWatch Logsへの送信遅延の監視
- アラート設定: エラーログ増加時のアラート設定
代替案の比較
| 方式 | メリット | デメリット | 適用場面 |
|---|---|---|---|
| 標準出力統合 | シンプル、軽量 | ログが混在、分析困難 | 小規模、単純なログ |
| EFS利用 | 既存構造そのまま | コスト高、設定複雑 | 大容量、永続化必要 |
| Fluentdサイドカー | ログ分離、柔軟性 | 設定複雑、リソース使用 | 中〜大規模、分析重視 |
まとめ
ECS FargateへのRailsアプリ移行において、Fluentdサイドカーパターンを使用することで:
- 既存のログ構造を維持しながらクラウドネイティブな環境に移行
- CloudWatch Logsの強力な分析機能を活用した運用監視
- EFS不要でコストを抑制しつつ、スケーラブルなログ管理
この方法により、EC2環境からFargateへのスムーズな移行と、効率的なログ運用が実現できます。
実際の運用では、ログの量やアクセスパターンに応じてFluentdの設定をチューニングし、最適なパフォーマンスとコストバランスを見つけることが重要です。