亚马逊AWS官方博客

如何在Amazon EKS中部署SR-IOV设备插件

前言

随着云技术和容器的发展,越来越多的基于容器集群的通信负载开始在公有云上运行,通信负载对网络性能要求极高,因此广泛使用SR-IOV技术来提升网络的吞吐量和减少抖动,同时降低应用的CPU开销。

SR-IOV 的全称是 Single Root I/O Virtualization,SR-IOV标准允许在虚拟机之间高效共享PCIe(快速外设组件互连)设备,由于它是在硬件中实现的,所以可以获得能够与本机性能接近的I/O性能。SR-IOV 使用 physical functions (PF) 和 virtual functions (VF) 为 SR-IOV 设备管理全局功能。PF 包含SR-IOV 功能的完整PCIe设备,PF 作为普通的PCIe 设备被发现、管理和配置 。PF 通过分配VF 来配置和管理 SR-IOV 功能。

VF 是轻量级 PCIe 功能(I/O 处理)的 PCIe 设备,每个 VF 都是通过 PF 来生成管理的,VF 的具体数量限制受限于 PCIe 设备自身配置及驱动程序的支持,启用SR-IOV后,主机将在一个物理NIC上创建单个PF和多个VF。 VF的数量取决于配置和驱动程序支持。

Amazon EKS 是一项托管服务,借助该服务,您可以轻松在 AWS 上运行 Kubernetes,而无需安装和操作您自己的 Kubernetes 控制面板或工作线程节点。Kubernetes 会将容器安排到各个逻辑分组以便进行管理和查找,然后在 Amazon Elastic Compute Cloud (Amazon EC2) 实例集群上启动它们。

本文介绍如何在亚马逊云科技EKS中安装SR-IOV设备插件,从而可以实现通信负载不需要进行部署文件修改就可以在云上进行部署,同时可以在Pod层面进行灵活配置EC2的接口IP地址。

SR-IOV网络设备插件介绍

SR-IOV网络设备插件可以帮助K8s对宿主机的网络资源SR-IOV的虚拟功能(VF)和PCI物理功能(PF)进行发现和宣告。

该插件可以实现如下功能:

  • 支持基于内核和命名空间(UIO and VFIO) 的设备的驱动
  • 允许使用K8S的selector进行资源分组
  • 允许用户可以自由定义资源名称
  • 检测Kubelet重启并且自动重注册
  • 检测链路状态并相应地更新VFs资源状态
  • 以最小的开销支持新的设备类型。

使用该插件部署负载的时候,需要和其他的CNI一起使用:

(1)支持基于设备的网络开通的CNI插件,譬如Multus ,或者DANM插件

该插件可以获取Pod的已经分配的网络设备的信息。

(2)具备消费分配给Pod的网络设备的CNI,譬如SR-IOV CNI,VPC CNI

该插件可以在Pod创建的时候,使用插件已知的信息,将分配好的SR-IOV的VF注入到Pods网络的命名空间。在Pod删除的时候,重置并释放VF信息。

部署SR-IOV设备插件前提条件:

  • 一个已经创建成功的EKS集群,使用EC2作为worker,并且可以ssh登录
  • EKS集群上安装了Multus插件,并且使用EC2作为计算资源。EC2上创建多网口,可以参考使用如下blog提供的方案:

https://thinkwithwp.com/cn/blogs/containers/amazon-eks-now-supports-multus-cni/

  • 本机安装kubectl软件,可以参考如下链接进行安装:

https://docs.thinkwithwp.com/eks/latest/userguide/install-kubectl.html

部署SR-IOV网络设备插件步骤

(1)创建ConfigMap,运行如下命令:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: sriovdp-config
  namespace: kube-system
data:
  config.json: |
    {
        "resourceList": {
                "resourceName": "sriov_dpdk_n3",
                "selectors": {
                    "vendors": ["1d0f"],
                    "devices": ["ec20"],
                    "drivers": ["ena"],
                    "pciAddresses": ["0000:00:06.0"]
                }
            }            
    }
EOF

其中:

  • resourceName可以根据实际配置命名
  • vendors 和drivers请不要修改
  • drivers,可以填ena,表示直接使用ena网卡
  • pciAddresses需要根据实际要配置的网络接口填写,登录到Worker的EC2,运行dmesg命令,寻找如下关键词:
ena 0000:00:06.0: Elastic Network Adapter (ENA) found at mem febf4000, mac addr 0a:e1:52:6c:01:9d

根据mac地址(0a:e1:52:6c:01:9d)可以匹配到相应的PciAddresses(0000:00:06.0),我们还需要匹配接口名称和mac地址,运行如下命令:

ifconfig | grep -3 '0a:e1:52:6c:01:9d'
eth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9001
        inet 172.31.12.46  netmask 255.255.240.0  broadcast 172.31.15.255
        inet6 fe80::8e1:52ff:fe6c:19d  prefixlen 64  scopeid 0x20<link>
        ether 0a:e1:52:6c:01:9d  txqueuelen 1000  (Ethernet)
        RX packets 1133569  bytes 320227183 (305.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1291997  bytes 799804159 (762.7 MiB)

这样我们就得到PciAddresses 0000:00:06.0 对应的接口是eth2。该对应信息在创建网络和测试pod的时候需要。

(2)创建daemonset

从如下链接获取sriovdp-daemonset.yam文件:

https://github.com/k8snetworkplumbingwg/sriov-network-device-plugin/blob/master/deployments/k8s-v1.16/sriovdp-daemonset.yaml

运行:

kubectl apply -f  sriovdp-daemonset.yaml

运行成功后,运行如下命令:

kubectl get nodes
kubectl  describe node ip-192-168-2-97.cn-northwest-1.compute.internal

其中ip-192-168-2-97.cn-northwest-1.compute.internal 是由kubectl get nodes得到的

我们可以看到如下信息,SR-IOV设备就成为了pod的一个可以分配的网络资源

Allocatable:
  attachable-volumes-aws-ebs:         25
  cpu:                                                 15890m
  ephemeral-storage:                        18242267924
  hugepages-1Gi:                              8Gi
  intel.com/sriov_dpdk_n3:               1
  memory:                                         20676900Ki
  pods:                                              234

使用和验证SR-IOV设备插件

使用SRIOV插件,首先我们需要创建一个NetworkAttachmentDefinition,修改并运行如下命令,其中:

  • Type填入host-device
  • device填入我们使用的EC2的网络接口名字,示例为eth2
  • PciBusID填入网络接口对应的PciBusID
cat <<EOF | kubectl apply -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: sriov-net1-n3
spec:
  config: '{
      "cniVersion": "0.3.1",
      "type": "host-device",
      "device": "eth2",
      "pciBusID": "0000:00:06.0"
    }'
EOF

创建一个testpod,指定网络设备为我们创建的NetworkAttachmentDefinition: sriov-net1-n3,运行如下命令:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: testpod1
  annotations:
    k8s.v1.cni.cncf.io/networks: sriov-net1-n3
spec:
  containers:
  - name: test-ubuntu
    image: public.ecr.aws/ubuntu/ubuntu:latest
    securityContext:
      allowPrivilegeEscalation: true
      capabilities:
        add: ["NET_ADMIN", "SYS_TIME"]
    imagePullPolicy: IfNotPresent
    command: ["sh", "-c", "trap : TERM INT; sleep infinity & wait"]
    args: [ "while true; do sleep 300000; done;" ]
EOF

下面我们来测试一下这个接口,首先登录到我们创建的Pod,运行如下命令:

Kubectl exec -it testpod1 -- sh

我们还需要安装一些工具进行IP配置以及测试:

apt-get update
apt-get install net-tools
apt-get install iputils-ping
apt-get install 
apt-get install sudo

运行:

ifconfig -a

其中eth0是EKS给pod创建的默认接口,这里可以看到我们创建的SR-IOV接口net1:

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9001
        inet 192.168.2.7  netmask 255.255.255.255  broadcast 0.0.0.0
        ether 56:96:46:56:54:4a  txqueuelen 0  (Ethernet)
        RX packets 9823  bytes 23050026 (23.0 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 7280  bytes 517793 (517.7 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

net1: flags=4098<BROADCAST,MULTICAST>  mtu 9001
        ether 02:b1:a5:4e:52:ea  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

此时,如果我们登录到该pod对应的EC2上运行ifconfig,会发现对应的接口eth2已经不可见,已经被我们的testpod独占。

运行以下命令,可以给net1配置IP地址(此处以192.168.2.111为例),因为eth2被Pod独占,因此我们可以给pod配置任意的在eth2所关联的子网的IP地址。如果配置的IP地址和EC2的eth2不一样,我们必须要关闭EC2的 Source / destination check才可以使该IP地址生效:

ifconfig net1 192.168.2.111 
sudo ifconfig net1 up

配置完毕,运行如下命令进行测试:

# ping 192.168.2.1 -I net1
PING 192.168.2.1 (192.168.2.1) from 192.168.2.111 net1: 56(84) bytes of data.
64 bytes from 192.168.2.1: icmp_seq=20 ttl=255 time=0.058 ms

看到ping回复包,表示配置成功。

总结:

本文详细介绍如何在EKS上部署SR-IOV设备插件,介绍了如何使用该插件,如何设置参数,以及如何获取相应的参数和它们之间的对应关系,通过部署SR-IOV设备插件,使用SR-IOV的应用可以平滑从其他的云平台迁移到亚马逊云平台以及EKS。

本篇作者

赵康

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

莫梓元

亚马逊云科技解决方案架构师,有着超过5年的云计算领域从业经验,工作中担任过解决方案、售前、研发、实施、运维等多种角色。加入 AWS 之前,作为技术骨干负责中国联通基于 OpenStack 的沃云平台自研工作。