亚马逊AWS官方博客

在中国区 EKS 上部署和使用 Multus CNI

亚马逊 Elastic Kubernetes 服务(Amazon EKS)在 2021 年 8 月 2 日宣布支持 Multus 容器网络接口 (CNI)插件,使客户能够连接多个网络接口并将高级网络配置应用于基于 Kubernetes 的应用程序。有了 Multus 的可用性,通信服务提供商和其他具有独特联网要求的客户可以配置他们的 EKS 群集,以运行连接到多个接口的多宿主 Kubernetes pod。

在本文中,我们将介绍 Multus CNI,涵盖适用的使用案例,并介绍如何在中国宁夏和北京区部署和设置 Multus CNI。

什么是 Multus CNI?

CNI 是容器网络接口,它提供了应用程序编程接口,用于在容器中配置网络接口。Multus CNI 是 Kubernetes 的容器网络接口插件,可将多个网络接口连接到 Pod。在 Kubernetes 中,默认情况下,每个 Pod 只有一个网络接口,但本地环回地址除外。使用 Multus,您可以创建具有多个接口的多宿主 Pod。Multus 充当 “meta” 插件,可以调用其他 CNI 插件来配置其他接口。

Pod 的多个网络接口在各种用例中都很有用,包括:

  • 流量分割:运行网络功能(NF),需要将控制/管理和数据/用户平面网络流量分离以满足低延迟服务质量(QoS)要求。
  • 性能:其他接口通常利用专门的硬件规格,例如单根 I/O 虚拟化(SR-IOV)数据平面开发套件 (DPDK),它们绕过操作系统内核以提高带宽和网络性能。
  • 安全性:支持具有严格流量隔离要求的多租户网络。 将多个子网连接到 Pod 以满足合规性要求。

多宿主 Pod

Multus CNI 插件允许 Pod 在 Kubernetes 中有多个接口。 Multus 的当前版本的 EKS 支持捆绑了 Amazon VPC CNI 作为默认委托插件(这是唯一受支持和验证的默认委托插件)。 默认委托插件为 Pod 配置主网络接口(eth0)以启用 Kubernetes 控制平面流量,包括 Pod 的主网络接口的 IP 地址管理(IPAM)。

以下是多宿主 Pod 如何在 AWS 上工作的示例。下图显示了两个具有两个网络接口的 pod,即 eth0 和 net1。在这两种情况下,Amazon VPC CNI 都管理 pod eth0(默认委托给 Multus)。接口 net1 由 Multus 通过 pod1 的 ipVLAN CNI 插件进行管理,该插件处理与 k8s 制平面流量分隔的用户平面(例如:语音、视频)流量。当 pod2 net1 通过主机设备 CNI 插件连接到主机弹性网络接口时,能够使用 DPDK 加速数据包处理。

现在让我们来看看如何在 Amazon EKS 群集上设置 Multus CNI。我们将通过对两个示例应用程序进行简单 ping 测试来演示流量分割场景。我们将设置 ipVLAN CNI 来管理 Pod 的辅助接口。ping 测试将在 ipvlan插件提供并由 Multus 管理的网络执行。

在此设置中,我们使用 CloudFormation 模板创建基础设施、EKS 群集和自管理的节点组。基础设施模板为集群和 Multus 配置了 Amazon 虚拟私有云(VPC)、公有和私有子网,以及执行 EKS 操作的堡垒主机。节点模板创建附加了附加 ENI 的工作节点以运行多宿主 Pod。这两个 AWS CloudFormation 模板一起创建了以下资源:

  • 基础设备创建模板
    • VpcCidr:将用于部署的 VPC CIDR。
    • AvailabilityZones根据 EKS 要求,至少有两个可用区。
    • PublicSubnet1/2:这些子网将托管堡垒主机以运行 kubectl 命令。 此外,这将托管 NAT-GW,以便为私有子网提供互联网访问。
    • PrivateSubnetaz1/2:AZ1 和 AZ2 中 EKS 控制平面的子网。
    • MultusSubnet1az1/2:Multus 将用于在示例 pod 中创建辅助接口的第一个子网。
    • MultusSubnet2az1/2:Multus 将用于在示例 pod 中创建辅助接口的第二个子网。
    • BastionInstance:我们可以从中运行 EKS 集群操作的堡垒主机(kubectl)。
    • eksCluster:将运行示例工作负载的 EKS 群集。
  • EKS 工作节点组创建模板
    • NodeGroup:用于运行示例 pod 的工作节点组。
    • lambdaAttachcNI:用于将其他 Multus 子网附着到工作节点的 Lambda 函数。
    • eventBridgeEventRule:CloudWatch 事件规则,用于监控实例向上和向下扩展以触发 Lambda 挂钩以将 Multus 子网附加额外的弹性网络接口(ENI)到工作节点组。

准备工作

  • 具有管理员权限的 AWS 账户:对于此博客,我们将假设您已经拥有具有管理员权限的 AWS 账户。
  • 使用 AWS 管理控制台创建 EC2 密钥对(EC2 用户指南中提到了这些步骤)。
  • 命令行工具:mac/Linux 用户需要在其工作站上安装最新版本的 AWS CLI、aws-iam-身份验证器和 git。 而 Windows 用户可能希望在 AWS 中使用 Cloud9 环境,然后在他们的 Cloud9 环境中安装这些 CLI。
  • 要开始进行 Multus 安装,请在本地工作站或 Cloud9 实例上克隆 eks-install Guide for multus github 存储库。

在克隆了仓库的本地文件夹中找到 eks-install-guide-for-multus/cfn/templates/cfn/nodegroup/lambda_function.zip 文件。在 AWS 管理控制台中导航到 S3,然后创建名为 eks-mulus-cluster(此为示例命名。请根据实际情况输入唯一的 S3 存储桶名字) 的存储桶。 选择与 EKS 集群相同的 AWS 区域,然后将其余区域保留为默认值。 点击新组建的存储桶上的 “上传”。 点击 “添加文件”。 上传 lambda_function.zip。当运行 CloudFormation 模板进行节点组创建时,S3 存储桶名称将用作输入参数之一。

步骤 1:创建 VPC 和 EKS 群集

运行 CloudFormation 以创建基础设施

  1. 使用管理员权限登录 AWS 控制台,然后转至 CloudFormation。
  2. 点击创建堆栈 → 使用新资源(标准)。
  3. 上传模板文件,然后选择 “eks-install-guide-for-multus/cfn/templates/infra/eks-infra_cn.yaml
  4. 输入堆栈名称 ‘eks-multus-cluster‘,堆栈名称也将是 EKS 集群名称。
  5. 从下拉菜单中选择两个可用区。
  6. 使用默认 VPC CIDR(10.0.0.0/16)块和子网范围。
  7. 选择堡垒实例类型(默认情况下,t3-medium 也可以)。
  8. 从下拉菜单中选择 EC2 密钥对名称。
  9. 在本练习中,您可以使用默认的 AMI ID。
  10. 点击下一步 → 我确认 → 创建堆栈。

等待 CloudFormation 堆栈完成(创建完成)。 我们创建了一个 Amazon VPC,其中包含两个公有和私有 EKS 子网、四个 Multus 子网(每个可用区两个)、一个 EKS 群集、IGW 和 NAT-GW。 该堆栈还为 Multus 子网和控制平面创建了安全组。

记录 EksCluster,EksControlSecurityGroup,PrivateSubnetAz1/2,MultusSubnet1/2Az1/2,MultusSecurityGroup,VpcId 的 CloudFormation 控制台输出。此外,还可以从基础堆栈输出中记录 BastionPublicIP。您将在下一节中需要此 IP。

堡垒主机配置

要使用 SSH 连接到堡垒,请执行以下操作:

在终端窗口中,使用 ssh 命令连接到实例。 指定私钥(.pem)的路径和文件名,要连接到您的实例,请输入以下命令。

ssh -i /path/my-keyy-pair.pem ec2-user @BastionPublicIp

你会看到类似以下内容的回复:

The authenticity of host 'ec2-198-51-100-1.compute-1.amazonaws.com (198-51-100-1)' can't be established.
ECDSA key fingerprint is l4UB/neBad9tvkgJf1QZWxheQmR59WgrgzEimCG6kZY.
Are you sure you want to continue connecting (yes/no) 

输入 yes

要配置 AWS CLI:

您可以使用临时证书来运行与管理员配置文件关联的 AWS CLI 命令。 你将使用自己的密钥(下面的密钥不适用于你的环境)。

使用以下命令确认凭据。

export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEX...
export AWS_DEFAULT_REGION=ap-northwest-1

运行如下命令:

aws sts get-caller-identity

输出如下:

{
"Account": "my-account",
"UserId": "CROAY6YKLVK242KL6D5W3:my-user-id",
"Arn": "arn:aws:sts::my-account:assumed-role/Admin/my-user-id"
}

安装和配置 kubectl:

curl -o kubectl https://amazon-eks.s3-us-west-2.amazonaws.com/1.21.2/2021-07-05/bin/linux/amd64/kubectl
curl -o kubectl.sha256 https://amazon-eks.s3.us-west-2.amazonaws.com/1.21.2/2021-07-05/bin/linux/amd64/kubectl.sha256
openssl sha1 -sha256 kubectl
chmod +x ./kubectl
mkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$PATH:$HOME/bin
echo 'export PATH=$PATH:$HOME/bin' >> ~/.bashrc
kubectl version —short —client

要使用 AWS CLI 创建 kubeconfig 文件,请执行以下操作:

aws eks update-kubeconfig --name eks-multus-cluster
kubectl get svc

输出:

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 172.20.0.1 <none> 443/TCP 31m

步骤 2:创建节点组

这一步,我们将创建自管理的节点组。 此步骤要求在准备工作下创建的 S3 存储桶以及基础设施堆栈才能完成。

运行 CloudFormation 模板进行节点组创建

  1. 使用管理员权限登录 AWS 管理控制台,然后转至 CloudFormation。
  2. 点击创建堆栈 → 使用新资源(标准)。
  3. 指定模板,然后上传模板文件。
  4. 从您的本地仓库位置选择 eks-install-guide-for-multus/cfn/templates/eks-nodegroup-multus_cn.yaml 文件。
  5. 指定先前创建堆栈的群集名称和 ClusterControl Plane 安全组。
  6. 输入堆栈名称 multus-cluster-ng01。
  7. 输入 multus-cluster-ng01作为节点组名称。
  8. 为节点自动扩缩组指定 1 所需容量、最大和最小大小。
  9. 选择 c5.large 作为实例类型,选择 20 作为卷大小。
  10. 使用默认节点 ImageID SSM 参数并留空以使用默认的 EKS AMI
  11. 选择准备工作下创建的 EC2 密钥对。
  12. 您可以使用引导程序的默认参数。
  13. 选择 vpc-eks-multus-clusterVPC ID。
  14. 选择要创建 Worker 的 EKS  subnet(privateAz1-eks-multus-cluster
  15. 指定 Multus subnet(multus1Az1-eks-multus-cluster, multus2Az1-eks-multus-cluster
  16. 输入 Multus security group: eks-multus-cluster-MultusSecurityGroup*
  17. 将 lambda S3 存储桶名称指定为 eks-multus-cluster(此为示例命名。请根据实际情况输入已经创建的唯一的 S3 存储桶名字),将 S3  key 指定为 Lambda_function.zip
  18. 点击下一步 → 我确认 → 创建堆栈。

等待 CloudFormation 堆栈完成(创建完成)。要从定义的 Multus 子网附加 ENI,节点组堆栈部署了 AWS Lambda 函数和 Amazon CloudWatch 事件规则。堆栈启动的 EC2 实例,其中附加了来自 Multus 子网的 ENI 以及标签 no_Manage: true。AWS VPC CNI 不会管理 ENI 标记为 no_Manage: true。对于 Multus 来说,这是管理 Pod 的其他网络的必备步骤。

从 CloudFormation 控制台输出记录 NodeInstancerRole

应用 K8s configMap 更新

登录堡垒主机,在那里你可以运行 kubectl 命令。

下载、编辑和应用 AWS 身份验证器配置映射:

curl -o aws-auth-cm.yaml https://s3.us-west-2.amazonaws.com/amazon-eks/cloudformation/2020-10-29/aws-auth-cm.yaml

使用你最喜欢的文本编辑器打开文件。 将 “rolearn” 替换为 NodeInstancerRole(来自工作节点组 CloudFormation 堆栈的输出)并保存文件。 不要修改此文件中的任何其他行。

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - rolearn: arn:aws:iam::my-account:role/worker-nodegroup-01-NodeInstanceRole-1M3F6VK25IKB0
      username: system:node:{{EC2PrivateDNSName}}
      groups:
        - system:bootstrappers
        - system:nodes

应用配置。 此命令可能需要几分钟才能完成。

kubectl apply -f aws-auth-cm.yaml

检测节点的状态并等待它们进入就绪状态。

kubectl get nodes --watch

步骤 3:安装和配置 Multus

使用 daemonset 安装 Multus CNI

登录堡垒主机,在那里你可以运行 kubectl 命令。

运行以下命令以下载并安装 Multus daemonset。 此命令将 AWS VPC CNI 配置为 Multus CNI 的默认委托插件。

kubectl apply -f https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/master/config/multus/v3.7.2-eksbuild.1/aws-k8s-multus.yaml

通过运行以下命令验证部署。 每个节点应该有一个名为 kube-mulus-ds 的 pod 。

kubectl get pods -n kube-system

创建附加接口

接下来,我们将为我们添加到 pod 中的每个额外接口创建配置。 Multus 提供了名为 NetworkAttachmentDefinition 的自定义资源定义(CRD)。我们将使用此 CRD 来构建额外的接口设置。

创建 ipvlan-conf-1

使用 ipvlan CNI 为容器配置额外的接口(来自 Multus 子网 10.0.4.0/24)。 将配置应用于群集。

cat <<EOF | kubectl apply -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: ipvlan-conf-1
spec:
  config: '{
      "cniVersion": "0.3.0",
      "type": "ipvlan",
      "master": "eth1",
      "mode": "l3",
      "ipam": {
        "type": "host-local",
        "subnet": "10.0.4.0/24",
        "rangeStart": "10.0.4.70",
        "rangeEnd": "10.0.4.80",
        "gateway": "10.0.4.1"
      }
    }'
EOF

创建 ipvlan-conf-2

为第二个 Multus 子网 (10.0.6.0/24) 创建另一个 ipVLAN CNI 。 将配置应用于群集。

cat <<EOF | kubectl apply -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: ipvlan-conf-2
spec:
  config: '{
      "cniVersion": "0.3.0",
      "type": "ipvlan",
      "master": "eth2",
      "mode": "l3",
      "ipam": {
        "type": "host-local",
        "subnet": "10.0.6.0/24",
        "rangeStart": "10.0.6.70",
        "rangeEnd": "10.0.6.80",
        "gateway": "10.0.6.1"
      }
    }'
EOF

通过运行以下命令验证配置。

kubectl describe network-attachment-definitions

要了解有关网络附件的配置选择和示例的更多信息,请参阅 Multus 的使用指南

步骤 4:部署示例应用

使用单个 ipvlan 附件部署示例应用程序

登录可以运行 kubectl 命令的堡垒主机。

使用我们在上一步中创建的网络注释创建示例应用程序样例 sampleapp-1

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: sampleapp-1
  annotations:
      k8s.v1.cni.cncf.io/networks: ipvlan-conf-1
spec:
  containers:
  - name: multitool
    command: ["sh", "-c", "trap : TERM INT; sleep infinity & wait"]
    image: praqma/network-multitool
EOF

使用以下命令验证 pod 网络。

$ kubectl exec -it sampleapp-1 -- ip -d address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 minmtu 0 maxmtu 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
3: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
    link/ether 82:62:21:8a:b5:98 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0 minmtu 68 maxmtu 65535
    veth numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 10.0.2.215/32 scope global eth0
       valid_lft forever preferred_lft forever
4: *net1@eth0*: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether 06:e4:d4:62:f4:6d brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535
    ipvlan  mode l3 bridge numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 10.0.4.70/24 brd 10.0.4.255 scope global net1
       valid_lft forever preferred_lft forever

AWS VPC CNI 管理接口 eth0,而 ipvlan CNI 通过 Multus 网络附件定义(ipvlan-conf-1)管理接口 net1。

使用双 ipvlan 附件部署示例应用程序

使用双网络注释 ipvlan-conf-1ipvlan-conf-2 创建示例应用程序(sampleapp-dual)

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: sampleapp-dual
  annotations:
      k8s.v1.cni.cncf.io/networks: ipvlan-conf-1, ipvlan-conf-2
spec:
  containers:
  - name: multitool
    command: ["sh", "-c", "trap : TERM INT; sleep infinity & wait"]
    image: praqma/network-multitool
EOF

使用以下命令验证 pod 网络。

$ kubectl exec -it sampleapp-dual -- ip -d address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
3: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP
    link/ether f6:45:bf:ea:22:06 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0
    veth
    inet 10.0.2.208/32 scope global eth0
       valid_lft forever preferred_lft forever
4: *net1@eth0*: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 06:e4:d4:62:f4:6d brd ff:ff:ff:ff:ff:ff promiscuity 0
    ipvlan
    inet 10.0.4.76/24 brd 10.0.4.255 scope global net1
       valid_lft forever preferred_lft forever
5: *net2@net1*: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 06:c2:b5:a5:08:c5 brd ff:ff:ff:ff:ff:ff promiscuity 0
    ipvlan
    inet 10.0.6.55/24 brd 10.0.6.255 scope global net2
       valid_lft forever preferred_lft forever

测试 pods 之间的 mutlus 接口网路连通性:

$ kubectl exec -it sampleapp-dual -- ping -I net1 <sampleapp-net1-ipaddress>
PING 10.0.4.77 (10.0.4.77) from 10.0.4.76 net1: 56(84) bytes of data.
64 bytes from 10.0.4.77: icmp_seq=1 ttl=255 time=0.034 ms
64 bytes from 10.0.4.77: icmp_seq=2 ttl=255 time=0.025 ms
64 bytes from 10.0.4.77: icmp_seq=3 ttl=255 time=0.023 ms
64 bytes from 10.0.4.77: icmp_seq=4 ttl=255 time=0.028 ms

恭喜成功部署 Multus CNI 并运行示例应用程序。 你可以参考 Multus 快速入门指南,了解适用于 pod 的完整注释选项。

清理

为避免将来产生费用,您可以删除使用 CloudFormation 服务创建的所有资源。 登录 AWS 管理控制台,导航到 CloudFormation,然后逐个删除堆栈(按工作节点堆栈和基础设施堆栈的顺序)。

结论

在这篇博客文章中,我们介绍了 Multus CNI 及其可能的使用案例。我们还使用 Multus CNI 创建了 Amazon EKS 集群,并为示例 Pod 配置了额外的网络定义,以展示流量分割。

通过使用 AWS 云开发工具包(CDK)和 AWS CodePipeline,或者通过 API 集成使用第三方工具和编排器,可以进一步实现这一标准化流程的自动化。有关 CDK 示例,请参阅 aws-Sample Git 仓库。此外,请访问 Amazon EKS 用户指南,了解 Multus 安装说明和最近的产品改进。

请注意,目前对 Multus CNI 的支持不包括对 pod 辅助网络接口或其他高阶接口的本机配置支持。这包括工作节点上 Multus 托管接口的 IPAM(ENI 标记为 no_Manage: true)以及与更高阶 pod 接口关联的 CNI 驱动程序和配置。在我们继续为 EKS 客户提供 Multus 体验的同时,请在 GitHub 上提供的 AWS 容器路线图上提供反馈并建议新功能。

有关更多信息,请查看此博客文章,了解如何使用 Open5G 和 Amazon EKS 使用 Multus CNI 构建 5G 移动核心网络。

本篇作者

赵康

亚马逊云科技解决方案架构师,专注于 telco 以及相关的领域云架构设计和咨询。