使用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自动部署