はじめに
2026年10月20日に、Amazon ECS Gateway が発表されました。
ECS サービスを外部に公開するための入口(イングレス)を、ECS が ALB ごと作って管理してくれる機能です。
複数のサービスが1台の ALB を共有し、ホスト名でサービスを振り分けます。
ドキュメントには「1台の ALB に載るのは最大25サービス」と書かれています。
では26個目を入れると何が起きるのか。
それを確かめるため、東京リージョンでサービスを27個作り、ALB が増える瞬間と減る瞬間を計測しました。
先に結果を書くと、26個目は作成から約9秒後に失敗していました。
エラー文言には上限の話が一切出てきません。
上限を引き上げても自動では再試行されず、手動で再デプロイして初めて2台目の ALB ができました。
ECS Gateway の仕組み
ECS Gateway では、プラットフォームチームとアプリケーションチームで作業が分かれます。
- プラットフォームチームがゲートウェイを作り、サブネット・WAF・ALB の上限数などを1か所で決める
- プラットフォームチームが、ゲートウェイを ECS クラスターに関連付ける
- アプリケーションチームは、サービスに gateway connector(公開するコンテナポート名)を1つ足すだけで HTTPS の URL を受け取る
ECS がアカウント内に作るリソースは、共有のものとサービス単位のものに分かれます。
| 種類 | 作られるリソース |
|---|---|
| ゲートウェイ単位(共有) | ALB、HTTPS リスナー、ALB 用セキュリティグループ |
| サービス単位 | ACM 証明書、ホスト名、リスナールール、ターゲットグループ、サービス用セキュリティグループ |
ゲートウェイを作っただけでは何も作られません。
最初のサービスが接続した時点で ALB が作られ、25サービスで満杯になると次の ALB が追加されます。
ALB の台数の上限は、ゲートウェイの maxAlbCount という設定で決まります。
既定値は10です。
検証環境
検証は次の条件で行いました。
- リージョンは ap-northeast-1、VPC はデフォルト VPC の3つのサブネット(1a / 1c / 1d)
- コンテナは busybox の httpd で、自分のサービス名だけを返す。Fargate の ARM64、0.25 vCPU / 0.5 GB
- 境界を観察しやすくするため、ゲートウェイは maxAlbCount=1 で作成
- AWS CLI は 2.38.4(ECS Gateway のコマンドは 2.38.3 から使えます)
ゲートウェイは次のコマンドで作りました。
aws ecs create-gateway \
--name gwlab-gateway \
--infrastructure-role-arn arn:aws:iam::<アカウントID>:role/gwlab-ecs-gateway-infra-role \
--configuration '{
"hostSharedAlb": {
"scheme": "internet-facing",
"maxAlbCount": 1,
"networkConfiguration": {
"subnets": ["subnet-aaa", "subnet-bbb", "subnet-ccc"],
"ipAddressType": "ipv4"
}
}
}'
インフラストラクチャロールには、AWS 管理ポリシーの AmazonECSInfrastructureRoleForGatewayHostSharedALB を付けています。
サービス側は、create-service に次の1行を足すだけです。
--gateway-connectors '[{"portName":"web"}]'
サービスを27個すべて動かすと、タスク代とパブリック IPv4 アドレス代がかさみます。
そこで最初に、タスク数0のサービスでもゲートウェイの接続(ルート)が作られるかを確かめました。
結果として作られたので、枠を埋めるだけのサービスはタスク0個にしています。
実際にタスクを動かして通信を確認したのは、svc-01・svc-26・svc-27 の3つです。
1個目は200秒、2個目は51秒
最初のサービス(svc-01)は、作成から接続が ACTIVE になるまで約200秒かかりました。
この中に ALB の新規作成が含まれています。
2個目(svc-02)は既存の ALB を使い回すため、約51秒で ACTIVE になりました。
発行された URL は次の形式です。
https://gw-<32桁の英数字>.ecs.ap-northeast-1.on.aws
ACTIVE になってから curl でサービス名が返るまでには、さらに30〜50秒かかりました。DNS の反映待ちと考えられますが、原因までは確認していません。
経過時間は10秒ごとに状態を確認して計っているため、最大10秒ほどの誤差があります。
25個で1台目が満杯になる
svc-03 から svc-25 までの23個をまとめて作成すると、どれも51〜68秒で ACTIVE になりました。
25個すべてが1台目の ALB に載っています。
このとき、1台目の ALB のリスナーには証明書が25枚付いていました。
既定の証明書1枚と追加の証明書24枚です。ECS はサービスごとに ACM 証明書を作ってリスナーに付けるので、サービス数と証明書の枚数が一致します。
このアカウントの「ALB 1台あたりの証明書数」のクォータも25でした。
25サービスという上限と数字は一致しますが、これが上限の理由なのかはドキュメントに書かれておらず、確認できていません。
リリース直後あるあるですね
26個目は作成直後に失敗した
maxAlbCount=1 のまま26個目(svc-26)を作成しました。
create-service コマンド自体は成功します。
ところが、作成から13秒後に状態を確認した時点で、デプロイは ROLLBACK_FAILED、接続は CREATE_FAILED になっていました。
サービスイベントの時刻を見ると、失敗が記録されたのは作成の約9秒後です。
サービスのイベントに出たのは次のメッセージです。
Service deployment rolled back because PROVISIONING lifecycle hook(s) failed. InternalServerError: Could not complete the operation..
maxAlbCount にも ALB の上限にも触れていません。
InternalServerError と書かれているので、AWS 側の一時的な障害と受け取ってもおかしくない文面です。
ロールバックの理由も「No rollback candidate was found to run the rollback.」(戻す先のデプロイがない)で、新規サービスなので戻しようがない、という内容でした。
この後、上限を2に上げて再デプロイすると成功しました。
そのため原因は上限と考えるのが自然ですが、エラー文言だけでは特定できません。
サービスが増えてきたら、ゲートウェイの currentAlbCount(今ある ALB の台数)と maxAlbCount を見比べておく必要があります。
aws ecs describe-gateway --gateway gwlab-gateway \
--query 'gateway.configuration.hostSharedAlb.[maxAlbCount,currentAlbCount]'
上限を上げても自動では再試行されない
maxAlbCount を1から2に上げました。
update-gateway は12秒で UPDATE_COMPLETE になります。
そのまま5分間、30秒ごとに svc-26 の状態を確認しましたが、ずっと ROLLBACK_FAILED / CREATE_FAILED のままでした。
ドキュメントの「上限を上げても、失敗したデプロイは再試行されない」という記載どおりです。
update-service --force-new-deployment で再デプロイすると、198秒後に接続が ACTIVE になりました。
2台目の ALB の新規作成込みで、1台目の約200秒とほぼ同じです。
curl でもサービス名が返りました。
再デプロイの直後に注意点が1つあります。update-service を実行した3秒後に list-service-deployments を見ると、新しいデプロイはまだ一覧に現れておらず、前回の失敗した結果が最新として返ってきました。
スクリプトで完了を待つときは、再デプロイ前のデプロイ ARN を控えておき、それと違うデプロイが現れるまで待つ必要があります。
間引いても ALB はまとまらない
この時点で、1台目に25個、2台目に1個のサービスが載っています。
ここから1台目の svc-02〜11 の10個を削除すると、45秒でルートが26本から16本に減りました。
1台目は15個、2台目は1個になりましたが、2台目のサービスが1台目に移ることはありませんでした。
ALB は2台のまま残ります。
ドキュメントにも「ECS は ALB を統合しない」とあり、そのとおりの動きです。
次に、新しいサービス svc-27 を作成しました。
空きがある ALB は2台ですが、svc-27 が入ったのは15個載っている1台目でした。
1個しか載っていない2台目は選ばれていません。
ただし観測は1回だけなので、「埋まっている側を優先する」という規則だとまでは言えません。
最後に、2台目の唯一のサービス svc-26 を削除しました。
2台目の ALB が消えるまでには598秒、約10分かかりました。
その間も、ALB の時間料金はかかり続けます。
update-gateway で省略した maxAlbCount は10に戻る
ドキュメントによると、update-gateway は指定した項目だけを変えるのではなく、変更できる設定をまとめて置き換えます。
指定しなかった項目は既定値に戻るか、設定が外れます。
このうち maxAlbCount について、実際に確かめました。
maxAlbCount=2 の状態で、httpDesync(HTTP リクエストの不正な形式への対処モード)だけを指定して update-gateway を実行します。
aws ecs update-gateway --gateway gwlab-gateway \
--infrastructure-role-arn arn:aws:iam::<アカウントID>:role/gwlab-ecs-gateway-infra-role \
--configuration '{"hostSharedAlb": {"httpDesync": "defensive"}}' \
--deletion-protection
12秒で UPDATE_COMPLETE になり、エラーも警告も出ません。
describe-gateway で確認すると、maxAlbCount は2から既定値の10に戻っていました。
| 項目 | 実行前 | 実行後 |
|---|---|---|
| maxAlbCount | 2 | 10 |
| 指定した httpDesync | defensive | defensive |
サブネットや scheme(internet-facing か internal か)は作成後に変えられない項目で、update-gateway には渡しません。
今回も指定していませんが、実行後もそのまま残っていました。
WAF の Web ACL、アクセスログ、自前のセキュリティグループ指定も、ドキュメントでは省略すると外れるとされています。
今回は設定していなかったので、この点は確かめていません。
update-gateway を実行するときは、先に describe-gateway で現在の設定を取得し、変えない項目も含めて渡すのが安全です。
今回の検証スクリプトでは、describe-gateway の結果から maxAlbCount・httpDesync・webACLArn・accessLogs・testTrafficRules を jq で引き継いでいます。
自前のセキュリティグループ(inboundSecurityGroup)だけは渡していません。
ECS に受信ルールを管理させていたためで、ここに値を渡すと ECS は受信ルールの管理をやめます。
ECS が作ったリソースの中身
片付ける前に、ECS が作ったリソースの設定を読み取りで保存しました。
主な設定値は次のとおりです。
- ALB はデフォルト VPC の3つの AZ にまたがり、削除保護が有効
- リスナーは HTTPS 443 の1つだけで、TLS ポリシーは ELBSecurityPolicy-TLS13-1-2-Ext1-PQ-2025-09
- どのルールにも一致しないリクエストには、既定のアクションで 404 Not Found を返す
- サービスごとのルールは、ホスト名(host-header)が一致したらターゲットグループへ転送する
セキュリティグループとターゲットグループは次のようになっていました。
- ALB 用セキュリティグループは、443 と 80 をすべての IP アドレスから受け付ける。ただし 80 番のリスナーはない
- サービス用セキュリティグループは、ALB 用セキュリティグループからのコンテナポート(8080)だけを受け付ける
- ターゲットグループのヘルスチェックは、パス /、30秒間隔、5秒でタイムアウト、200 を期待する設定
タスク用に作ったセキュリティグループには受信ルールを入れていませんでしたが、ALB からの通信は届きました。
サービスのリビジョンには、ECS が管理するサービス用セキュリティグループが1つ記録されていました。
これがタスクに付いて通信を許可していると考えられますが、タスクのネットワークインターフェースまでは確認していません。
証明書は、Amazon が発行した ECDSA 鍵(EC-prime256v1)のものでした。
この鍵の種類は、片付けの確認で見落としやすい点につながります。
片付けで気をつけること
片付けは、削除保護の解除、全サービスの削除、ゲートウェイとクラスターの関連付けの解除、ゲートウェイの削除の順で行いました。
全サービスを削除してからルートがすべて消えるまでに509秒かかっています。
途中で気づいた点は3つあります。
- 削除保護は既定で有効で、ECS が作った ALB にも引き継がれるため、update-gateway --no-deletion-protection で外す必要がある
- aws acm list-certificates は、鍵の種類を指定しないと ECDSA の証明書を一覧に出さない。残っていないかの確認には --includes keyTypes=EC_prime256v1,RSA_2048 を付ける
- タグで検索する API(resourcegroupstaggingapi)には、削除したサービスやセキュリティグループのルールがしばらく残って見える。個別に調べると実体はなかった
また、同じアカウントには ECS Express Mode が作った ALB があり、その ALB が使うターゲットグループと証明書にも、同じ AmazonECSManaged=true のタグが付いていました。片付けの対象は、aws:ecs:gateway-arn タグの値が自分のゲートウェイの ARN と一致するものに絞る必要があります。
費用の比較
東京リージョンの ALB の時間料金は、1台あたり 0.0243 USD です(Pricing API で取得)。730時間で計算すると、ALB の時間料金だけで次の差になります。
| 構成 | ALB の台数 | 月額(ALB の時間料金のみ) |
|---|---|---|
| サービスごとに ALB を立てる | 26台 | 約461 USD |
| ECS Gateway で共有する | 2台 | 約35 USD |
ここには LCU(処理量に応じた料金)と、ALB のパブリック IPv4 アドレス料金(1個あたり 0.005 USD/時)は含めていません。
internet-facing の ALB は AZ ごとに IP アドレスを持つので、台数が減ればこちらも減ります。
今回の検証では、ALB が最大2台、タスクが最大3つ動きました。
単価から計算すると、1時間あたりの費用は多いときでも約0.13 USD です。
検証してわかったこと
冒頭の「26個目を入れると何が起きるのか」への答えは、「上限の話が出てこないエラーで失敗し、上限を上げても自動では直らない」でした。
ALB の共有そのものは説明どおりに動き、サービスが26個あっても ALB は2台で済みました。気をつけたいのは、ALB が足りなくなったことに気づきにくい点です。
新しいサービスの接続が InternalServerError で失敗したら、まず currentAlbCount が maxAlbCount に達していないかを確かめ、上限はサービスが増える前に引き上げておくのが安全です。
最後まで読んでいただきありがとうございました。

