AWS CDK如何同时实现ECS服务发现与Route53 CNAME记录配置?
解决方案
核心思路
不需要手动查询CloudMap自动生成的私有托管区域,aws_servicediscovery.PrivateDnsNamespace 构造对象本身会直接暴露自动生成的托管区域属性,你可以直接在CDK栈之间传递该属性,同时满足手动添加CNAME和自动注册ECS服务发现的需求。
实现步骤
步骤1:在VPC栈中创建私有DNS命名空间并暴露核心属性
直接在创建VPC的栈里初始化私有DNS命名空间,同时导出自动生成的托管区域和命名空间对象给后续栈使用:
from aws_cdk import ( Stack, Duration, aws_ec2 as ec2, aws_servicediscovery as servicediscovery, aws_route53 as route53 ) from constructs import Construct class VpcStack(Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # 创建VPC self.vpc = ec2.Vpc(self, "CustomVpc", max_azs=2) # 创建私有DNS命名空间,CloudMap会自动生成对应私有托管区域 self.private_dns_namespace = servicediscovery.PrivateDnsNamespace( self, "CustomPrivateNamespace", name="domain.com", vpc=self.vpc ) # 直接获取自动生成的托管区域,暴露给其他栈调用 self.private_hosted_zone: route53.IPrivateHostedZone = self.private_dns_namespace.hosted_zone
步骤2:在数据库栈中添加数据库CNAME记录
接收VPC栈传递的托管区域对象,直接添加指向数据库端点的CNAME记录:
from aws_cdk import ( Stack, Duration, aws_rds as rds, aws_route53 as route53, aws_ec2 as ec2 ) from constructs import Construct class DbStack(Stack): def __init__(self, scope: Construct, construct_id: str, vpc: ec2.Vpc, hosted_zone: route53.IPrivateHostedZone, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # 创建RDS实例(其他数据库同理) db_instance = rds.DatabaseInstance( self, "PostgresDb", engine=rds.DatabaseInstanceEngine.postgres(version=rds.PostgresEngineVersion.VER_15), vpc=vpc, # 其余配置省略 ) # 新增db.domain.com的CNAME记录 route53.CnameRecord( self, "DbDnsRecord", zone=hosted_zone, record_name="db.domain.com", domain_name=db_instance.db_instance_endpoint_address, ttl=Duration.minutes(5) )
步骤3:在ECS服务栈中配置服务发现
接收VPC栈传递的私有DNS命名空间,直接配置ECS服务的服务发现规则,CDK会自动生成对应DNS记录:
from aws_cdk import ( Stack, Duration, aws_ecs as ecs, aws_servicediscovery as servicediscovery, aws_ec2 as ec2 ) from constructs import Construct class EcsServiceStack(Stack): def __init__(self, scope: Construct, construct_id: str, vpc: ec2.Vpc, dns_namespace: servicediscovery.IPrivateDnsNamespace, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # ECS集群、任务定义创建逻辑省略 cluster = ecs.Cluster(self, "EcsCluster", vpc=vpc) task_def = ecs.FargateTaskDefinition(self, "ServiceATaskDef") task_def.add_container("ServiceAContainer", image=ecs.ContainerImage.from_registry("amazon/amazon-ecs-sample")) # 创建ECS服务并配置服务发现,自动生成serviceA.domain.com记录 ecs.FargateService( self, "ServiceA", cluster=cluster, task_definition=task_def, cloud_map_options=ecs.CloudMapOptions( name="serviceA", cloud_map_namespace=dns_namespace, dns_record_type=servicediscovery.DnsRecordType.A, dns_ttl=Duration.minutes(1) ) )
步骤4:在CDK入口配置栈依赖
在app.py中按顺序初始化三个栈,传递对应的属性即可:
from aws_cdk import App from vpc_stack import VpcStack from db_stack import DbStack from ecs_service_stack import EcsServiceStack app = App() vpc_stack = VpcStack(app, "VpcStack") DbStack(app, "DbStack", vpc=vpc_stack.vpc, hosted_zone=vpc_stack.private_hosted_zone) EcsServiceStack(app, "EcsServiceStack", vpc=vpc_stack.vpc, dns_namespace=vpc_stack.private_dns_namespace) app.synth()
原报错原因说明
你之前使用PrivateHostedZone.from_lookup报错,是因为该方法用于查询当前CDK应用之外已经存在的托管区域,需要在synth阶段就拿到所有参数的实际值(比如VPC ID)。如果你的VPC是同个CDK应用下其他栈创建的,synth阶段vpc.vpc_id是未解析的Token占位符,无法用来查询AWS账户的现有资源,因此会触发报错。
使用同个CDK应用内生成的PrivateDnsNamespace的hosted_zone属性,走的是CDK内部的引用传递逻辑,不需要提前知道资源的实际ID,部署时会自动解析,完全不会出现Token相关的错误。
内容的提问来源于stack exchange,提问作者Claus Agerskov
相关产品推荐
相关产品推荐

