0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ECS FargateでRailsアプリのログを種類別に分離管理する方法

Posted at

はじめに

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での問題点

  1. 一時ストレージの制限: Fargateは20-200GBの一時ストレージのみ、タスク終了時に消失
  2. ログの混在: 標準出力に全て出力すると、Rails、Sidekiq、New Relicなどのログが混ざる
  3. 分析の困難: ログ種別での絞り込みが難しい

解決方法: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"]

動作確認

デプロイ後の確認

  1. ECSタスクの起動確認
aws ecs list-tasks --cluster your-cluster-name --service-name your-service-name
  1. CloudWatch Logsグループの確認
aws logs describe-log-groups --log-group-name-prefix "/ecs/rails-app"
  1. 各ログストリームにデータが流れているか確認
    • /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サイドカーパターンを使用することで:

  1. 既存のログ構造を維持しながらクラウドネイティブな環境に移行
  2. CloudWatch Logsの強力な分析機能を活用した運用監視
  3. EFS不要でコストを抑制しつつ、スケーラブルなログ管理

この方法により、EC2環境からFargateへのスムーズな移行と、効率的なログ運用が実現できます。

実際の運用では、ログの量やアクセスパターンに応じてFluentdの設定をチューニングし、最適なパフォーマンスとコストバランスを見つけることが重要です。

参考資料

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?