使用AWS CDK在ECS Fargate上部署容器
AWS ECS Fargate让你无需管理服务器即可运行容器。结合AWS CDK(Cloud Development Kit),你可以用类型安全的代码定义基础设施。本教程将从零开始部署一个生产就绪的容器化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. 双可用区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,
});
new cdk.CfnOutput(this, 'ServiceURL', {
value: http://${fargateService.loadBalancer.loadBalancerDnsName},
});
}
}
步骤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
npx cdk bootstrap # 首次使用
npx cdk diff # 预览变更
npx cdk deploy --require-approval broadening
CDK将自动:构建Docker镜像 → 推送到ECR → 创建VPC、ALB、ECS集群和Fargate服务 → 输出ALB DNS。
步骤7-9:CI/CD、自定义域名、监控
通过CodePipeline实现git push自动部署,使用Route 53和ACM配置HTTPS自定义域名,以及CloudWatch告警和仪表板监控。
步骤10:清理
npx cdk destroy --all
架构图
┌─────────────────────────────────────────────────┐
│ VPC │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 公有子网 │ │ 私有子网 │ │
│ │ ┌────────┐ │ │ ┌────────────────┐ │ │
│ │ │ ALB │──┼────┼─▶│ ECS Fargate │ │ │
│ │ └────────┘ │ │ │ Task 1, 2... │ │ │
│ └──────────────┘ │ └────────────────┘ │ │
│ └──────────────────────┘ │
└─────────────────────────────────────────────────┘
核心要点
1. CDK模式简化部署 — 一个构造创建30+资源
2. Fargate = 零服务器管理 — AWS管理底层基础设施
3. 内置自动扩缩 — 基于CPU、内存或请求数扩缩
4. 真正的代码即基础设施 — TypeScript提供自动补全和类型检查
5. CodePipeline实现CI/CD — git push自动部署