AWS CDK Java:在默认VPC中创建PrivateSubnet后无法通过SubnetType.PRIVATE选择子网的问题咨询
问题解答
1. CDK如何确定要选择的VPC?
当你使用 Vpc.fromLookup() 时,CDK会在合成(synth)阶段执行以下逻辑定位目标VPC:
- 调用AWS API查询当前
env参数指定的账户和区域中的VPC资源; - 根据传入的
VpcLookupOptions(这里是isDefault: true)筛选出AWS默认创建的VPC; - 查询完成后,CDK会缓存该VPC的所有元数据(包括已存在的子网、路由表、网关等),后续的子网选择操作都基于这些缓存数据进行。
关键提醒:fromLookup() 获取的是AWS账户中已部署的现有VPC,它不会感知到你在同一个栈里刚定义还未部署的新子网——因为在synth阶段,这些新子网还未实际存在于AWS环境中,所以lookup结果里不会包含它们。
2. 为什么手动创建的PrivateSubnet无法被按SubnetType.PRIVATE选中?
你遇到的问题主要源于两个核心原因:
(1)CDK对子网类型的判定逻辑
CDK不会单纯根据你创建的子网类(比如PrivateSubnet)标记子网类型,而是依据子网关联的路由表配置来判断:
- 如果子网的路由表包含指向互联网网关(IGW)的路由,CDK会将其标记为
Public类型; - 只有当子网的路由表仅包含指向NAT网关的路由(无直接到IGW的路由)时,才会被判定为
Private类型。
你手动创建PrivateSubnet时,默认会关联VPC的主路由表,而AWS默认VPC的主路由表本身就包含指向IGW的路由,这就导致你的“私有子网”被CDK判定为公共子网。
(2)fromLookup的VPC不包含新创建的子网
如第一点所述,Vpc.fromLookup() 获取的是现有VPC的缓存元数据,你在栈中手动创建的新子网还未部署到AWS,所以CDK进行子网选择时,只会从lookup到的原有子网(默认VPC的公共子网)里查找,自然找不到你刚定义的私有子网。
解决方案
针对你的场景,推荐两种处理方式:
方式一:使用自定义VPC而非默认VPC
如果你需要私有子网,更稳妥的方式是直接用CDK的Vpc构造器创建自定义VPC,它会自动配置好私有子网的路由表(关联NAT网关而非IGW),代码示例:var stack = new Stack(app, "system-resource", StackProps.builder().env(env).build()); var vpc = Vpc.Builder.create(stack, "CustomVpc") .maxAzs(3) .subnetConfiguration(List.of( SubnetConfiguration.builder() .name("PrivateSubnet") .subnetType(SubnetType.PRIVATE_WITH_EGRESS) .cidrMask(24) .build(), SubnetConfiguration.builder() .name("PublicSubnet") .subnetType(SubnetType.PUBLIC) .cidrMask(24) .build() )) .build(); // 此时可以直接用SubnetType.PRIVATE选择私有子网 var vpcSubnets = SubnetSelection.builder().subnetType(SubnetType.PRIVATE_WITH_EGRESS).build();方式二:手动配置子网路由并指定子网ID选择
如果你一定要基于默认VPC扩展,需要手动修改子网的路由表,并在子网选择时直接指定子网ID:var stack = new Stack(app, "system-resource", StackProps.builder().env(env).build()); final IVpc vpc = Vpc.fromLookup(stack, "default-vpc", VpcLookupOptions.builder().isDefault(true).build()); final AtomicInteger index = new AtomicInteger(); var zones = stack.getAvailabilityZones(); List<Subnet> privateSubnets = zones.stream().map(z -> { var subnet = new PrivateSubnet(stack, "private-subnet-" + index.getAndIncrement(), PrivateSubnetProps.builder() .vpcId(vpc.getVpcId()) .cidrBlock(String.format(cidrTemplate, start.get())) .availabilityZone(z) .build()); // 移除主路由表的IGW路由,创建新路由表并添加NAT网关路由 var natGateway = NatGateway.fromNatGatewayId(stack, "NatGateway-" + index.get(), vpc.getPublicSubnets().get(0).getNatGatewayId()); var privateRouteTable = new RouteTable(stack, "PrivateRouteTable-" + index.get(), RouteTableProps.builder().vpc(vpc).build()); privateRouteTable.addRoute("NatRoute", RouteProps.builder() .destinationCidrBlock("0.0.0.0/0") .natGateway(natGateway) .build()); subnet.associateRouteTable(privateRouteTable); start.addAndGet(increment); return subnet; }).collect(Collectors.toList()); // 直接用子网ID选择 var vpcSubnets = SubnetSelection.builder().subnetIds(privateSubnets.stream().map(Subnet::getSubnetId).collect(Collectors.toList())).build();
内容的提问来源于stack exchange,提问作者Cherry
相关产品推荐
相关产品推荐

