🏰

Amazon ECS Express Mode で Websocket 対応してみた話

に公開

はじめに

大切に温めてきた?w App Runner の脅威となる存在が突如現れたということで早速試してみました。
コンソールをポチポチするのも良いのですが、Terraform で一瞬に構築できた方が皆さんにとってもすぐに体験し、横展開が可能だと思いますので公開しました。

背景

2025年11月、AWSから Amazon ECS Express Mode という新機能が発表されました。

https://aws.amazon.com/jp/blogs/aws/build-production-ready-applications-without-infrastructure-complexity-using-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 リソース

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/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で公開しています。

https://github.com/nix-tkobayashi/terraform-aws-ecs-express-mode

終わりに

ECS Express Modeは、コンテナアプリケーションのデプロイを大幅に簡素化する強力な機能です。

メリット

  • 最小限の設定で本番環境レベルのアプリケーションをデプロイ可能
  • ALB、オートスケーリング、CloudWatch Logsなどが自動設定される
  • Terraform(AWS Provider v6.0以上)で管理可能
  • WebSocketもネイティブサポート
  • 追加料金なし

考慮点

  • Fargateのみサポート(EC2起動タイプは非対応)
  • デプロイ戦略はカナリアデプロイメントのみ
  • カスタムドメインの設定にはCloudFront等の追加構成が必要

プロトタイピングや開発環境はもちろん、シンプルな要件の本番環境にも適しています。高度なカスタマイズが必要な場合は、従来のECSサービスを検討してください。

一言

もしこの記事が気に入って頂けたらここでのいいね&フォローとX( @nix )でのフォローもお願い致します。

Discussion