ECS Express Modeで未対応のARM64 / FARGATE_SPOT / ECS Execを利用してみた
ECS Express Modeは最小限の設定で迅速に環境を構築できる便利な機能ですが、ARM64(Graviton)やFargate Spot、ECS Execなどの機能を、作成時に設定して利用することができません。
今回、ECS Express Modeを採用した開発環境で、コスト改善やデバッグ効率を向上させるため、これらの機能の有効化を試みる機会がありましたので、その手順を紹介します。
課題:Express Modeの制約と回避策
ECS Express Mode(AWS::ECS::ExpressGatewayService)は、タスク定義・サービス・ALB・Auto Scalingなどの低レベルリソースを内部で自動生成・管理する高レベルコンポーネントです。そのため、通常のCloudFormationで指定できる runtimePlatform や capacityProviderStrategy といったプロパティが、Express Modeのテンプレートには存在しません。
アーキテクチャは x86_64 に固定、update-express-gateway-service APIを通じても変更できません。
この制約を回避するため、以下の2段階デプロイを実施しました。
- ダミーデプロイ: 本番と同一ポート・パスを持つx86ダミーイメージでスタックを作成。
- アーキテクチャ転換:自動生成されたタスク定義をCLIでARM64に編集し、
update-serviceで適用。
手順1:CloudFormationによる初期スタック作成
まずはx86のダミーイメージを使用して、Express Modeの管理下にあるリソースを正常にデプロイしました。
検証コード(CloudFormationテンプレート)
- ECS Execを利用するため、SSM用のIAMは初期テンプレートの設置時点で設定しました。
初期テンプレート全文
AWSTemplateFormatVersion: '2010-09-09'
Description: 'ECS Express Mode - 初回設置用(x86ダミー)'
Parameters:
ServiceName:
Type: String
Default: 'my-app-dev'
InitialStackName:
Type: String
Default: 'ecs-express-initial'
Resources:
ECSLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub '/aws/ecs/default/${ServiceName}-service'
RetentionInDays: 14
TaskRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${ServiceName}-task-role'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ECSExecPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ssmmessages:CreateControlChannel
- ssmmessages:CreateDataChannel
- ssmmessages:OpenControlChannel
- ssmmessages:OpenDataChannel
Resource: '*'
ExpressModeService:
Type: AWS::ECS::ExpressGatewayService
DependsOn: ECSLogGroup
Properties:
ServiceName: !Sub '${ServiceName}-service'
Cluster: 'default'
ExecutionRoleArn:
Fn::ImportValue: !Sub '${InitialStackName}-TaskExecutionRoleArn'
InfrastructureRoleArn:
Fn::ImportValue: !Sub '${InitialStackName}-InfrastructureRoleArn'
TaskRoleArn: !GetAtt TaskRole.Arn
PrimaryContainer:
Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/my-app:dummy'
ContainerPort: 8000
AwsLogsConfiguration:
LogGroup: !Sub '/aws/ecs/default/${ServiceName}-service'
LogStreamPrefix: 'ecs'
NetworkConfiguration:
Subnets:
Fn::Split:
- ','
- Fn::ImportValue: !Sub '${InitialStackName}-PrivateSubnets'
Cpu: '256'
Memory: '512'
HealthCheckPath: '/health'
ScalingTarget:
MinTaskCount: 1
MaxTaskCount: 1
AutoScalingMetric: 'AVERAGE_CPU'
AutoScalingTargetValue: 70
Outputs:
ServiceEndpoint:
Value: !GetAtt ExpressModeService.Endpoint
このテンプレートをデプロイし、タスク定義の runtimePlatform がデフォルトの x86_64 で起動していることを確認しました。
手順2:タスク定義のARM64化と適用
Express Modeが自動生成したタスク定義リビジョンを取得し、cpuArchitecture を ARM64 に、イメージを本番用ARMイメージに置換して登録しました。
SERVICE_NAME="my-app-dev-service"
TD_FAMILY="default-${SERVICE_NAME}"
REGION="ap-northeast-1"
ARM_IMAGE="123456789012.dkr.ecr.$REGION.amazonaws.com/my-app:latest"
# 1. 現在のタスク定義を取得
LATEST=$(aws ecs list-task-definitions --family-prefix $TD_FAMILY --sort DESC \
--region $REGION --query 'taskDefinitionArns[0]' --output text)
aws ecs describe-task-definition --task-definition $LATEST \
--region $REGION --query 'taskDefinition' > /tmp/td.json
# 2. ARM64 + 本番イメージに変更(メタデータの削除を含む)
jq --arg img "$ARM_IMAGE" '
.runtimePlatform.cpuArchitecture = "ARM64" |
.containerDefinitions[0].image = $img |
.cpu = "512" | .memory = "2048" |
del(.taskDefinitionArn, .revision, .status, .requiresAttributes,
.compatibilities, .registeredAt, .registeredBy)
' /tmp/td.json > /tmp/td_arm.json
# 3. 新リビジョンを登録
aws ecs register-task-definition \
--cli-input-json file:///tmp/td_arm.json \
--region $REGION
# 4. サービスを更新
NEW_REV=$(aws ecs list-task-definitions --family-prefix $TD_FAMILY --sort DESC \
--region $REGION --query 'taskDefinitionArns[0]' --output text)
aws ecs update-service \
--cluster default \
--service $SERVICE_NAME \
--task-definition $NEW_REV \
--force-new-deployment \
--region $REGION
update-stack 実行時の挙動確認
CLIを用いてARM64へ切り替えた後、CloudFormationの update-stack を実行し、テンプレート内のイメージ設定を変更しました。
update-stackの実行により、新しいタスク定義リビジョンが生成されました。- 新しいリビジョンにおいても、
runtimePlatform内のcpuArchitectureがARM64として維持されていることを確認しました。 - テンプレートで指定したイメージへの更新が、新リビジョンに反映されていることを確認しました。
Express Modeのスタック更新時、現在稼働中のタスク定義からアーキテクチャ設定を引き継ぐ挙動を確認できました。
手順3:FARGATE_SPOTとECS Execの有効化
ARM64への転換後、さらにコスト最適化とデバッグ機能を有効化しました。
これらは aws ecs update-service で適用しました。
aws ecs update-service \
--cluster default \
--service my-app-dev-service \
--capacity-provider-strategy capacityProvider=FARGATE_SPOT,base=0,weight=1 \
--enable-execute-command \
--propagate-tags TASK_DEFINITION \
--force-new-deployment \
--region ap-northeast-1
確認
設定反映後、ECS Execでコンテナ内にログインし、アーキテクチャを確認しました。
# コンテナ内で実行
$ uname -m
aarch64
aarch64 と表示され、ARM64(Graviton)で動作していることを確認しました。また、マネジメントコンソール上のサービス設定において、キャパシティプロバイダー戦略に FARGATE_SPOT が適用されていることを確認しました。
| 機能 | デフォルト | 拡張後の設定 | 方法 |
|---|---|---|---|
| Arch | x86_64 | ARM64 (Graviton) | タスク定義の再登録 |
| 購入モデル | FARGATE | FARGATE_SPOT | update-service (CLI) |
| デバッグ | 無効 | ECS Exec 有効 | IAM付与 + update-service |
まとめ
ECS Express Modeにおいても、2段階のデプロイプロセスを経ることでARM64、Fargate Spot、ECS Execを利用することができました。
ただし現時点で公式ドキュメントに明記されていない手順となるため、今後のアップデートにより挙動が変化したり、IaC外の変更による干渉や、ドリフトなどの問題が生じる可能性があります。開発、検証環境など自己責任で利用できる範囲に留めた利用をおすすめします。






