ECS Service Connectの新機能「Zone-Awareルーティング」で同一AZ優先のサービス間通信を試してみた
はじめに
2026年7月1日、ECS Service ConnectにZone-Awareルーティング機能が追加されました。
マルチAZ構成のECSサービス間通信において、リクエストを同一AZ内のエンドポイントに優先ルーティングすることで、クロスAZデータ転送コストとレイテンシを削減する機能です。
従来のService Connectはラウンドロビンで全AZに均等分散していたため、マルチAZ構成では必然的にクロスAZ通信が発生していました。マルチAZ構成自体は可用性のために必要ですが、AZ間のデータ転送には追加料金が発生し、レイテンシも増加します。
今回のアップデートにより、デフォルトで同一AZ優先ルーティングが有効になります。
| 項目 | 従来の Service Connect | Zone-Aware 対応後 |
|---|---|---|
| ルーティング方式 | ラウンドロビン(全AZ均等分散) | 同一AZ優先 |
| クロスAZデータ転送 | マルチAZ構成で必然的に発生 | 同一AZ内へ優先ルーティングされるため削減 |
| 可用性 | AZ障害時は自動フェイルオーバー | 同様(容量不足時はクロスAZへ自動再分散) |
| 設定変更 | — | 不要(デフォルト有効) |
| 既存サービスへの適用 | — | 1回の再デプロイで有効化 |
| 追加料金 | — | なし |
仕組み
Zone-Awareルーティングは、Service Connectプロキシ(Envoyサイドカー)のゾーン認識ルーティング機能を利用しています。
動作の流れは以下のとおりです。
- エンドポイント検出: プロキシが宛先サービスの全エンドポイントとそのAZ配置を把握する
- 同一AZ優先: リクエスト送信元と同じAZのエンドポイントに優先的にルーティングする
- 残余容量ルーティング: 同一AZに収まりきらないトラフィックは、他のAZの残余容量に基づいて分配する
- フォールバック: 同一AZのエンドポイントが不健全または不足の場合、他のAZに自動的にルーティングする
有効化の閾値条件
Zone-Awareルーティングが有効になるには、宛先サービスのエンドポイント数が 2 × AZ数 以上必要です。
- 2AZ構成: 最低4エンドポイント
- 3AZ構成: 最低6エンドポイント
この閾値を下回る場合はAZを考慮しない通常の負荷分散にフォールバックし、エンドポイントが増えると自動的に再有効化されます。これは単一AZへの過負荷を防ぐための仕組みです。
類似概念との比較
同一AZ優先でクロスAZ転送を減らすアプローチは、他のAWSサービスにも存在します。
| 仕組み | 対象通信 | 同一AZ優先の実現方法 |
|---|---|---|
| Regional NAT Gateway | アウトバウンド通信 | ワークロード検出によるAZ親和性(動的) |
| Service Connect Zone-Aware | ECSサービス間通信 | Envoyプロキシの重み付け(動的) |
| ALB クロスゾーン無効化 | クライアント→ターゲット | ALBノードの分散設定 |
検証
実際に2AZ構成のECSクラスタでZone-Awareルーティングの動作を確認しました。
検証構成
ECSクラスタ (Service Connect namespace: "test-sc-zone-aware")
├── サービスA (クライアント役, 2タスク)
│ ├── AZ-a: 1タスク (ECS Exec有効)
│ └── AZ-c: 1タスク (ECS Exec有効)
└── サービスB (サーバー役, 4タスク)
├── AZ-a: 2タスク
└── AZ-c: 2タスク
サービスBは自タスクのAZ情報をレスポンスに含めるシンプルなHTTPサーバーです。サービスAからService Connect経由でリクエストし、レスポンスのAZ情報からどのAZのタスクにルーティングされたかを確認します。サービスBを4タスクにしているのは閾値条件(2 × AZ数)を満たすためです。
サービスBのアプリ
from flask import Flask, jsonify
import requests, os
app = Flask(__name__)
@app.route("/")
def az():
meta_uri = os.environ.get("ECS_CONTAINER_METADATA_URI_V4", "")
if meta_uri:
task_meta = requests.get(meta_uri + "/task", timeout=2).json()
return jsonify({"az": task_meta.get("AvailabilityZone", "unknown")})
return jsonify({"az": "no-metadata-uri"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
Service Connect の設定
サービスBのタスク定義でポートマッピングにnameとappProtocolを指定し、サービス作成時にService Connectのサーバー側設定を行います。
{
"portMappings": [
{
"containerPort": 8080,
"protocol": "tcp",
"name": "http",
"appProtocol": "http"
}
]
}
サービス作成時のService Connect設定:
{
"enabled": true,
"namespace": "test-sc-zone-aware",
"services": [
{
"portName": "http",
"discoveryName": "service-b",
"clientAliases": [
{
"port": 8080,
"dnsName": "service-b"
}
]
}
]
}
サービスA(クライアント側)はクライアントモードのみで設定します:
{
"enabled": true,
"namespace": "test-sc-zone-aware"
}
Zone-Awareルーティングのための追加設定は不要です。デフォルトで有効になります。
検証1: Zone-Aware ルーティングの動作確認
サービスAの各AZのタスクにECS Execで接続し、Service Connect経由でservice-bに20回リクエストを送信しました。
# AZ-aのタスクからリクエスト
aws ecs execute-command --cluster test-sc-zone-aware --task <task-id> \
--container app --interactive \
--command 'sh -c "for i in $(seq 1 20); do curl -s http://service-b:8080/; echo; done"'
AZ-a(ap-northeast-1a)のタスクから実行:
{"az":"ap-northeast-1a"}
{"az":"ap-northeast-1a"}
{"az":"ap-northeast-1a"}
...(以下すべて同様)
20/20(100%)が同一AZ(ap-northeast-1a)のエンドポイントにルーティングされました。
AZ-c(ap-northeast-1c)のタスクから実行:
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
...(以下すべて同様)
こちらも20/20(100%)が同一AZ(ap-northeast-1c)にルーティングされました。エンドポイントが均等に配置されている場合、100%同一AZで処理されることが確認できました。
検証2: フォールバック動作の確認
サービスBのAZ-a側のタスクを2つとも手動停止し、AZ-aのクライアントからリクエストした場合の挙動を確認しました。
# AZ-aのservice-bタスクを停止
aws ecs stop-task --cluster test-sc-zone-aware --task <az-a-task-id-1> --reason "test fallback"
aws ecs stop-task --cluster test-sc-zone-aware --task <az-a-task-id-2> --reason "test fallback"
AZ-aのサービスAタスクからリクエスト(停止後20秒待機してから実行):
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
10/10(100%)がAZ-cにルーティングされました。AZ-a側のエンドポイントが不在となったことで、リクエストはすべてAZ-cの健全なエンドポイントに送られています。
なお、ECSはdesired-countを維持するために新しいタスクを起動します。同一AZ側に健全なエンドポイントが再び存在しZone-Awareルーティングの条件を満たす状態になると、再び同一AZ優先に戻ります(ドキュメントに記載のフォールバック動作)。
補足: 閾値以下時の挙動
参考として、サービスBが2タスク(各AZに1つ)の状態で同じテストを実施した結果を示します。
{"az":"ap-northeast-1a"}
{"az":"ap-northeast-1a"}
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1c"}
{"az":"ap-northeast-1a"}
{"az":"ap-northeast-1c"}
...(14回実行)
| AZ | 回数 | 比率 |
|---|---|---|
| ap-northeast-1a | 8 | 57% |
| ap-northeast-1c | 6 | 43% |
AZ-aとAZ-cにほぼ均等に分散されており、少なくとも同一AZ優先の挙動にはなっていないことが確認できました。ドキュメントの記載どおり、エンドポイント数が閾値(2 × AZ数 = 4)未満の場合はZone-Awareルーティングの条件を満たさず、AZを考慮しない通常の負荷分散にフォールバックします。
注意事項
Fargate環境でのモニタリングについて補足します。
ドキュメントにはEnvoyの統計情報でZone-Awareルーティングの状態を確認する方法が記載されています。確認に使うメトリクスはlb_zone_routing_cross_zoneとlb_zone_cluster_too_smallです。ただしこの手順はSSM Session ManagerでEC2インスタンスに接続してDocker execする前提です。
Fargate環境ではService Connectエージェントコンテナに直接アクセスできないため、Envoy統計の確認はできませんでした。FargateでクロスAZ通信の傾向を確認する手段としては、VPC Flow Logs(az-idフィールド付き)の利用が選択肢になります。
まとめ
ECS Service ConnectのZone-Awareルーティングにより、マルチAZ構成のサービス間通信で同一AZ内のエンドポイントが優先されるようになりました。追加設定は不要で、条件を満たすService Connectサービスではデフォルトで有効になります。
今回の検証構成では、各AZにエンドポイントを均等配置した状態で100%同一AZにルーティングされることを確認しました。同一AZのエンドポイントが不在になった場合も、別AZの健全なエンドポイントへ自動的にフォールバックされました。
マルチAZでECS Service Connectを利用している環境では、クロスAZ通信を減らし、データ転送コストやレイテンシの削減が期待できます。







