1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CDK で ECS Express Mode を使ってコンテナをデプロイしてみた

Posted at

本記事は『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 向けのサンプルをベースにしています。

Dockerfile
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 ポートで起動できるようにしています。

src/index.ts
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, // アプリケーションで必要なポリシーがあればタスクロールで設定する
});

必須となるのは executionRoleArninfrastructureRoleArnprimaryContainer などで、それ以外の clusternetworkConfigurationscalingTarget は省略すると Express Mode 側のデフォルト値が使われます。

デプロイ

CDK で実装した ECS Express Mode のサービスをデプロイします。

npx cdk deploy -c imageTag=xxxxx

デプロイすると ECS Express Mode でリソースが作成されていることを確認できました。

image.png

image.png

作成されたアプリケーション URL にアクセスすることで、コンテナが動作していることを確認できました。
また、ECS Express Mode が作成した各リソースは、マネコンからも参照することができました。ドリフト前提ですが、作成されたリソースをマネコンやCLIで設定を変更することができそうでした。変更の際は、IaC との整合性に注意が必要そうですね。

networkConfiguration にプライベートサブネットを指定した場合

networkConfiguration でプライベートサブネットのみを指定した場合は、Express Mode が内部的に作成する ALB もプライベートサブネットに配置され、Internal Load Balancerとして動作するようでした。

image.png

image.png

アプリケーションを更新してデプロイ

デプロイ済みのサービスを更新する際は、デプロイ戦略: Canary を使ったカナリアデプロイとなります。主な設定は以下のとおりです。

  • Canary パーセント: 5%
    • 新しいコンテナに何パーセントトラフィックを流すか
  • Canary ベイク時間: 3分
    • Canary パーセントを新しいコンテナに流している状態で、何分間様子をみるか

image.png

image.png

Canary デプロイの進捗状況は、サービスの デプロイ タブで確認することができ、今回アプリケーションの更新でデプロイした際は、おおよそ10分程度かかりました。

さいごに

今回は、ECS Express Mode を使って Hono のコンテナを CDK からデプロイし、最小限の設定で API を公開できることを確認しました。コンテナイメージといくつかの必須プロパティさえ用意すれば、ALB やスケーリング周りまでよしなに面倒を見てくれるのは、いいなぁと思いました。
一方で、執筆時点では CDK から利用できるのは L1 Construct のみで、指定できる項目もそれなりに絞られているため、細かい制御が必要な場合は、現状の L2 ConstructL3 Construct を使う方が相性が良さそうに感じました。
今後のアップデートを追いつつ、よりよい使い方を探っていこうと思います。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?