亚马逊AWS官方博客

提升 EKS 集群网络安全:Pod 自定义网络和 Karpenter 的协同应用

背景介绍

随着云计算和容器技术的普及,越来越多的业务选择部署在弹性容器服务(如 Amazon EKS)上。然而,这种部署方式也带来了新的挑战:如何在保持容器化应用的灵活性和可扩展性的同时,实现不同业务之间的网络隔离和安全性。

许多用户在使用 Amazon VPC(Virtual Private Cloud)时,为了实现更精细的网络隔离,通常希望为不同的业务划分独立的子网(subnet)。这种需求在处理敏感数据或运行关键业务的场景中尤为突出。然而,当这些业务都部署在 EKS 集群的容器中时,传统的节点与 Pod 共用子网的方式无法很好的实现网段的隔离,尤其是对其中需要与外部服务交互的 Pod 无法固定其私有 IP CIDR 区间。如何在这种动态环境中实现特定业务 Pod 的子网隔离,同时不影响其他 Pod 的默认网络规划,是我们要解决的问题。

Karpenter 是一个用于 Kubernetes 集群的自动伸缩工具,它可以根据实际需求自动调整节点数量和规格,以实现高性能和低成本的集群管理。

本文将探讨如何基于 EKS Pod 自定义网络和 Karpenter 来构建特定业务 Pod 的子网隔离方案,以满足用户对网络安全、业务隔离和系统灵活性需求。通过这种方法,我们旨在展示如何在云原生环境中实现更精细的网络控制,同时保持容器化的优势。

解决方案架构

基于我们在背景介绍中提到的需求,我们设计了一个基于 EKS Pod 自定义网络功能和 Karpenter 结合的解决方案,以实现特定业务 Pod 的子网隔离和 Node 快速扩缩容。以下是该解决方案的架构说明:

1. 网络规划

本文测试在新加坡区域(ap-southeast-1)的三个可用区(Availability Zones,简称 AZ)中建立了总计 9 个子网,包含以下三种类型的子网:

  • Node 子网:用于部署 EKS 工作节点,命名为 node1a, node1b, node1c
  • Pod 默认子网:用于常规 Pod 的网络需求,命名为 pod1a-default, pod1b-default, pod1c-default
  • Pod 自定义子网:专门用于承载特定业务 Pod 的网络需求,命名为 pod1a-custom, pod1b-custom, pod1c-custom

2. ENIConfig 配置

为了实现 Pod 网络的灵活配置,我们使用了基于 Amazon VPC CNI 插件的 ENIConfig 功能。ENIConfig 允许我们为不同的 Pod 指定不同的网络配置。具体内容请参考此处文档

默认 ENIConfig:

  • 为每个 AZ 创建了一个默认的 ENIConfig,,使用 AZ 的名称作为 metadata name,如 ap-southeast-1a
  • subnet 字段指向该 AZ 的 Pod 默认子网 id

自定义 ENIConfig:

  • 为每个 AZ 创建一个自定义 ENIConfig,这些 ENIConfig 使用特定的命名格式,如 custom-ap-southeast-1a
  • subnet 字段指向该 AZ 的 Pod 自定义子网 id

3. Karpenter NodePool 配置

为了让特定的 Pod 使用自定义子网,我们在 Karpenter NodePool 中使用了 annotations 来将 Pod 指定到子网中。

annotations:
  k8s.amazonaws.com/eniConfig: custom-ap-southeast-1b

当满足这个 NodePool 的业务在被 Karpenter 扩容创建新的 Node 时候,应该使用指定的自定义 ENIConfig。

4. 网络流程

根据架构图,我们可以看到 VPC 中的网络流程:

  • Node 通过 EC2NodeClass 和 NodePool 配置在 Node 子网中开启。
  • 常规 Pod 通过默认 ENIConfig 连接到 Pod 默认子网。
  • 特定业务 Pod 通过自定义 ENIConfig 连接到 Pod 自定义子网。

这种设计利用 Karpenter 的自动扩缩容能力,规范了 Node 子网位置和主 IP,同时做到了常规业务 Pod 和特定业务 Pod 的网络隔离,使我们可以根据对外访问需求灵活配置 Pod 子网网络。

服务部署

本文测试使用的配置文件可以在此处 Github 仓库下载。

1. 部署 VPC 网络

根据上面的 VPC 网络架构部署 VPC 和子网,分配好相应 CIDR 网段。如下图所示。

3 个 Node 子网网段分别为:10.0.0.1/24,10.0.0.2/24,10.0.0.3/24。

3 个 Pod 默认子网网段分别为:10.0.0.4/24,10.0.0.5/24,10.0.0.6/24。

3 个 Pod 自定义子网网段分别为:10.0.0.7/24,10.0.0.8/24,10.0.0.9/24。

2. 创建 EKS 集群

本文测试基于一个已经创建好的 EKS 集群,版本为 1.29。请确保您有足够的权限访问该集群,并且 kubectl 已正确配置。具体步骤可以参考此处文档。在创建集群时,选择 9 个子网,之后我们会通过 EC2NodeClass和NodePool 来控制 Node 落入的相应的 Node 子网。

3. 安装 Karpenter

本文测试中我们使用一个 managed node group 来安装 Karpenter,具体步骤可以参考此处文档

验证 Karpenter 安装完毕。

本文测试我们使用两个 Node 来部署 Karpenter,均来自于 managed node group,如下图所示。

4. 部署默认 ENIConfig,NodePool 和 EC2NodeClass

部署默认的 ENIConfig 资源。ENIConfig 是 EKS 自定义网络的配置文件,通过 Amazon VPC CNI 插件实现针对 Pod 的网络分离。

默认 ENIConfig 配置文件 sample 参考此处链接,此处使用 AZ 名称作为 metadata name,并配置上每个 AZ 中的 Pod 默认子网 id。这样在业务没有指定 ENIConfig 的情况下,启用的 Pod 会自动落入各个 AZ 的 Pod 默认子网中。值得注意的是,在启用 Pod 自定义网络时,我们要修改 Amazon VPC CNI 插件的参数,使默认 ENIConfig 被启用和识别。

kubectl set env daemonset aws-node -n kube-system AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=true
kubectl set env daemonset aws-node -n kube-system ENI_CONFIG_LABEL_DEF=topology.kubernetes.io/zone

用户可以替换对应的安全组和子网 id 完成部署。

部署默认的 EC2NodeClass 资源。 本文测试使用的 sample 参考此处链接。我们通过在子网中添加 Tag 的方式让 EC2NodeClass 指定了 Node 所在在子网。在前面部署的 3 个 Node 子网, 我们都需要配上对应的 Tag,本文测试我们使用了 karpenter.sh/discovery: testnet 作为 Node 子网的 Tag。同时在安全组上也标记了相同的 Tag 让 Karpenter 来识别。用户可以修改 sample,使用自己的 Tag 完成部署。

部署默认的 NodePool 资源。默认 NodePool 中定义了默认情况下的业务可以拉起的 Node 资源。默认 NodePool sample 参考此处链接。本文测试中我们采取默认 NodePool 覆盖 3 个 AZ 的方式。nodeClassRef 指向上面的默认 EC2NodeClass。用户可以根据自己情况修改其中参数。

      nodeClassRef:
        apiVersion: karpenter.k8s.aws/v1beta1
        kind: EC2NodeClass
        name: default

部署三个资源,并检查部署完成。

kubectl apply -f eniconfig-default.yaml
kubectl apply -f default-nodeclass.yaml
kubectl apply -f default-nodepool.yaml

5. 部署定制的 ENIConfig,NodePool 和 EC2NodeClass

在我们的场景中,同一个 EKS cluster 中的某些特定业务需要单独拥有自己的 Pod 子网,来做好这些特定业务上对外交互的网络管理,比如需要给出这些 Pod 网段的 IP 白名单到其他服务。我们将这些业务的 Pod IP 通过自定义 ENIConfig 分配到单独的子网中。

部署自定义 ENIConfig。自定义 ENIConfig sample 参考此处链接。Sample 中我们按照子网和 AZ 配置了 3 个自定义 ENIConfig,对应上面三个 Pod 自定义子网。 用户可以修改其中安全组和子网 id 完成部署。

部署定制 EC2NodeClass。定制 EC2NodeClass sample 参考此处链接。在定制 EC2NodeClass 中,我们使用子网 Tag 来规范 Node 选择的子网。部署 3 个 EC2NodeClass 对应三个不同的 Node 子网。用户可以修改其中 subnetSelectorTerms 选择自己设置的子网 Tag 完成部署。

  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: testnet
        Name: node1a

部署定制的 NodePool。定制 NodePool sample 参考此处链接。3 个定制 NodePool 对应 3 个定制 EC2NodeClass,同时使用 annotation 的方式在 Node 启动后直接使用指定的 ENIConfig 配置,达到自动关联特定 Pod 网络的目标。

    metadata:
      labels:
        type: karpenter
        azsubnet: custom-1a
      annotations:
        k8s.amazonaws.com/eniConfig: custom-ap-southeast-1a

部署三个资源,并检查部署完成

kubectl apply -f eniconfig-custom.yaml
kubectl apply -f custom-nodeclass.yaml
kubectl apply -f custom-nodepool.yaml

测试

部署完成后,我们可以使用 sample 仓库中测试脚本进行测试。

kubectl apply -f testdefault.yaml

测试默认 deployment,我们可以看到 Deployment Pod 被分配在 Pod 默认子网中,Karpenter 启动的 Node 也在对应的 Node 子网中。

kubectl apply -f testcustom-1a.yaml  
kubectl apply -f testcustom-1b.yaml  
kubectl apply -f testcustom-1c.yaml  

测试 custom deployment. 在 sample 中我们使用了 nodeSelector 来指定 deployment 需要的 Node,来对应 Karpenter 启用的 NodePool 资源,NodePool 中设置的 annotation 对应的 ENIConfig 被同时启用,将这些特定的 Pod 归入 Pod 自定义子网中。

      nodeSelector:
        azsubnet: custom-1c

从 Pod IP 我们可以看到 Pod 被分配到对应的自定义子网中。

从 Console 上面我们可以看到 pod 自定义子网的 IP 被使用情况。

总结

本文深入探讨了如何在 Amazon EKS 环境中实现 Pod 级别的网络隔离,同时保持容器化环境的灵活性和可扩展性。

我们提出的解决方案结合了 EKS 的自定义网络功能和 Karpenter 的自动扩缩容能力,创造出一个既安全又灵活的网络架构。通过设计多层次的网络结构,包括 Node 子网、默认 Pod 子网和自定义 Pod 子网,我们为不同类型的工作负载提供了独立的网络环境。利用 ENIConfig 的灵活性,我们实现了对 Pod 网络的精细化控制,使特定业务的 Pod 能够使用专用子网,从而提高了整体系统的安全性。Karpenter 的集成则确保了节点的自动扩缩容与 Pod 网络配置的无缝协作。

参考资料

本篇作者

曾鑫

亚马逊云科技解决方案架构师,负责基于 AWS 云平台的解决方案咨询和设计,对 AWS 安全产品的使用与整合有多年实践经验。

刘佳

亚马逊云科技解决方案架构师,负责基于 AWS 云计算客户方案咨询和架构设计,在金融领域有着多年的数据中心虚拟化解决方案经验。