1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Amazon ECS Gateway で1台の ALB に26個目のサービスを入れてみた

1
Posted at

はじめに

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]'

flow-ja.jpg

上限を上げても自動では再試行されない

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)のものでした。
この鍵の種類は、片付けの確認で見落としやすい点につながります。

architecture-ja.jpg

片付けで気をつけること

片付けは、削除保護の解除、全サービスの削除、ゲートウェイとクラスターの関連付けの解除、ゲートウェイの削除の順で行いました。
全サービスを削除してからルートがすべて消えるまでに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 に達していないかを確かめ、上限はサービスが増える前に引き上げておくのが安全です。

最後まで読んでいただきありがとうございました。

参考

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?