【免费下载链接】aws-cdk-examples
Example projects using the AWS CDK
本指南基于 aws-cdk-examples 仓库中的 java/eks/private-cluster 示例,深入讲解如何用 AWS CDK(Java 语言)在一座完全没有公网出口的 VPC 中搭建 EKS 私有集群:VPC 只包含隔离子网(Isolated Subnets),不创建 Internet Gateway、NAT Gateway 或任何互联网代理;集群通过一组 VPC Endpoints 私有访问 AWS 服务;管理员借助 AWS Systems Manager Session Manager 免公网登录 EC2 实例,并通过
kubectl管理集群。读完本文,你将掌握私有集群的整体架构设计、全部 VPC Endpoint 清单与作用、完整的 Java 源码实现要点、部署与验证步骤,以及无互联网环境下安装 Docker、kubectl 等工具的可行方法。
适用场景
私有 EKS 集群(Private EKS Cluster)通常用于企业级部署或高合规监管环境,例如金融、医疗、政府等对数据出境、公网暴露有严格要求的场景。这类环境的核心诉求是:部署的工作负载不允许任何互联网连通性——Pod 不能访问公网,公网也无法访问集群。示例中所有 EC2 实例都不具备公网 IP,唯一的运维入口是通过 AWS Systems Manager(SSM)的私有通道进行会话管理。
整体架构与设计要点
从 EksPrivateClusterStack.java 的构造逻辑看,整个 Stack 由四个部分组成:
- VPC:仅含隔离子网,无公网出口;
- VPC Endpoints:一组接口端点(Interface Endpoint)+ 一个网关端点(Gateway Endpoint),保证集群在无公网环境下仍能调用 AWS API;
- EKS 集群:私有访问模式的托管集群,外加一个自动扩缩组(Auto Scaling Group)承载工作节点;
- kubectl 客户端实例:一个位于隔离子网内的 BastionHostLinux 实例,通过 SSM 访问,用于执行
kubectl命令。
VPC:只创建隔离子网
createVpc() 方法给出了 VPC 的关键配置:
this.vpc = Vpc.Builder.create(this, "vpc") .ipAddresses(IpAddresses.cidr("10.0.0.0/16")) .maxAzs(2) .natGateways(0) // 不创建 NAT 网关 .subnetConfiguration( List.of( SubnetConfiguration.builder() .cidrMask(24) .name("isolated") .subnetType(SubnetType.PRIVATE_ISOLATED) // 仅隔离子网 .build())) .build();要点说明:
- CIDR:使用
10.0.0.0/16作为 VPC 网段,cidrMask(24)将每个子网切分为/24; maxAzs(2):在两个可用区(AZ)中创建子网,满足高可用要求,同时控制端点和集群成本;natGateways(0):显式关闭 NAT 网关,配合不创建 Internet Gateway,使 VPC 完全与公网隔绝;- 子网类型:仅配置
PRIVATE_ISOLATED一种子网类型。隔离子网既没有到 Internet Gateway 的路由,也没有 NAT 出口路由,实例因此完全无法访问公网。
测试类 EksPrivateClusterStackTest.java 中专门用两个断言来验证这一点:
@Test public void testNoInternetGateway() { final Template template = synthesizeTemplate(); template.resourceCountIs("AWS::EC2::InternetGateway", 0); } @Test public void testNoNatGateway() { final Template template = synthesizeTemplate(); template.resourceCountIs("AWS::EC2::NatGateway", 0); }resourceCountIs(..., 0)断言合成出的 CloudFormation 模板中InternetGateway 与 NatGateway 的资源数量均为 0,从测试层面锁定了"无公网出口"这一核心设计。
EKS 集群:私有端点访问模式
createEksCluster() 方法配置了集群本体:
this.cluster = Cluster.Builder.create(this, "eks") .vpc(vpc) .version(KubernetesVersion.V1_34) // Kubernetes 1.34 .vpcSubnets( List.of(SubnetSelection.builder().subnetType(SubnetType.PRIVATE_ISOLATED).build())) .endpointAccess(EndpointAccess.PRIVATE) // 私有端点访问 .clusterName("eks-private") .kubectlLayer(new KubectlV34Layer(this, "KubectlLayer")) .defaultCapacity(0) // 默认节点池容量为 0 .mastersRole(clusterAdmin) // 集群管理员 IAM 角色 .placeClusterHandlerInVpc(true) // 集群自定义资源 Lambda 放入 VPC .clusterHandlerEnvironment(Map.of("AWS_STS_REGIONAL_ENDPOINTS", "regional")) .kubectlEnvironment(Map.of("AWS_STS_REGIONAL_ENDPOINTS", "regional")) .outputClusterName(true) .outputConfigCommand(true) .outputMastersRoleArn(true) .build();关键配置的含义:
endpointAccess(EndpointAccess.PRIVATE):集群 API Server 只暴露私有端点(private endpoint),不开放公网端点。测试用例testEksClusterEndpointAccess断言了对应的resourcesVpcConfig属性为endpointPublicAccess: false、endpointPrivateAccess: true;version(KubernetesVersion.V1_34):使用 Kubernetes 1.34,并配套cdk-lambda-layer-kubectl-v34依赖(见 pom.xml 中的kubectl.version属性)与KubectlV34Layer提供 kubectl Lambda Layer;defaultCapacity(0):不启用 CDK 默认的托管节点组,而是由后续addNodes()手动添加基于 Auto Scaling Group 的工作节点;mastersRole(clusterAdmin):将根账户角色的管理员角色加入aws-authConfigMap 的system:masters,该角色在 Stack 中以AccountRootPrincipal被创建;placeClusterHandlerInVpc(true):这是私有集群能否成功部署的关键。CDK 在创建 EKS 集群时依赖一组自定义资源(Custom Resource)Lambda 函数来监控集群生命周期(如等待集群可用、配置 kubectl 等)。默认情况下这些 Lambda 部署在 AWS 托管的公共 VPC 中,无法访问隔离子网内的私有集群;设置为true后,集群处理器 Lambda 会被部署到本 VPC 的隔离子网中,配合 VPC Endpoints 才能调用 EKS API;clusterHandlerEnvironment/kubectlEnvironment中的AWS_STS_REGIONAL_ENDPOINTS=regional:让 Lambda 内的 STS 调用使用区域化端点,因为隔离环境中没有到全局端点所在区域的网络路径;- 输出开关:
outputClusterName、outputConfigCommand、outputMastersRoleArn为true,使cdk deploy完成后直接打印集群名、kubeconfig命令和管理员角色 ARN,便于后续操作。
工作节点:Auto Scaling Group
addNodes() 通过cluster.addAutoScalingGroupCapacity添加工作节点:
AutoScalingGroup asg = this.cluster.addAutoScalingGroupCapacity( "nodes", AutoScalingGroupCapacityOptions.builder() .instanceType(InstanceType.of(InstanceClass.T3, InstanceSize.MEDIUM)) // t3.medium .minCapacity(1) // 至少 1 个节点 .vpcSubnets( SubnetSelection.builder().subnetType(SubnetType.PRIVATE_ISOLATED).build()) .ssmSessionPermissions(true) // 允许 SSM Session Manager 会话 .machineImageType(null) .build()); asg.addToRolePolicy( PolicyStatement.Builder.create() .effect(Effect.ALLOW) .actions( List.of( "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer", "ecr:GetAuthorizationToken")) .resources(List.of("*")) .build());要点:
- 工作节点使用
t3.medium类型、最小容量 1,部署在隔离子网内; ssmSessionPermissions(true):为节点实例角色附加 SSM 相关权限,使工作节点同样支持 Session Manager 接入(排障时很有用);- 节点角色被显式授予 ECR 拉取镜像所需的最小权限集(
ecr:BatchCheckLayerAvailability、ecr:BatchGetImage、ecr:GetDownloadUrlForLayer、ecr:GetAuthorizationToken),配合 ECR 接口端点即可从私有仓库拉取容器镜像。
kubectl 客户端实例:BastionHostLinux
createBastion() 创建了名为kubectl-client的堡垒机实例:
BastionHostLinux client = BastionHostLinux.Builder.create(this, "kubectl-client") .vpc(vpc) .instanceName("kubectl-client") .blockDevices( List.of( BlockDevice.builder() .deviceName("/dev/sdf") .volume( BlockDeviceVolume.ebs( 10, EbsDeviceOptions.builder().encrypted(true).build())) .build())) .subnetSelection( SubnetSelection.builder().subnetType(SubnetType.PRIVATE_ISOLATED).build()) .securityGroup(this.cluster.getClusterSecurityGroup()) // 复用集群安全组 .instanceType(InstanceType.of(InstanceClass.T3, InstanceSize.SMALL)) // t3.small .build();后续的权限配置体现了私有集群的运维设计:
clusterAdmin.grantAssumeClientRole(client.getRole())(源码中为grantAssumeRole):允许客户端实例角色Assume 集群管理员角色,从而获得system:masters级别的管理权限;- 客户端实例角色被授予
eks:DescribeCluster与eks:ListClusters,这是执行aws eks update-kubeconfig所需的权限; - 附加 AWS 托管策略
AmazonEC2ContainerRegistryPullOnly(用于拉取镜像)与AmazonS3ReadOnlyAccess(用于从 S3 资产桶读取 kubectl 等工具二进制); - 10 GB 的根 EBS 卷设置为加密(
encrypted(true)),符合高合规环境的安全基线。
VPC Endpoints 清单与作用
addEndpoints() 创建了全部端点。在控制台中,这些端点会出现在每一个隔离子网中,服务名以com.amazonaws.[region].为前缀,例如com.amazonaws.ap-southeast-1.s3。
网关端点(Gateway Endpoint)
| 服务 | 端点名称 | 用途 |
|---|---|---|
| S3 | s3 | 从 S3 桶下载资产、构件和工具(例如 kubectl 二进制、Amazon Linux 仓库中的软件包) |
S3 使用网关端点而非接口端点:网关端点通过路由表实现、不产生数据处理费,且 Amazon Linux 的 yum 仓库托管在 S3 桶中,因此在隔离环境中仍可正常安装系统软件包。
接口端点(Interface Endpoint)
| 服务 | 端点名称 | 用途 |
|---|---|---|
| ECR | ecr.api、ecr.dkr | 拉取私有仓库中的容器镜像,也可通过 ECR pull through cache 获取上游公共仓库镜像 |
| EC2 | ec2 | 创建工作节点与客户端实例时调用 EC2 API |
| EKS | eks | 调用 EKS API 完成集群创建、查询等操作 |
| Security Token Service | sts | 获取临时安全凭证,配合 AssumeRole 流程 |
| CloudWatch | logs、monitoring | 日志与监控数据的采集上报 |
| Systems Manager | ec2messages、ssm、ssmmessages | 支撑 SSM Session Manager 会话(另见 AWS 文档中 Systems Manager 所需的端点清单) |
| Lambda | lambda | 集群自定义资源处理器的依赖服务 |
| Step Functions | states、sync-states | CDK 的集群处理器(Custom Resource)在创建 EKS 集群时依赖的状态机服务 |
接口端点的实现与 README 中的列表一一对应,在源码中体现为addEndpoints()内的 13 个InterfaceVpcEndpointAwsService常量(ECR、ECR_DOCKER、CLOUDWATCH_MONITORING、CLOUDWATCH_LOGS、STS、SSM、SSM_MESSAGES、LAMBDA、EKS、EC2、EC2_MESSAGES、STEP_FUNCTIONS、STEP_FUNCTIONS_SYNC)。
端点的作用说明
- S3 端点:用于下载存储在 S3 桶中的资产、构件和工具;
- ECR 端点:用于拉取私有仓库的容器镜像,也可以通过 ECR pull through cache 拉取上游注册表中的公共镜像;
- EC2 端点:创建工作节点和客户端实例时调用 EC2 服务;
- EKS 端点:调用 EKS API 完成集群创建;
- CloudWatch 端点:提供日志与监控能力;
- Lambda 与 Step Functions 端点:CDK 的集群处理器(Custom Resource)在创建 EKS 集群时必须依赖这两个服务。
后续扩展与计费注意
- 部署完成后,随着你在集群中添加需要调用其他 AWS 服务的应用,需要为这些服务逐一添加对应的 VPC Endpoint(完整的 Privatelink 支持清单见 AWS 官方文档);
- 注意:VPC Endpoint 在每个可用区中保持开通状态时按小时计费,同时还会产生数据处理费,请参考 AWS PrivateLink 定价页面评估成本。
构建与部署
示例的 Java 入口类是 PrivateClusterCdkApp.java,它创建 ID 为EksPrivateCluster的 Stack 并调用app.synth()。构建依赖通过 pom.xml 管理,其中aws-cdk-lib版本区间为[2.262.0,3.0.0),编译目标为 Java 17;cdk.json 中配置的应用启动命令为mvn -e -q compile exec:java。
部署步骤如下:
进入示例根目录:
cd java/eks/private-cluster合成(synth)CloudFormation 模板:
cdk synth部署:
cdk deploy整个部署大约需要25 分钟,其中包括 EKS 集群、VPC Endpoints 以及 CDK 用于监控集群生命周期所需的 Lambda 函数的创建。这些函数被部署在 VPC 内部,这正是集群配置中
placeClusterHandlerInVpc设为true的效果(详见 EKS Construct Library)。部署成功后,留意 CDK 输出的集群名称、kubeconfig 设置、kubectl 客户端实例 ID 以及集群 master IAM 角色,后续操作都会用到。
示例输出:
Outputs: EksPrivateCluster.eksClusterNameXXXXXXXX = eks-private EksPrivateCluster.eksConfigCommandXXXXXXXX = aws eks update-kubeconfig --name eks-private --region ap-southeast-1 --role-arn arn:aws:iam::111111111111:role/EksPrivateCluster-clusteradminxxxxxxxx-xxxxxxxxxxxx EksPrivateCluster.eksGetTokenCommandXXXXXXXX = aws eks get-token --cluster-name eks-private --region ap-southeast-1 --role-arn arn:aws:iam::111111111111:role/EksPrivateCluster-clusteradminxxxxxxxx-xxxxxxxxxxxx EksPrivateCluster.eksMastersRoleArnXXXXXXXX = arn:aws:iam::111111111111:role/EksPrivateCluster-clusteradminxxxxxxxx-xxxxxxxxxxxx EksPrivateCluster.kubectlclientBastionHostXXXXXXXX = i-000000000aaaaaaaa(输出中的
XXXXXXXX为 CDK 自动生成的后缀,111111111111为示例账号 ID,实际部署时以真实输出为准。)
除了运行部署,你还可以用 Maven 运行单元测试来验证模板的合规性:
mvn testEksPrivateClusterStackTest.java 覆盖了四项关键断言:集群名为eks-private且版本为1.34、私有端点访问开启且公网端点关闭、不含 Internet Gateway、不含 NAT Gateway。
通过 SSM Session Manager 访问 EC2 实例
登录 AWS 控制台,进入EC2 服务,找到 CDK 创建的名为
kubectl-client的实例,点击Connect,选择Session Manager; 2.(可选)在 Session Manager Preferences 中配置默认 shell 为 bash,并设置 kubectl 别名与环境变量:/bin/bash alias k=kubectl export AWS_STS_REGIONAL_ENDPOINTS=regional cd $HOME其中
AWS_STS_REGIONAL_ENDPOINTS=regional与集群配置中写入处理器环境的值保持一致,确保 STS 走区域化端点,在纯隔离网络下尤为重要。
在隔离环境中安装工具与软件包
Docker
Amazon Linux 的软件仓库托管在 Amazon S3 桶中,因此即使在隔离环境中,从这些仓库安装或更新软件包也能正常工作(本次部署的实例正是 Amazon Linux,且 VPC 中配置了 S3 网关端点)。以安装 Docker 为例:
sudo yum update -y sudo amazon-linux-extras install docker sudo service docker startkubectl
对于kubectl这类不在系统仓库中的工具,需要先准备一个可从隔离环境访问的 S3 桶来存放二进制资产。你可以使用 AWS CloudShell 下载工具并上传到该资产桶,然后在kubectl-client实例中安装。CloudShell 侧的操作示例:
[cloudshell-user@ip-10-2-84-204 ~]$ curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.34.2/2025-11-13/bin/linux/amd64/kubectl [cloudshell-user@ip-10-2-84-204 ~]$ aws s3 cp kubectl s3://my-bucket/kubectl-1.34.2 upload: ./kubectl to s3://my-bucket/kubectl-1.34.2之后在kubectl-client实例中通过aws s3 cp(依赖已附加的AmazonS3ReadOnlyAccess托管策略)取回该二进制,并按 EKS 官方指南完成kubectl安装。实例角色已经具备 S3 只读权限,因此无需额外配置。
使用 kubectl 访问 EKS 集群
SSM 登录kubectl-client实例后,使用部署时记录的输出信息更新 kubeconfig:
[ssm-user@ip-10-0-0-240 bin]$ aws eks update-kubeconfig --name eks-private --region ap-southeast-1 --role-arn arn:aws:iam::111111111111:role/EksPrivateCluster-clusteradminxxxxxxxx-xxxxxxxxxxxx Added new context arn:aws:eks:ap-southeast-1:111111111111:cluster/eks-private to /home/ssm-user/.kube/config验证集群访问,查看节点与全部命名空间的 Pod:
[ssm-user@ip-10-0-0-240 ~]$ kubectl get nodes NAME STATUS ROLES AGE VERSION ip-10-0-0-60.ap-southeast-1.compute.internal Ready <none> 19h v1.34.2-eks-ecaa3a6 [ssm-user@ip-10-0-0-240 ~]$ kubectl get pods -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system aws-node-9sqcb 2/2 Running 2 (4h39m ago) 19h kube-system coredns-6787556b84-cg29g 1/1 Running 1 (4h39m ago) 19h kube-system coredns-6787556b84-q98ks 1/1 Running 1 (4h39m ago) 19h kube-system kube-proxy-m9ms4 1/1 Running 1 (4h39m ago) 19h从输出可以看到:工作节点版本为v1.34.2-eks-ecaa3a6,aws-node、coredns、kube-proxy等系统组件均正常Running,说明私有集群的控制面、节点注册与容器网络在无公网环境下运转正常。
清理资源
不再需要时,在示例根目录执行:
cdk destroy该命令会删除集群、VPC、VPC Endpoints、Lambda 函数及相关资源。需要留意的是,如果集群中创建了独立的负载均衡器或持久化存储卷(EBS/EFS),可能需要按依赖顺序额外处理。
实践提示小结
- 务必开启
placeClusterHandlerInVpc:否则 CDK 的集群处理器 Lambda 无法访问隔离子网内的私有集群,部署会失败; - 端点是按需扩展的:新增应用若依赖其他 AWS 服务(如 DynamoDB、Secrets Manager),需要补充对应的 VPC Endpoint,同时可在
addEndpoints()的端点列表中直接追加; - IAM 权限建议收敛:源码注释明确指出,生产环境应限制对特定仓库与桶的访问(如将
AmazonS3ReadOnlyAccess、ECR 权限收窄到具体资源),示例中使用托管策略是为了演示的便捷性; - 计费不可忽视:VPC Endpoint 按小时计费并叠加数据处理费,多可用区部署会放大成本,请结合架构高可用要求与预算权衡;
- 模板合规性可自动化验证:通过 EksPrivateClusterStackTest.java 的断言(无 IGW、无 NAT、私有端点、集群版本)即可在 CI 中守住"纯隔离"这一架构底线。
【免费下载链接】aws-cdk-examples
Example projects using the AWS CDK
相关推荐
网络隔离方案:terraform-aws-eks私有集群配置
网络隔离方案:terraform aws eks私有集群配置 还在为Kubernetes集群的网络安全隐患而烦恼?本文将为你详细介绍如何使用terraform
云原生IaC容器编排集群管理使用 AWS CDK(Java)部署 EKS Fargate 集群:从 VPC 搭建到 kubectl 验证的完整实战
使用 AWS CDK(Java)部署 EKS Fargate 集群:从 VPC 搭建到 kubectl 验证的完整实战 本指南以 aws cdk example
在AWS上使用EKS创建Kubernetes集群的完整指南
在AWS上使用EKS创建Kubernetes集群的完整指南 前言 Kubernetes作为目前最流行的容器编排平台,已经成为云原生应用开发的事实标准。AWS提供
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考