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で自動デプロイ