log
码中赤兔

使用CDK构建ALB + Fargate

发布于 2025年4月27日
更新于 2025年4月27日
15 分钟阅读
AWS

在现代服务架构中,借助容器化与 serverless 模式可以大幅简化运维工作。本文示例展示如何使用 AWS CDK(v2)通过 Fargate 部署 serverless服务。

1. 项目结构概览

在开始之前,假设你已经安装好 AWS CDK 并初始化了一个 TypeScript 项目。目录结构示例如下:

my-cdk-project/
├── bin/
│   └── my-cdk-project.ts  // CDK 应用入口
├── lib/
│   └── server-stack.ts    // 我们接下来要编写的 Stack
├── package.json
└── tsconfig.json

2. 核心 CDK 代码

import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecr from 'aws-cdk-lib/aws-ecr';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as log from 'aws-cdk-lib/aws-logs';
import { Construct } from 'constructs';

export class ServerStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // 1. 加载已有网络与安全组资源
    const albSg = ec2.SecurityGroup.fromSecurityGroupId(this, 'alb-sg', 'alb-sg-id');
    const ecsSg = ec2.SecurityGroup.fromSecurityGroupId(this, 'ecs-sg', 'ecs-sg-id');
    const vpc = ec2.Vpc.fromLookup(this, 'vpc', { vpcId: 'vpc-id' });
    const albSubnetA = ec2.Subnet.fromSubnetId(this, 'alb-subnet-a', 'alb-subnet-a-id');
    const albSubnetC = ec2.Subnet.fromSubnetId(this, 'alb-subnet-c', 'alb-sunbet-c-id');
    const ecsSubnetA = ec2.Subnet.fromSubnetId(this, 'ecs-subnet-a', 'ecs-subnet-a-id');
    const ecsSubnetC = ec2.Subnet.fromSubnetId(this, 'ecs-subnet-c', 'ecs-sunbet-c-id');

    // 2. 引入已有 IAM 角色
    const taskRole = iam.Role.fromRoleArn(this, 'task-role', 'task-role-arn', {
      mutable: false,
    });
    const taskExecutionRole = iam.Role.fromRoleArn(this, 'task-execution-role', 'task-execution-role-arn', {
      mutable: false,
    });

    // 3. 创建 ECS 集群
    const cluster = new ecs.Cluster(this, 'ecs-cluster', {
      vpc: vpc,
      clusterName: 'ecs-cluster',
    });

    // 4. 创建任务定义
    const taskDefinition = new ecs.FargateTaskDefinition(this, 'task-definition', {
      taskRole: taskRole,
      executionRole: taskExecutionRole,
      cpu: 512,
      memoryLimitMiB: 1024,
    });

    // 5. 引入 ECR Repository
    const repository = ecr.Repository.fromRepositoryArn(this, 'ecr-repository', 'ecr-arn');

    // 6. 创建log group
    const logGroup = new log.LogGroup(this, 'server-log', {
      logGroupName: '/aws/server-log',
      retention: log.RetentionDays.THREE_YEARS,
    });

    // 7. 配置容器镜像
    taskDefinition.addContainer('task', {
      image: ecs.ContainerImage.fromEcrRepository(repository, 'xxxxx'),
      logging: ecs.LogDriver.awsLogs({
        logGroup: logGroup,
        streamPrefix: 'api',
      }),
      portMappings: [{ containerPort: 80 }],
      environment: {
        NODE_ENV: 'it',
      },
      readonlyRootFilesystem: true,
    });

    // 8. 部署 Fargate 服务
    const service = new ecs.FargateService(this, 'service', {
      serviceName: 'server_name',
      cluster: cluster,
      taskDefinition: taskDefinition,
      securityGroups: [ecsSg],
      vpcSubnets: { subnets: [ecsSubnetA, ecsSubnetC] },
    });

    // 9. 生成应用型负载均衡(ALB)
    const alb = new elbv2.ApplicationLoadBalancer(this, `alb`, {
      loadBalancerName: 'alb',
      securityGroup: albSg,
      vpc: vpc,
      vpcSubnets: { subnets: [albSubnetA, albSubnetC] },
    });

    // 10. 创建目标组与健康检查
    const targetGroup = new elbv2.ApplicationTargetGroup(this, 'alb-tg', {
      targetGroupName: '',
      vpc: vpc,
      port: 80,
      protocol: elbv2.ApplicationProtocol.HTTP,
      targetType: elbv2.TargetType.IP,
      crossZoneEnabled: true,
      healthCheck: {
        path: '/health',
        healthyHttpCodes: '200',
      },
      targets: [service],
    });

    // 11. 添加监听器与路由规则
    const listener = alb.addListener('alb-listener', {
      port: 80,
      protocol: elbv2.ApplicationProtocol.HTTP,
      open: false,
      defaultAction: elbv2.ListenerAction.fixedResponse(404, { contentType: 'application/json' }),
    });

    new elbv2.CfnListenerRule(this, 'server-rule', {
      listenerArn: listener.listenerArn,
      priority: 1,
      conditions: [
        {
          field: 'path-pattern',
          values: ['/api/*'], // 在这里我们可以指定路由规则,只将符合规则的请求转发到Fargate
        },
      ],
      actions: [{ type: 'forward', targetGroupArn: targetGroup.targetGroupArn }],
    });
  }
}

3. 总结

通过本文示例,你可以快速复用已有的 VPC、子网、安全组、IAM 角色、ECR 仓库和日志组,借助 AWS CDK 一键式部署:

  1. 高可用:多可用区负载均衡 + Fargate 无服务器服务
  2. 安全:细粒度安全组、只读容器文件系统
  3. 可观测:CloudWatch Logs 实时日志

将此模板进一步扩展,可支持 HTTPS、自动扩缩容、蓝绿发布等多种生产级特性。欢迎在评论区分享你的优化方案!

关于

分享技术见解、经验和思考的个人博客

联系方式

  • Email: hushukang_blog@proton.me
  • GitHub

© 2025 码中赤兔. 版权所有