タダです.
ECS サービスの前段に置く ALB,証明書,セキュリティグループを ECS 側で払い出して管理してくれる Amazon ECS Gateway が発表されました.東京リージョンでゲートウェイの作成からサービスの接続を試したのでメモにまとめます.
ECS Gateway の概要
ECS Gateway は,ECS サービスへのイングレスの設定をゲートウェイに一度書き,それを複数のサービスで共有するアカウントレベルのリソースです.サブネット,セキュリティグループ,WAF の Web ACL などを決めたゲートウェイを作ってクラスターに関連付け,アプリケーションチームはサービスにゲートウェイコネクタというパラメーターを足すだけで HTTPS の URL を得られます.
ECS が作るリソースは,ゲートウェイ単位で共有するものとサービス単位のものに分かれます.1 つの ALB をホストベースルーティングで最大 25 サービスが共有し,いっぱいになると次の ALB が追加されます.
| 種別 | リソース |
|---|---|
| 共有 | ALB,リスナー,ALB のセキュリティグループ |
| サービス単位 | ACM 証明書,DNS ホスト名,リスナールール,ターゲットグループ,サービスのセキュリティグループ |
事前準備
AWS CLI 2.38.5 にして検証を行いました.
ECS が ALB や証明書を作るときに引き受けるインフラストラクチャロールも必要です.ドキュメントの手順どおり,ecs.amazonaws.com を信頼するロールに AWS 管理ポリシーの AmazonECSInfrastructureRoleForGatewayHostSharedALB を付けました.
aws iam create-role \ --role-name ecsInfrastructureRoleForGateways \ --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ecs.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' aws iam attach-role-policy \ --role-name ecsInfrastructureRoleForGateways \ --policy-arn arn:aws:iam::aws:policy/AmazonECSInfrastructureRoleForGatewayHostSharedALB
iam:PassRole が要るのはゲートウェイを作る側だけで,アプリケーションチームには ALB の作成権限も要りません.
ECS Gateway を試す
検証用にクラスター ecs-gateway-demo を作り,デフォルト VPC の 3 つのパブリックサブネット(1a,1c,1d)を使いました.
ゲートウェイの作成
create-gateway で名前,インフラストラクチャロール,ALB の設定を渡してゲートウェイを作り,associate-gateway-with-cluster でクラスターに関連付けます.
aws ecs create-gateway \ --name demo-public-gateway \ --infrastructure-role-arn arn:aws:iam::123456789012:role/ecsInfrastructureRoleForGateways \ --configuration '{ "hostSharedAlb": { "networkConfiguration": { "subnets": ["subnet-6e379519", "subnet-309e5969", "subnet-7ad68052"], "ipAddressType": "ipv4" }, "scheme": "internet-facing" } }' aws ecs associate-gateway-with-cluster \ --gateway demo-public-gateway \ --cluster ecs-gateway-demo
describe-gateway を見ると,deletionProtection が既定で true,maxAlbCount が 10,currentAlbCount が 0 でした.クラスターに関連付けても ALB は作られず,サービスが接続するまで何も作られないというドキュメントどおりの動きでした.
サービスの接続
タスク定義は nginx のコンテナのポートに web という名前を付けただけで,Fargate のサービスを --gateway-connectors 付きで作りました.
aws ecs create-service \ --cluster ecs-gateway-demo \ --service-name web \ --task-definition ecs-gateway-demo-web \ --desired-count 1 --launch-type FARGATE \ --network-configuration 'awsvpcConfiguration={subnets=[subnet-6e379519,subnet-309e5969,subnet-7ad68052],assignPublicIp=ENABLED}' \ --gateway-connectors '[{"portName": "web"}]'
接続状態は describe-service-revisions の ecsManagedResources.gatewayConnections で確認します.
[ { "portName": "web", "serviceUrl": "https://we-ec25e2c719fb482db56acdf726266ac7.ecs.ap-northeast-1.on.aws", "accessType": "PUBLIC", "status": "ACTIVE", "resources": { "hostSharedAlb": [ { "acmCertificateArn": "...", "loadBalancerArn": "...", "listenerArn": "...", "listenerRuleArn": "...", "targetGroupArn": "..." } ] } } ]
ECS が作ったリソース
サービスの接続で ECS が自分のアカウントに作ったリソースを,ALB のルーティング,タスクのセキュリティグループ,タグの順に確認しました.
まず,ALB がどのようにリクエストを振り分けているかです.ALB の DNS 名を取得し,払い出された URL ではなく ALB の DNS 名に直接アクセスしてみました.
ALB_DNS=$(aws elbv2 describe-load-balancers \ --query 'LoadBalancers[?starts_with(LoadBalancerName,`ecs-gateway`)].DNSName' --output text) curl -sk -o /dev/null -w '%{http_code}\n' https://$ALB_DNS/
結果は 404 でした.リスナーは HTTPS の 443 番だけで,ホストヘッダーが払い出されたホスト名と一致したリクエストだけをサービスのターゲットグループへ転送し,それ以外は 404 を返すルールになっています.複数のサービスで 1 つの ALB を共有できるのは,このホスト名ごとのルールがあるためです.
次に,タスクにどのセキュリティグループが付いているかです.タスクの ENI を辿って確認しました.
TASK=$(aws ecs list-tasks --cluster ecs-gateway-demo --service-name web --query 'taskArns[0]' --output text) ENI=$(aws ecs describe-tasks --cluster ecs-gateway-demo --tasks $TASK \ --query 'tasks[0].attachments[0].details[?name==`networkInterfaceId`].value' --output text) aws ec2 describe-network-interfaces --network-interface-ids $ENI --query 'NetworkInterfaces[0].Groups'
サービス作成時にセキュリティグループを指定しなかったので VPC の default が付き,そこに ECS が作ったサービス用のグループが追加されていました.
[ { "GroupId": "sg-42980d27", "GroupName": "default" }, { "GroupId": "sg-0e745e46104981b95", "GroupName": "ecs-gateway-service-sg-78453db0...-1" } ]
サービス用のグループは ALB からの 80 番だけを許可しますが,自分で指定したグループのルールもそのまま効きます.ALB からしか届かない状態にするには,自前のセキュリティグループ側も絞っておく必要があります.
最後にタグです.ECS が作った ALB,ターゲットグループ,セキュリティグループ,証明書には AmazonECSManaged=true と aws:ecs:gateway-arn(値はゲートウェイの ARN)のタグが付いていて,ELB や EC2 のコンソールでこのタグを使って絞り込めました.
2 つ目のサービスの接続
同じタスク定義で 2 つ目のサービス web2 を作ると,50 秒ほどで ACTIVE になりました.describe-gateway-resources を見ると同じ ALB に 2 つのルートが載っていて,currentAlbCount は 1 のままです.1 つ目との差は ALB を作るかどうかで,2 つ目以降のサービスはすぐに URL を得られるのが良いです.
アクセスタイプのフィルター
ゲートウェイコネクタは接続先のゲートウェイを名前で指定せず,アクセスタイプ,VPC,アベイラビリティーゾーンの条件を満たすゲートウェイすべてに接続します.PUBLIC のゲートウェイしかないクラスターに PRIVATE で絞ったサービスを作ると,API 呼び出しの時点で拒否されました.
aws: [ERROR]: An error occurred (InvalidParameterException) when calling the CreateService operation: gatewayConnector with portName 'web' has filters.accessTypes [PRIVATE] but none of the gateways associated with cluster arn:aws:ecs:ap-northeast-1:123456789012:cluster/ecs-gateway-demo match; associated gateway access types: [PUBLIC]
逆にフィルターを省略すると両方が対象になるので,あとからインターネット向けのゲートウェイがクラスターに関連付けられると,次のデプロイでサービスが公開されます.公開してはいけないサービスは PRIVATE で絞っておくべきです.
料金と対応リージョン
ECS Gateway 自体は追加料金なしで,ALB やデータ転送など作られたリソースの分だけ課金されます.商用リージョンすべてで利用できます.
まとめ
Amazon ECS Gateway でゲートウェイの作成からサービスの接続を東京リージョンで試しました.