跨AWS CDK Stack与ECS服务共享VPC等资源的最优方案
解决方案:ECS资源共享与跨CDK Stack资源复用
一、ECS服务间共享VPC、Security Group、RDS并配置Global Accelerator
核心思路
通过统一ECS集群复用EC2实例,将所有关联资源(VPC、安全组、RDS)纳入同一网络环境,再借助Global Accelerator提供稳定静态公网IP。
具体实现步骤
创建共享VPC
构建包含公有/私有子网的VPC,所有ECS服务、RDS均部署在此VPC内,避免重复创建网络资源。复用ECS集群的EC2实例
创建单个ECS集群(EC2启动类型),将多个ECS服务部署至该集群。集群会自动调度、管理EC2实例,实现资源复用,无需重复创建实例。共享Security Group
- 创建基础安全组:允许ECS服务间内部通信,以及ECS服务访问RDS的指定端口(如MySQL的3306)。
- 各ECS服务可直接复用此基础安全组,或基于它扩展额外规则(如允许负载均衡访问ECS服务端口)。
共享RDS实例
将RDS部署在VPC私有子网中,仅授权ECS服务的安全组访问RDS,既保障数据库安全,又能供所有ECS服务共享使用。配置Global Accelerator获取静态IP
- 为ECS服务绑定的负载均衡(ALB/NLB)创建Global Accelerator终端节点组,关联负载均衡的ARN。
- Global Accelerator会分配两个静态公网IP,用户可通过这两个IP稳定访问ECS服务,不受区域或IP变动影响。
CDK代码示例(TypeScript)
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 rds from 'aws-cdk-lib/aws-rds'; import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2'; import * as globalaccelerator from 'aws-cdk-lib/aws-globalaccelerator'; import * as gaTargets from 'aws-cdk-lib/aws-globalaccelerator-endpoints'; const app = new cdk.App(); // 1. 共享VPC Stack const vpcStack = new cdk.Stack(app, 'SharedVpcStack'); const vpc = new ec2.Vpc(vpcStack, 'SharedVpc', { maxAzs: 2, subnetConfiguration: [ { name: 'Public', subnetType: ec2.SubnetType.PUBLIC }, { name: 'Private', subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, ], }); // 2. 共享ECS集群与RDS Stack const resourceStack = new cdk.Stack(app, 'SharedResourceStack', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, }); // 共享安全组 const ecsSg = new ec2.SecurityGroup(resourceStack, 'EcsSharedSg', { vpc }); const rdsSg = new ec2.SecurityGroup(resourceStack, 'RdsSg', { vpc }); rdsSg.addIngressRule(ecsSg, ec2.Port.tcp(3306), '允许ECS访问RDS'); // 共享RDS实例 const rdsInstance = new rds.DatabaseInstance(resourceStack, 'SharedRds', { engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_33 }), vpc, vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, securityGroups: [rdsSg], instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO), }); // ECS集群(复用EC2实例) const ecsCluster = new ecs.Cluster(resourceStack, 'SharedEcsCluster', { vpc }); ecsCluster.addCapacity('DefaultAutoScalingGroup', { instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM), desiredCapacity: 2, securityGroups: [ecsSg], }); // 3. 部署第一个ECS服务 const ecsService1Stack = new cdk.Stack(app, 'EcsService1Stack', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, }); const alb1 = new elbv2.ApplicationLoadBalancer(ecsService1Stack, 'Alb1', { vpc, internetFacing: true, }); const listener1 = alb1.addListener('Listener1', { port: 80 }); const task1 = new ecs.Ec2TaskDefinition(ecsService1Stack, 'Task1'); task1.addContainer('AppContainer1', { image: ecs.ContainerImage.fromRegistry('nginx'), memoryLimitMiB: 256, }); const service1 = new ecs.Ec2Service(ecsService1Stack, 'Service1', { cluster: ecsCluster, taskDefinition: task1, desiredCount: 1, }); listener1.addTargets('Target1', { port: 80, targets: [service1], }); // 配置Global Accelerator const accelerator = new globalaccelerator.Accelerator(ecsService1Stack, 'GlobalAccelerator'); const gaListener = accelerator.addListener('GaListener', { portRanges: [{ fromPort: 80 }], }); gaListener.addEndpointGroup('EndpointGroup', { endpoints: [new gaTargets.ApplicationLoadBalancerEndpoint(alb1)], }); // 4. 部署第二个ECS服务(复用集群、VPC、安全组) const ecsService2Stack = new cdk.Stack(app, 'EcsService2Stack', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, }); const task2 = new ecs.Ec2TaskDefinition(ecsService2Stack, 'Task2'); task2.addContainer('AppContainer2', { image: ecs.ContainerImage.fromRegistry('httpd'), memoryLimitMiB: 256, }); const service2 = new ecs.Ec2Service(ecsService2Stack, 'Service2', { cluster: ecsCluster, taskDefinition: task2, desiredCount: 1, securityGroups: [ecsSg], });
二、跨AWS CDK Stack共享资源的最佳实践
1. 同App内Stack:构造函数传递资源引用
这是最直接、类型安全的方式,适用于同一CDK App下的多个Stack。
- 在资源所属Stack中创建资源后,将其作为参数传入依赖Stack的构造函数。
- 优点:编译时类型检查,无额外导出导入操作,维护简单。
- 示例:
// 资源Stack class ResourceStack extends cdk.Stack { public readonly vpc: ec2.Vpc; constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); this.vpc = new ec2.Vpc(this, 'SharedVpc'); } } // 依赖Stack class EcsStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props: { vpc: ec2.Vpc }) { super(scope, id); new ecs.Cluster(this, 'EcsCluster', { vpc: props.vpc }); } } // 使用 const app = new cdk.App(); const resourceStack = new ResourceStack(app, 'ResourceStack'); new EcsStack(app, 'EcsStack', { vpc: resourceStack.vpc });
2. 跨App/同区域:Stack输出与Fn.importValue
适用于不同CDK App部署的Stack,或需要跨Stack共享资源属性的场景。
- 在资源Stack中通过
cdk.CfnOutput导出资源的关键属性(如VPC ID、安全组ARN)。 - 在依赖Stack中使用
cdk.Fn.importValue导入这些属性。 - 优点:无需代码耦合,支持跨App部署;缺点:缺少类型检查,需确保输出名称完全一致。
- 示例:
// 资源Stack const resourceStack = new cdk.Stack(app, 'ResourceStack'); const vpc = new ec2.Vpc(resourceStack, 'SharedVpc'); new cdk.CfnOutput(resourceStack, 'VpcIdOutput', { value: vpc.vpcId, exportName: 'SharedVpcId', }); // 依赖Stack const ecsStack = new cdk.Stack(app, 'EcsStack'); const vpcId = cdk.Fn.importValue('SharedVpcId'); const vpc = ec2.Vpc.fromLookup(ecsStack, 'ImportedVpc', { vpcId });
3. 跨部署/灵活场景:SSM Parameter Store存储资源属性
将资源的关键属性(如VPC ID、安全组ID)存储在SSM Parameter Store中,其他Stack读取这些参数。
- 优点:支持跨账户、跨区域(需配置对应权限),适合长期共享或频繁变更的资源;缺点:需额外管理SSM参数,增加运维成本。
- 示例:
import * as ssm from 'aws-cdk-lib/aws-ssm'; // 资源Stack const resourceStack = new cdk.Stack(app, 'ResourceStack'); const vpc = new ec2.Vpc(resourceStack, 'SharedVpc'); new ssm.StringParameter(resourceStack, 'VpcIdParam', { parameterName: '/shared-resources/vpc-id', stringValue: vpc.vpcId, }); // 依赖Stack const ecsStack = new cdk.Stack(app, 'EcsStack'); const vpcId = ssm.StringParameter.valueFromLookup(ecsStack, '/shared-resources/vpc-id'); const vpc = ec2.Vpc.fromLookup(ecsStack, 'ImportedVpc', { vpcId });
4. 封装为可复用Construct
将共享资源(如VPC、安全组)封装成自定义Construct,在多个Stack中实例化该Construct,确保资源配置一致且避免重复创建。
- 优点:代码复用性高,配置统一;缺点:需确保Construct在同一环境中仅实例化一次,避免重复创建资源。
内容的提问来源于stack exchange,提问作者Anwar Javed
相关产品推荐
相关产品推荐

