📊

ECS Fargate + PythonでCloudWatch Application Signalsを導入する

に公開

はじめに

背景

ECS Fargate上で動作するPythonアプリケーションに、オブザーバビリティを導入したいと考えていました。

  • どのサービスと通信しているか把握しづらい
  • ログはあるが、問題発生時にリクエスト単位で追跡するのが困難
  • レイテンシやエラー率の集計を自動でしてほしい
  • アプリケーションのコードを変更せずにインフラ設定のみで導入したい
  • すでにCloudWatchを利用しており、エコシステム内で完結したい

これらの条件を満たすソリューションとして、CloudWatch Application Signalsを選択しました。アプリケーションコードやDockerfileの変更なしで分散トレーシングとメトリクス自動収集を実現できます。

この記事では、ECS Fargate + Python環境での導入手順を紹介します。

Application Signalsとは

CloudWatch Application Signalsは、アプリケーションのパフォーマンスを自動的に計測・可視化するサービスです。

  • コード変更不要の自動計装
  • サービスマップによる依存関係の可視化
  • レイテンシ、エラー率、リクエスト数の自動収集
  • SLO設定とアラート

アーキテクチャ

3つのコンテナで構成します。

コンテナ 役割
init Python OpenTelemetry計装ライブラリをボリュームにコピー
app アプリケーション本体
cloudwatch-agent OpenTelemetryデータをCloudWatchに送信
ポート番号について

OTLP(OpenTelemetry Protocol)の標準ポートは4317(gRPC)/4318(HTTP)ですが、CloudWatch Agentは4316を使用します。これはCloudWatch Agent固有の設定です。また、2000番ポートはX-Rayサンプリングルール取得用です。

参考: Deploy using the sidecar strategy - Amazon CloudWatch

なぜCloudWatch Agentを使うのか

ECSでApplication Signalsを使う場合、ADOT(AWS Distro for OpenTelemetry)CollectorではなくCloudWatch Agentを使います。Application Signalsのメトリクス送信には専用の設定が必要で、CloudWatch Agentがこれをサポートしています。

前提条件

1. アカウントでApplication Signalsを有効化

CloudWatchコンソール → Application Signals → 「ECSでApplication Signalsを有効にする」をクリックします。これはアカウントレベルで1回だけ実行します。

2. IAM権限

タスクロールにCloudWatchAgentServerPolicyとX-Ray権限、タスク実行ロールにSSM読み取り権限が必要です。詳細はサンプルリポジトリを参照してください。

実装手順

1. SSM ParameterにCloudWatch Agent設定を保存

CloudWatch Agentの設定JSONをSSM Parameterに保存します。Application Signalsを有効化する最小構成はtraces.traces_collected.application_signalslogs.metrics_collected.application_signalsの指定のみです。

具体的な設定はサンプルリポジトリを参照してください。

2. タスク定義

3つのコンテナで構成します。完全なTerraformコードはサンプルリポジトリを参照してください。

タスク定義の構造:
├── init (ADOT Python) - OpenTelemetryライブラリをボリュームにコピー
├── app (アプリケーション) - 環境変数でOpenTelemetryを有効化
└── cloudwatch-agent - テレメトリをCloudWatchに送信

環境変数の設定はAWS公式ドキュメントを参照してください。

CloudWatch Agentのessential設定

サンプルではessential: trueとしています。ECSタスク定義のessentialパラメータに記載があり、essential: trueのコンテナが停止すると、タスク内の全コンテナが停止します。

設定 メリット デメリット
essential: true Agent異常時にタスク再起動、トレース欠落を検知しやすい Agent障害でアプリも停止
essential: false Agent障害でもアプリは継続 トレース欠落に気づきにくい

要件に応じて検討してください。

トレース除外設定

不要なトレースを除外することで、ノイズを減らしコストを抑えられます。

OTEL_PYTHON_EXCLUDED_URLS

サンプルではヘルスチェックをトレースから除外しています。

{ "name": "OTEL_PYTHON_EXCLUDED_URLS", "value": "health" }

カンマ区切りで複数パターンを指定可能です。詳細はOpenTelemetry Python Configurationを参照してください。

OTEL_PYTHON_DISABLED_INSTRUMENTATIONS

今回は使用していませんが、特定の計装を完全に無効化したい場合はOTEL_PYTHON_DISABLED_INSTRUMENTATIONSが使えます。詳細はOpenTelemetry Python Configurationを参照してください。

サンプリング設定

デフォルトではX-Rayのサンプリングルールが適用されます。高トラフィック環境ではトレースが欠落する可能性があります。

サンプリングルールはX-Rayコンソールで設定できます。詳細はX-Rayサンプリングルールのドキュメントを参照してください。

無効化(ロールバック)

問題が発生した場合、以下の環境変数でApplication Signalsを無効化できます。

{ "name": "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", "value": "false" }

タスク定義を更新してデプロイすることで、アプリケーションコードを変更せずに切り戻しが可能です。

動作確認

デプロイ後、CloudWatchコンソール → Application Signals → サービスを開くと、自動的にサービスが検出されます。

確認できる情報:

  • サービスマップ(依存関係の可視化)
  • レイテンシ(p50/p90/p99)
  • エラー率
  • リクエスト数

サービスマップ

サービス間の依存関係が自動的に可視化されます。

サービスマップ

トレース

リクエスト単位でのトレースを確認できます。各サービスでの処理時間や呼び出し順序が可視化されます。

トレース

補足: サービスマップの表示について

現象

Application Signalsのサービスマップで、ECS内部サービスが「Remote Endpoint」として表示され、計装済みサービスノードとリンクされませんでした。

期待: [frontend] → [api] → [backend]
実際: [frontend] → [api:8080 (Remote)]
                   [api] → [backend:8080 (Remote)]
                          [backend] → [httpbin.org]

原因

Python OpenTelemetry auto-instrumentationにpeer.serviceマッピング機能がないためです。

Service DiscoveryでもService Connectでも発生します。上記OpenTelemetry計装レベルの制限のようです。

影響範囲

機能 状態
トレース詳細 ✅ サービス間の関係が正しく表示される
サービスマップ ⚠️ 内部サービスがRemote Endpointとして表示
メトリクス ✅ 正常に収集される
SLO ✅ 設定・監視可能

まとめ

ECS Fargate + PythonでCloudWatch Application Signalsを導入しました。

メリット:

  • Dockerfileの変更不要
  • 自動計装でコード変更なし
  • サービスマップで依存関係を可視化
  • AWS純正のため追加SaaS契約不要

注意点:

  • サービスマップでは内部サービス間のリンクが表示されない場合がある(詳細は「補足」参照)
  • 本番環境ではinitコンテナ・CloudWatch Agentともに固定バージョンを推奨

参考資料

Discussion