Amazon ECS Express Mode で Websocket 対応してみた話
はじめに
大切に温めてきた?w App Runner の脅威となる存在が突如現れたということで早速試してみました。
コンソールをポチポチするのも良いのですが、Terraform で一瞬に構築できた方が皆さんにとってもすぐに体験し、横展開が可能だと思いますので公開しました。
背景
2025年11月、AWSから Amazon ECS Express Mode という新機能が発表されました。
従来のECSサービスでは、コンテナアプリケーションをデプロイするために、ECSクラスター、タスク定義、サービス、ALB、ターゲットグループ、セキュリティグループ、オートスケーリングなど、多くのリソースを個別に設定する必要がありました。
ECS Express Modeは、これらの複雑な設定を自動化し、コンテナイメージを指定するだけで本番環境レベルのアプリケーションをデプロイできる ようにする画期的な機能です。
今回、この新機能を実際にTerraformで構築し、WebSocket対応の動作確認まで実施しました。
概要
ECS Express Modeの主な特徴は以下の通りです。
最小限の設定で開始可能
- コンテナイメージ
- タスク実行ロール
- インフラストラクチャロール
この3つだけでデプロイを開始できます。
自動プロビジョニングされるリソース
- ECSクラスター/サービス/タスク定義
- Application Load Balancer(HTTPS対応)
- セキュリティグループ
- オートスケーリング
- CloudWatch Logs
- AWS提供ドメイン(
*.ecs.<region>.on.aws)
追加料金なし
ECS Express Mode自体に追加料金はなく、作成されるAWSリソース(Fargate、ALB、CloudWatch等)の通常料金のみが発生します。
内容
検証環境の構成
今回の検証では、Terraformを使用して以下の構成を構築しました。
| リソース | 設定値 |
|---|---|
| リージョン | us-east-1 |
| VPC CIDR | 10.0.0.0/16 |
| サブネット | パブリック × 3 AZ |
| コンテナイメージ | jmalloc/echo-server:latest |
| CPU/メモリ | 1024 / 2048 |
| スケーリング | 最小1 〜 最大10タスク |
Terraform AWS Provider v6.0以上が必要
ECS Express Modeを使用するには、Terraform AWS Providerのバージョン6.0以上が必要です。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
aws_ecs_express_gateway_service リソース
ECS Express Modeのサービスは aws_ecs_express_gateway_service リソースで定義します。
resource "aws_ecs_express_gateway_service" "main" {
service_name = var.service_name
primary_container {
image = var.container_image
container_port = var.container_port
}
execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
infrastructure_role_arn = aws_iam_role.ecs_infrastructure_role.arn
network_configuration {
subnets = toset([...])
}
cpu = tostring(var.cpu)
memory = tostring(var.memory)
scaling_target {
min_task_count = var.min_capacity
max_task_count = var.max_capacity
auto_scaling_metric = "AVERAGE_CPU"
auto_scaling_target_value = 60
}
health_check_path = var.health_check_path
}
必要なIAMロール
ECS Express Modeには2つのIAMロールが必要です。
Task Execution Role
コンテナイメージの取得やCloudWatch Logsへの書き込みに使用します。
resource "aws_iam_role" "ecs_task_execution_role" {
name = "${var.name_prefix}-task-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "ecs-tasks.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy" {
role = aws_iam_role.ecs_task_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
Infrastructure Role
ECS Express Modeがリソースを作成・管理するために使用します。
resource "aws_iam_role" "ecs_infrastructure_role" {
name = "${var.name_prefix}-infrastructure-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "AllowAccessInfrastructureForECSExpressServices"
Effect = "Allow"
Principal = { Service = "ecs.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy_attachment" "ecs_infrastructure_role_policy" {
role = aws_iam_role.ecs_infrastructure_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSInfrastructureRoleforExpressGatewayServices"
}
コンテナイメージの指定
primary_container.image にイメージURIを指定するだけで、外部リポジトリから自動的に取得されます。
| リポジトリ | 例 |
|---|---|
| Docker Hub |
nginx:latest, jmalloc/echo-server:latest
|
| Amazon ECR | 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:v1 |
| ECR Public | public.ecr.aws/nginx/nginx:latest |
| GHCR | ghcr.io/owner/image:tag |
エンドポイントの取得
ECS Express Modeが提供するドメイン名は、サービス名ベースではなく 内部IDベースの形式 になります。
ec-<internal-id>.ecs.<region>.on.aws
このドメイン名は ingress_paths[0].endpoint 属性から取得できます。
locals {
express_service_endpoint_raw = aws_ecs_express_gateway_service.main.ingress_paths[0].endpoint
express_service_domain = replace(local.express_service_endpoint_raw, "https://", "")
express_service_endpoint = "https://${local.express_service_domain}"
}
output "express_service_endpoint" {
value = local.express_service_endpoint
}
WebSocket対応
Application Load BalancerはネイティブでWebSocketをサポートしています。ECS Express Modeで作成されるALBも同様で、追加設定なしでWebSocketアプリケーションをデプロイできます。
今回の検証では jmalloc/echo-server を使用しました。このイメージはHTTPエコーサーバーとWebSocketテストUIを提供します。
- HTTPエンドポイント:
https://<domain>/ - WebSocketテストUI:
https://<domain>/.ws
検証で判明した注意点
セキュリティグループの自動管理
ECS Express Modeはセキュリティグループを自動的に作成・管理します。Terraformで管理する場合は lifecycle ブロックで変更を無視する設定が必要です。
lifecycle {
ignore_changes = [
network_configuration[0].security_groups
]
}
カスタムドメインの制約
ALBはホストヘッダーベースのルーティングを使用しており、ECS Express Modeが提供するドメイン名のみを受け付けます。単純なCNAMEレコードでカスタムドメインを設定しても404が返されます。
カスタムドメインを使用する場合は、CloudFront経由での設定が推奨されます。
実際の Websocket の様子
メッセージを送信すると同じメッセージを返信してくれるシンプルなシステムです。

GitHubリポジトリ
今回作成したTerraformコードはGitHubで公開しています。
終わりに
ECS Express Modeは、コンテナアプリケーションのデプロイを大幅に簡素化する強力な機能です。
メリット
- 最小限の設定で本番環境レベルのアプリケーションをデプロイ可能
- ALB、オートスケーリング、CloudWatch Logsなどが自動設定される
- Terraform(AWS Provider v6.0以上)で管理可能
- WebSocketもネイティブサポート
- 追加料金なし
考慮点
- Fargateのみサポート(EC2起動タイプは非対応)
- デプロイ戦略はカナリアデプロイメントのみ
- カスタムドメインの設定にはCloudFront等の追加構成が必要
プロトタイピングや開発環境はもちろん、シンプルな要件の本番環境にも適しています。高度なカスタマイズが必要な場合は、従来のECSサービスを検討してください。
一言
もしこの記事が気に入って頂けたらここでのいいね&フォローとX( @nix )でのフォローもお願い致します。
Discussion