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サンプリングルール取得用です。
なぜ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_signalsとlogs.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マッピング機能がないためです。
- Application Signalsは
peer.service属性でサービス間のリンクを判定 - JavaではOTEL_INSTRUMENTATION_COMMON_PEER_SERVICE_MAPPINGが実装済み
- Pythonでは同等の機能が未実装(2026年2月時点)
Service DiscoveryでもService Connectでも発生します。上記OpenTelemetry計装レベルの制限のようです。
影響範囲
| 機能 | 状態 |
|---|---|
| トレース詳細 | ✅ サービス間の関係が正しく表示される |
| サービスマップ | ⚠️ 内部サービスがRemote Endpointとして表示 |
| メトリクス | ✅ 正常に収集される |
| SLO | ✅ 設定・監視可能 |
まとめ
ECS Fargate + PythonでCloudWatch Application Signalsを導入しました。
メリット:
- Dockerfileの変更不要
- 自動計装でコード変更なし
- サービスマップで依存関係を可視化
- AWS純正のため追加SaaS契約不要
注意点:
- サービスマップでは内部サービス間のリンクが表示されない場合がある(詳細は「補足」参照)
- 本番環境ではinitコンテナ・CloudWatch Agentともに固定バージョンを推奨
Discussion