本記事は『AWS CDK Advent Calendar 2025』の 13 日目の記事です。
はじめに
先日発表された Amazon ECS の新しい実行モードである Express Mode を、CDK で構築してみました。ECS Express Mode は、これまで自分で組み立てていた多くのリソースをいい感じに抽象化してくれる仕組みで、少ない設定でコンテナアプリを起動することができます。
今回はこれを CDK で、Hono で書いた API コンテナをデプロイしてみます。
実行環境
- Node.js:
v22.14.0 - aws-cdk:
2.1010.0 - aws-cdk-lib:
2.232.1 - Hono:
4.10.7
起動するコンテナを準備
ECS でコンテナを起動するために、コンテナレジストリ(ECR) とコンテナイメージ(Hono)を準備します。
コンテナレジストリを作成
コンテナレジストリの作成は、CDK で ECR を作成します。
const repository = new ecr.Repository(this, "ContainerRepository", {
repositoryName: `${id.toLowerCase()}-repository`,
});
コンテナイメージを作成
まずは、Hono を使ってシンプルな API を実装し、そのアプリをコンテナで動かすための Dockerfile を用意します。Dockerfile 自体は、Hono の公式ドキュメントに掲載されている Node.js 向けのサンプルをベースにしています。
FROM node:22-alpine AS base
FROM base AS builder
RUN apk add --no-cache gcompat
WORKDIR /app
COPY package*json tsconfig.json src ./
RUN npm ci && \
npm run build && \
npm prune --production
FROM base AS runner
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 hono
COPY --from=builder --chown=hono:nodejs /app/node_modules /app/node_modules
COPY --from=builder --chown=hono:nodejs /app/dist /app/dist
COPY --from=builder --chown=hono:nodejs /app/package.json /app/package.json
USER hono
EXPOSE 3000
CMD ["node", "/app/dist/index.js"]
Hono アプリ本体は src/index.ts に実装しておき、公式ドキュメントのサンプルをベースにしつつ、Node.js コンテナ上で 3000 ポートで起動できるようにしています。
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
serve({
fetch: app.fetch,
port: 3000
}, (info) => {
console.log(`Server is running on http://localhost:${info.port}`)
})
コンテナイメージのビルドと ECR へのプッシュ
Dockerfile と Hono アプリを用意したら、次はコンテナイメージをビルドし、先ほど作成した ECR リポジトリに push します。ここでは、ローカルでイメージをビルドしたあと、ECR のリポジトリ URI をタグとして付け直し、docker push でアップロードします。
# イメージをビルド(リポジトリ名は任意)
docker build -t cdkecsexpressmodesamplestack-repository . --platform linux/amd64
# ECR のリポジトリ URI をタグに付ける
docker tag cdkecsexpressmodesamplestack-repository:latest xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/cdkecsexpressmodesamplestack-repository:f6a167ce-019a-7000-85e3-98d88bdd9b71
# ECR にプッシュ
docker push xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/cdkecsexpressmodesamplestack-repository:f6a167ce-019a-7000-85e3-98d88bdd9b71
CDK で実装
ここからは、実際に CDK で ECS Express Mode のスタックをどのように定義しているかを見ていきます。
まず前提として、執筆時点(2025/12/9)では ECS Express Mode 向けの L2/L3 Construct は用意されておらず、CloudFormation リソース AWS::ECS::ExpressGatewayService に対応する L1 Construct のみが提供されています。
このセクションでは、この L1 Construct を使って Express Mode のサービスをどのように定義しているかを順に見ていきます。
IAM ロールを定義する
ECS Express Mode を作成するために必要なロールを作成します。タスク実行ロール、タスクロールについては、これまで ECS でコンテナを起動する際にも設定が必要だったロールです。それに加えて、インフラストラクチャロールが必要となります。インフラストラクチャロールには、AmazonECSInfrastructureRoleforExpressGatewayServices ポリシーをアタッチすることで、ECS サービスが、ALB や Auto Scaling の設定を管理することができるようになります。
const executionRole = new iam.Role(this, "TaskExecutionRole", {
assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy")],
});
const infrastructureRole = new iam.Role(this, "InfrastructureRole", {
assumedBy: new iam.ServicePrincipal("ecs.amazonaws.com"),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSInfrastructureRoleforExpressGatewayServices"),
],
});
// アプリケーションから AWS の各種サービス(S3, DynamoDB など)にアクセスする必要がある場合は、その権限を付与する
const taskRole = new iam.Role(this, "TaskRole", {
assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
});
taskRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonS3ReadOnlyAccess"));
ネットワーク設定の定義
サービスのネットワークとして利用するサブネット、セキュリティグループを明示的に指定することもできますし、指定しなかった場合は、デフォルト VPC に関連付けられたサブネットとセキュリティグループが利用されます。
CDK から明示的に指定したい場合は、VPC を定義し、サブネットやセキュリティグループを networkConfiguration 経由で渡します。
const vpc = new ec2.Vpc(this, "Vpc", {
maxAzs: 2,
natGateways: 1,
});
// 必要に応じてセキュリティグループを宣言
ExpressGatewayService を定義する
最後に、ECS Express Mode のサービス本体となる ExpressGatewayService を定義します。
ここでは、先ほど作成した ECR リポジトリのイメージ URI と、タスク実行ロール・インフラストラクチャロール・タスクロールなどを CfnExpressGatewayService に渡しています。
const expressGatewayService = new ecs.CfnExpressGatewayService(this, "ExpressGatewayService", {
executionRoleArn: executionRole.roleArn,
infrastructureRoleArn: infrastructureRole.roleArn,
primaryContainer: {
image: `${repository.repositoryUri}:${imageTag}`,
containerPort: 3000,
// ログの設定、環境変数の設定などがあればここに追加
// see: https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs.CfnExpressGatewayService.ExpressGatewayContainerProperty.html
},
// ここから下はオプショナルなプロパティ
// 省略すると default という名前のクラスターが作成される
cluster: cluster.ref,
cpu: "256",
memory: "512",
// 省略すると default VPC が利用される
// see: https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-expressgatewayservice-expressgatewayservicenetworkconfiguration.html
networkConfiguration: {
// securityGroups: ["securityGroupId"],
subnets: vpc.publicSubnets.map((subnet) => subnet.subnetId),
},
// 省略すると平均 CPU 仕様率 60% で最小1, 最大20 でスケーリングされる
// scalingTarget: {
// autoScalingMetric: "autoScalingMetric",
// autoScalingTargetValue: 123,
// maxTaskCount: 123,
// minTaskCount: 123,
// },
// serviceName: "serviceName",
taskRoleArn: taskRole.roleArn, // アプリケーションで必要なポリシーがあればタスクロールで設定する
});
必須となるのは executionRoleArn、infrastructureRoleArn、primaryContainer などで、それ以外の cluster や networkConfiguration、scalingTarget は省略すると Express Mode 側のデフォルト値が使われます。
デプロイ
CDK で実装した ECS Express Mode のサービスをデプロイします。
npx cdk deploy -c imageTag=xxxxx
デプロイすると ECS Express Mode でリソースが作成されていることを確認できました。
作成されたアプリケーション URL にアクセスすることで、コンテナが動作していることを確認できました。
また、ECS Express Mode が作成した各リソースは、マネコンからも参照することができました。ドリフト前提ですが、作成されたリソースをマネコンやCLIで設定を変更することができそうでした。変更の際は、IaC との整合性に注意が必要そうですね。
networkConfiguration にプライベートサブネットを指定した場合
networkConfiguration でプライベートサブネットのみを指定した場合は、Express Mode が内部的に作成する ALB もプライベートサブネットに配置され、Internal Load Balancerとして動作するようでした。
アプリケーションを更新してデプロイ
デプロイ済みのサービスを更新する際は、デプロイ戦略: Canary を使ったカナリアデプロイとなります。主な設定は以下のとおりです。
- Canary パーセント:
5%- 新しいコンテナに何パーセントトラフィックを流すか
- Canary ベイク時間:
3分- Canary パーセントを新しいコンテナに流している状態で、何分間様子をみるか
Canary デプロイの進捗状況は、サービスの デプロイ タブで確認することができ、今回アプリケーションの更新でデプロイした際は、おおよそ10分程度かかりました。
さいごに
今回は、ECS Express Mode を使って Hono のコンテナを CDK からデプロイし、最小限の設定で API を公開できることを確認しました。コンテナイメージといくつかの必須プロパティさえ用意すれば、ALB やスケーリング周りまでよしなに面倒を見てくれるのは、いいなぁと思いました。
一方で、執筆時点では CDK から利用できるのは L1 Construct のみで、指定できる項目もそれなりに絞られているため、細かい制御が必要な場合は、現状の L2 Construct や L3 Construct を使う方が相性が良さそうに感じました。
今後のアップデートを追いつつ、よりよい使い方を探っていこうと思います。
参考





