AWS CDKでECS Fargateにコンテナをデプロイ

AWS ECS Fargateを使えば、サーバーを管理せずにコンテナを実行できます。AWS CDK(Cloud Development Kit)と組み合わせることで、型安全なInfrastructure as Codeを快適に書くことができます。このチュートリアルでは、本番対応のコンテナ化APIをゼロからデプロイします。

構築するもの

  • Docker コンテナ化されたNode.js API
  • パブリック/プライベートサブネットを持つVPC
  • Application Load Balancer(ALB)
  • オートスケーリング付きECS Fargateサービス
  • CloudWatch ロギングとヘルスチェック
  • CodePipelineによるCI/CDパイプライン
  • 前提条件

  • CLIが設定済みのAWSアカウント
  • Node.js 18+ と npm
  • Dockerインストール済み
  • TypeScriptの基本知識

---

ステップ1: アプリケーションの作成

まず、コンテナ化するシンプルなExpress APIを作成します:

mkdir ecs-fargate-app && cd ecs-fargate-app

mkdir app infra

cd app && npm init -y

npm install express

app/src/index.js:

const express = require('express');

const app = express();

const PORT = process.env.PORT || 3000;

app.get('/health', (req, res) => {

res.json({ status: 'healthy', timestamp: new Date().toISOString() });

});

app.get('/api/info', (req, res) => {

res.json({

service: 'ecs-fargate-demo',

version: process.env.APP_VERSION || '1.0.0',

region: process.env.AWS_REGION || 'unknown',

task: process.env.ECS_TASK_ARN || 'local',

});

});

app.get('/api/compute/:n', (req, res) => {

const n = parseInt(req.params.n) || 10;

const fib = (num) => num <= 1 ? num : fib(num - 1) + fib(num - 2);

const start = Date.now();

const result = fib(Math.min(n, 40));

res.json({ input: n, fibonacci: result, computeMs: Date.now() - start });

});

app.listen(PORT, '0.0.0.0', () => {

console.log(Server running on port ${PORT});

});

ステップ2: アプリケーションのコンテナ化

app/Dockerfile:

FROM node:18-alpine AS builder

WORKDIR /app

COPY package*.json ./

RUN npm ci --only=production

FROM node:18-alpine

RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

COPY --from=builder /app/node_modules ./node_modules

COPY src/ ./src/

USER appuser

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \

CMD wget -qO- http://localhost:3000/health || exit 1

CMD ["node", "src/index.js"]

ローカルでテスト:

cd app

docker build -t ecs-demo .

docker run -p 3000:3000 ecs-demo

curl http://localhost:3000/api/info

ステップ3: AWS CDKプロジェクトのセットアップ

cd ../infra

npx cdk init app --language typescript

npm install aws-cdk-lib constructs

ステップ4: インフラストラクチャの定義

infra/lib/ecs-fargate-stack.ts:

import * as cdk from 'aws-cdk-lib';

import * as ec2 from 'aws-cdk-lib/aws-ec2';

import * as ecs from 'aws-cdk-lib/aws-ecs';

import * as ecsPatterns from 'aws-cdk-lib/aws-ecs-patterns';

import * as logs from 'aws-cdk-lib/aws-logs';

import * as path from 'path';

import { Construct } from 'constructs';

export class EcsFargateStack extends cdk.Stack {

constructor(scope: Construct, id: string, props?: cdk.StackProps) {

super(scope, id, props);

// 1. 2つのAZを持つVPC(パブリック+プライベートサブネット)

const vpc = new ec2.Vpc(this, 'AppVpc', {

maxAzs: 2,

natGateways: 1,

subnetConfiguration: [

{

name: 'Public',

subnetType: ec2.SubnetType.PUBLIC,

cidrMask: 24,

},

{

name: 'Private',

subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,

cidrMask: 24,

},

],

});

// 2. ECSクラスター

const cluster = new ecs.Cluster(this, 'AppCluster', {

vpc,

clusterName: 'fargate-demo-cluster',

containerInsights: true,

});

// 3. ALB付きFargateサービス(高レベルパターン使用)

const fargateService = new ecsPatterns.ApplicationLoadBalancedFargateService(

this, 'AppService', {

cluster,

serviceName: 'fargate-demo-service',

desiredCount: 2,

cpu: 256,

memoryLimitMiB: 512,

taskImageOptions: {

image: ecs.ContainerImage.fromAsset(

path.join(__dirname, '../../app')

),

containerPort: 3000,

environment: {

APP_VERSION: '1.0.0',

NODE_ENV: 'production',

},

logDriver: ecs.LogDrivers.awsLogs({

streamPrefix: 'fargate-demo',

logRetention: logs.RetentionDays.ONE_WEEK,

}),

},

publicLoadBalancer: true,

assignPublicIp: false,

}

);

// 4. ヘルスチェック設定

fargateService.targetGroup.configureHealthCheck({

path: '/health',

interval: cdk.Duration.seconds(30),

timeout: cdk.Duration.seconds(5),

healthyThresholdCount: 2,

unhealthyThresholdCount: 3,

});

// 5. オートスケーリング

const scaling = fargateService.service.autoScaleTaskCount({

minCapacity: 2,

maxCapacity: 10,

});

scaling.scaleOnCpuUtilization('CpuScaling', {

targetUtilizationPercent: 70,

scaleInCooldown: cdk.Duration.seconds(60),

scaleOutCooldown: cdk.Duration.seconds(60),

});

scaling.scaleOnMemoryUtilization('MemoryScaling', {

targetUtilizationPercent: 80,

});

// 6. リクエストベースのスケーリング

scaling.scaleOnRequestCount('RequestScaling', {

requestsPerTarget: 1000,

targetGroup: fargateService.targetGroup,

});

// 出力

new cdk.CfnOutput(this, 'LoadBalancerDNS', {

value: fargateService.loadBalancer.loadBalancerDnsName,

description: 'Application Load Balancer DNS',

});

new cdk.CfnOutput(this, 'ServiceURL', {

value: http://${fargateService.loadBalancer.loadBalancerDnsName},

description: 'Service URL',

});

}

}

ステップ5: CDKアプリの設定

infra/bin/infra.ts:

import 'source-map-support/register';

import * as cdk from 'aws-cdk-lib';

import { EcsFargateStack } from '../lib/ecs-fargate-stack';

const app = new cdk.App();

new EcsFargateStack(app, 'EcsFargateStack', {

env: {

account: process.env.CDK_DEFAULT_ACCOUNT,

region: process.env.CDK_DEFAULT_REGION || 'ap-northeast-1',

},

description: 'ECS Fargate Demo with ALB and Auto-Scaling',

});

ステップ6: デプロイ

cd infra

# CDKブートストラップ(初回のみ)

npx cdk bootstrap

# 変更のプレビュー

npx cdk diff

# デプロイ

npx cdk deploy --require-approval broadening

CDKは以下を実行します:

1. ローカルでDockerイメージをビルド

2. ECR(自動作成)にプッシュ

3. VPC、ALB、ECSクラスター、Fargateサービスを作成

4. ALBのDNS名を出力

# デプロイされたサービスをテスト

curl http://<ALB-DNS>/api/info

curl http://<ALB-DNS>/api/compute/30

ステップ7: CI/CDパイプラインの追加

CodePipelineを使用して、GitプッシュでのECS自動デプロイを設定できます。GitHub Sourceステージ、CodeBuildでのDockerビルド、ECSデプロイアクションの3ステージパイプラインを構築します。

ステップ8: カスタムドメインとHTTPS

Route 53ホストゾーンとACM証明書を使用して、api.example.comのようなカスタムドメインでHTTPSアクセスを設定できます。CDKのパターンコンストラクトが自動的にHTTP→HTTPSリダイレクトを処理します。

ステップ9: モニタリングとアラート

CloudWatchアラームとSNSトピックを設定して、CPU使用率やHTTP 5XXエラーを監視します。CloudWatchダッシュボードでメトリクスを可視化できます。

ステップ10: クリーンアップ

npx cdk destroy --all

アーキテクチャ図

┌─────────────────────────────────────────────────┐

│ VPC │

│ ┌──────────────┐ ┌──────────────────────┐ │

│ │ Public Subnet │ │ Private Subnet │ │

│ │ ┌────────┐ │ │ ┌────────────────┐ │ │

│ │ │ ALB │──┼────┼─▶│ ECS Fargate │ │ │

│ │ └────────┘ │ │ │ Task 1, 2... │ │ │

│ └──────────────┘ │ └────────────────┘ │ │

│ └──────────────────────┘ │

└─────────────────────────────────────────────────┘

まとめ

1. CDKパターンでデプロイを簡素化 — 1つのコンストラクトで30以上のリソースを作成

2. Fargate = サーバー管理不要 — AWSが基盤インフラを管理

3. オートスケーリング内蔵 — CPU、メモリ、リクエスト数でスケーリング

4. 実コードとしてのインフラ — TypeScriptで型チェックとリファクタリングが可能

5. CodePipelineでCI/CD — git pushで自動デプロイ