使用Nvidia加速网络计算进行CNF加速评估的技术预览
创建于2022年8月14日 范围 本文档提供了在K8s集群环境中通过NAT和路由网络应用加速来评估Nvidia加速网络计算技术的背景和指南。
文档目录
创建于2022年8月14日
范围
本文档提供了在K8s集群环境中通过NAT和路由网络应用加速来评估Nvidia加速网络计算技术的背景和指南。
缩写和缩略语
| 术语 | 定义 | 术语 | 定义 |
|---|---|---|---|
| PF | 物理功能 | DPDK | 数据平面开发套件 |
| VF | 虚拟功能 | CNI | 容器网络接口 |
| SRIOV | 单根输入输出虚拟化 | RDMA | 远程直接内存访问 |
| VPP | 向量包处理 | NIC | 网络接口 |
| PMD | 轮询模式驱动 | DAC | 直连铜缆 |
| DUT | 被测设备 | TG | 流量生成器 |
| MPPS | 百万包每秒 | CNF | 云原生网络功能 |
| CRD | 自定义资源定义 | SDK | 软件开发工具包 |
| K8s | Kubernetes | NAT | 网络地址转换 |
| TP | 技术预览 | SW | 软件 |
| VNF | 虚拟网络功能 | NF | 网络功能 |
参考文献
- kubernetes
- kubespray
- Nvidia Network ConnectX-6 DX
- Nvidia Network DPDK PMD
- Cisco Trex - traffic generator
- TRex in a few steps using Nvidia ConnectX adapters
- DPDK RTE_FLOW
- FD.io VPP processing platform
引言
云计算是现代数据中心(包括电信数据中心)的首选架构。在此架构下,网络功能(NF)作为虚拟化或容器化网络功能(CNF或VNF)运行。
NF必须跟上5G和AI等技术带来的吞吐量、消息速率以及低且可预测的延迟的日益增长的需求。作为云应用,NF高度依赖于服务器的CPU,而CPU是通用型的,并未针对网络应用进行优化。因此,NF可能需要大量CPU核心才能达到预期性能,导致实现效率极低,甚至可能完全无法满足预期性能。
加速网络计算是NVIDIA的一项技术,允许使用位于数据处理单元(DPU)或智能网卡上的专用应用加速器来加速NF。NF的应用逻辑(或控制平面)继续在主机CPU上运行,而NF的数据平面通过一组定义良好的API进行加速,从而实现了前所未有的效率和性能。加速网络计算支持云和云原生环境,并可与K8s等现代编排框架在多租户环境中使用。
本文档演示了如何使用加速网络计算来加速一个示例CNF。该示例CNF实现了一个路由器应用,该应用在k8s云环境中由ConnectX-6 Dx智能网卡加速,并使用通用的标准DPDK API。
我们将把加速CNF的性能和效率与基于流行向量包处理(VPP)框架的同一CNF的软件实现进行比较。
加速CNF
加速网络计算是指使用物理网络加速器(如Nvidia智能网卡)来执行原本由x86 CPU执行的NF数据平面功能。这些数据平面功能包括包分类、包导向、QoS、NAT等。
将CNF功能加速到网卡上,使CNF无需处理数据平面,从而减少CPU资源需求,并为CNF处理其他用例腾出空间。加速功能在智能网卡加速器上的执行效率高于x86 CPU。因此,操作可以以更高的吞吐量、更低的延迟和更少的CPU核心数执行,使运营商能够在同一服务器上加载更多CNF应用,或者使用更少/更便宜的服务器。
下图比较了两种方法。左侧系统在主机CPU上运行应用(仅软件),而右侧系统通过编程ConnectX-6 Dx应用加速器来加速数据平面。

解决方案架构
关键组件和技术
-
NVIDIA ConnectX SmartNICs 10/25/40/50/100/200和400G以太网网卡 业界领先的NVIDIA® ConnectX®系列智能网卡提供先进的硬件卸载和加速。 NVIDIA以太网适配器为超大规模、公有云和私有云、存储、机器学习、AI、大数据和电信平台提供最高的ROI和最低的总拥有成本。
-
NVIDIA LinkX Cables NVIDIA® LinkX®产品系列的线缆和收发器提供了业界最完整的10、25、40、50、100、200和400GbE以太网以及100、200和400Gb/s InfiniBand产品线,适用于云、HPC、超大规模、企业、电信、存储和人工智能数据中心应用。
-
NVIDIA Spectrum 以太网交换机 灵活的外形规格,支持16到128个物理端口,支持1GbE到400GbE速度。 基于突破性的硅技术,针对性能和可扩展性进行了优化,NVIDIA Spectrum交换机非常适合构建高性能、高性价比的
高效云数据中心网络、以太网存储架构和深度学习互连。
NVIDIA 结合了基于业界领先专用集成电路(ASIC)技术的 NVIDIA Spectrum™ 交换机的优势,以及多种现代网络操作系统选择,包括 NVIDIA Cumulus® Linux、SONiC 和 NVIDIA Onyx®。
-
Kubernetes Kubernetes 是一个开源容器编排平台,用于容器化应用的部署自动化、扩缩容和管理。
-
Kubespray Kubespray 由 Ansible 剧本、清单、配置工具和通用操作系统/Kubernetes 集群配置管理任务的领域知识组成,提供:
- 高可用集群
- 可组合属性
- 支持大多数主流 Linux 发行版
-
NVIDIA Network Operator NVIDIA Network Operator 简化了 Kubernetes 集群中 NVIDIA 网络资源的配置和管理。该操作符自动安装所需的主机网络软件,汇集所有必要组件以提供高速网络连接。这些组件包括 NVIDIA 网络驱动、Kubernetes 设备插件、CNI 插件、IP 地址管理(IPAM)插件等。NVIDIA Network Operator 与 NVIDIA GPU Operator 协同工作,为可扩展的 GPU 计算集群提供高吞吐量、低延迟的网络。
-
NVIDIA PMD 轮询模式驱动(PMD)是一个开源上游驱动,嵌入在 dpdk.org 发行版中,通过提供接收和发送的内核旁路,并避免中断处理的性能开销,实现快速数据包处理和低延迟。
-
TRex – 真实流量生成器 TRex 是一个开源、低成本、有状态和无状态的流量生成器,由 DPDK 驱动。它基于预处理和 L7 流量模板的智能重放生成 L4-7 流量。TRex 放大客户端和服务器端流量,可在单个 UCS 上扩展到 200Gb/秒。
服务器角色方案
服务器角色和网络描述:
-
一个计算层:
- 部署节点
- K8s Master 节点
- K8s Worker 节点,安装有 Nvidia ConnectX-6 DX 网卡,托管 Testpmd/VPP POD
- TRex 服务器节点,安装有 Nvidia ConnectX-6 DX 网卡
-
两个独立的网络层:
- 管理 K8s 主网络,1Gb 链路速度
- 高速 100Gb 以太网 Nvidia 交换机,用于 DPDK K8s 辅助网络

软件栈组件

物料清单

部署与配置
布线
Trex 和 Worker 节点连接到 100GE 数据平面 K8s 辅助网络。 此外,Worker、Master 和 Deployment 节点连接到 1GE 管理 K8s 主网络。

主机前提条件
-
为 k8s master、worker 和 deployment 节点安装 CentOS 8.4。
-
在 worker 节点上安装 OFED >= 5.4。 从 Nvidia 网络 OFED/EN 官方下载网站 下载最新的 Nvidia OFED/EN。 通过以下命令安装所需的库和内核模块:
Worker 节点控制台
./mlnxofedinstall --dpdk -
K8s worker 节点 BIOS 设置:
- 将电源节能设置为性能模式
- 设置 VT-D 标志 - 开启
- 设置 Turbo-boost - 开启
- SRIOV - 启用
-
查找 NVIDIA 网卡使用的 NUMA 节点(本例中网卡名称为 ens2f0):
Worker 节点控制台
cat /sys/class/net/ens2f0/device/numa_node提取与 NVIDIA 网卡相同 NUMA 节点的核心列表:
Worker 节点控制台
lscpu | grep NUMA -
编辑 worker 节点 grub 文件以进行 DPDK 性能优化和巨页配置: core-list 包含仅用于 Pod 的专用核心列表。本例将使用核心 4-23。不在列表中的剩余核心可保留给 K8s 系统使用。 使用 isolcpus 内核启动命令行将 CPU 与内核调度器隔离。这将确保用户空间进程不会被内核调度。
intel_iommu=on iommu=pt default_hugepagesz=1G hugepagesz=1G hugepages=32 isolcpus=<core-list>
RDMA 子系统配置
每个 Worker 节点上都需要进行 RDMA 子系统配置。
-
设置 PF 接口在启动时链路为“UP”(本例中网卡 ens2f0, ens2f1):
Worker 节点控制台
sed -i 's/\(ONBOOT=\)no/\1yes/g' /etc/sysconfig/network-scripts/ifcfg-ens2f0 sed -i 's/\(ONBOOT=\)no/\1yes/g' /etc/sysconfig/network-scripts/ifcfg-ens2f1 -
将 RDMA 子系统网络命名空间模式设置为独占模式。 RDMA 子系统网络命名空间模式(ib_core 模块中的 netns 参数)在独占模式下允许 Worker 节点服务器上 RDMA 工作负载的网络命名空间隔离。请创建 /etc/modprobe.d/ib_core.conf 配置文件以更改 ib_core 模块参数:
Worker 节点控制台
echo "options ib_core netns_mode=1" > /etc/modprobe.d/ib_core.conf
设置RDMA netns模式
在worker节点上,将RDMA netns模式设置为exclusive。
Worker节点控制台
echo "options ib_core netns_mode=0" > /etc/modprobe.d/ib_core.conf
dracut -f
重启worker服务器:
Worker节点控制台
reboot
服务器重启后,检查netns模式:
Worker节点控制台
rdma system
输出应为:"netns exclusive"
在部署服务器上安装Kubespray
部署节点控制台
yum install python3-pip jq git -y
git clone https://github.com/kubernetes-sigs/kubespray.git
cd kubespray
pip3 install -r requirements.txt
使用Kubespray部署K8s集群
-
设置master和worker节点的本地IP,并为每个节点生成配置文件:
部署节点控制台
cp -rfp inventory/sample inventory/mycluster declare -a IPS=(192.168.200.3 192.168.200.2) CONFIG_FILE=inventory/mycluster/hosts.yaml python3 contrib/inventory_builder/inventory.py ${IPS[@]}验证文件
inventory/mycluster/hosts.yaml中的集群信息,必要时进行编辑。 本例中node1为master节点,node2为worker节点。部署文件示例:all: hosts: node1: ansible_host: 192.168.200.3 ip: 192.168.200.3 access_ip: 192.168.200.3 node2: ansible_host: 192.168.200.2 ip: 192.168.200.2 access_ip: 192.168.200.2 children: kube-master: hosts: node1: kube-node: hosts: node2: etcd: hosts: node1: k8s-cluster: children: kube-master: kube-node: calico-rr: hosts: {} -
修改文件
inventory/mycluster/group_vars/k8s-cluster/k8s-cluster.yml中的部署参数,设置k8s版本:kube_version: v1.21.5 -
为Kubespray服务器设置无密码SSH认证的RSA密钥:
部署节点控制台
ssh-keygen将RSA密钥复制到master和worker节点:
部署节点控制台
ssh-copy-id root@<Node-IP> -
安装K8s集群:
部署节点控制台
ansible-playbook -i inventory/mycluster/hosts.yaml -e 'ansible_python_interpreter=/usr/bin/python3' --become --become-user=root cluster.yml成功安装的输出示例:
PLAY RECAP ************************************************************************************************************************************************************************************************* localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 node1 : ok=551 changed=24 unreachable=0 failed=0 skipped=1142 rescued=0 ignored=0 node2 : ok=350 changed=11 unreachable=0 failed=0 skipped=587 rescued=0 ignored=0 Tuesday 24 November 2020 09:31:22 +0200 (0:00:00.029) 0:04:50.249 ****** =============================================================================== kubernetes-apps/ansible : Kubernetes Apps | Lay Down CoreDNS Template ------------------------------------------------------------------------------------------------------------------------------ 14.73s container-engine/docker : ensure docker packages are installed ------------------------------------------------------------------------------------------------------------------------------------- 10.24s kubernetes/master : Master | wait for kube-scheduler ------------------------------------------------------------------------------------------------------------------------------------------------ 8.52s kubernetes-apps/network_plugin/multus : Multus | Start resources ------------------------------------------------------------------------------------------------------------------------------------ 7.91s kubernetes-apps/ansible : Kubernetes Apps | Start Resources ----------------------------------------------------------------------------------------------------------------------------------------- 7.76s network_plugin/multus : Multus | Copy manifest files ------------------------------------------------------------------------------------------------------------------------------------------------ 4.43s network_plugin/calico : Calico | Create calico manifests -------------------------------------------------------------------------------------------------------------------------------------------- 4.37s Gather necessary facts ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 4.34s kubernetes/preinstall : Get current version of calico cluster version ------------------------------------------------------------------------------------------------------------------------------- 4.17s download : check_pull_required | Generate a list of information about the images on a node --------------------------------------------------------------------------------------------------------- 4.11s network_plugin/calico : Get current version of calico cluster version ------------------------------------------------------------------------------------------------------------------------------- 3.88s kubernetes/master : kubeadm | Check if apiserver.crt contains all needed SANs ----------------------------------------------------------------------------------------------------------------------- 3.52s kubernetes/master : slurp kubeadm certs ------------------------------------------------------------------------------------------------------------------------------------------------------------- 3.27s policy_controller/calico : Create calico-kube-controllers manifests --------------------------------------------------------------------------------------------------------------------------------- 2.98s network_plugin/cni : CNI | Copy cni plugins --------------------------------------------------------------------------------------------------------------------------------------------------------- 2.92s network_plugin/calico : Start Calico resources ------------------------------------------------------------------------------------------------------------------------------------------------------ 2.82s kubernetes/master : Backup old certs and keys ------------------------------------------------------------------------------------------------------------------------------------------------------- 2.75s kubernetes-apps/cluster_roles : PriorityClass | Create k8s-cluster-critical ------------------------------------------------------------------------------------------------------------------------- 2.60s network_plugin/calico : Calico | wait for etcd ------------------------------------------------------------------------------------------------------------------------------------------------------ 2.44s policy_controller/calico : Start of Calico kube controllers ----------------------------------------------------------------------------------------------------------------------------------------- 2.23s -
为node2设置"worker"标签:
Master节点控制台
kubectl label nodes node2 node-role.kubernetes.io/worker=
为K8s集群安装NVIDIA Network Operator
NVIDIA Network Operator利用Kubernetes CRD和Operator SDK管理网络相关组件,以启用K8s集群中工作负载的快速网络和RDMA。快速网络是K8s集群的辅助网络,适用于需要高带宽或低延迟的应用。
要使其工作,需要配置和部署多个组件。所有Operator配置和安装步骤应在K8s Master节点上使用root用户账户执行。
准备工作
-
按照Helm安装文档在master节点服务器上安装Helm。
-
安装额外的RDMA CNI插件。 RDMA CNI插件允许容器化环境中RDMA工作负载的网络命名空间隔离。 使用以下YAML文件部署CNI:
Master节点控制台
# 此处应提供具体的YAML文件内容或命令,原文未完整给出
kubectl apply -f https://raw.githubusercontent.com/Mellanox/rdma-cni/master/deployment/rdma-cni-daemonset.yaml
要确保插件正确安装,请运行以下命令:
主节点控制台
kubectl -n kube-system get pods -o wide | egrep "rdma"
kube-rdma-cni-ds-c9cml 1/1 Running 0 22s 1.1.1.2 node1 <none> <none>
kube-rdma-cni-ds-cw22n 1/1 Running 0 22s 1.1.1.3 node2 <none> <none>
kube-rdma-cni-ds-dh28z 1/1 Running 0 22s 1.1.1.4 node3 <none> <none>
部署
-
添加NVIDIA Network Operator Helm仓库:
主节点控制台
helm repo add mellanox https://mellanox.github.io/network-operator helm repo update -
创建 values.yaml 文件(示例):
nfd: enabled: true sriovNetworkOperator: enabled: true # NicClusterPolicy CR values: deployCR: true ofedDriver: deploy: false nvPeerDriver: deploy: false rdmaSharedDevicePlugin: deploy: false sriovDevicePlugin: deploy: false secondaryNetwork: deploy: true cniPlugins: deploy: true image: containernetworking-plugins repository: mellanox version: v0.8.7 imagePullSecrets: [] multus: deploy: true image: multus repository: nfvpe version: v3.6 imagePullSecrets: [] config: '' ipamPlugin: deploy: true image: whereabouts repository: mellanox version: v0.3 imagePullSecrets: [] -
部署Operator:
主节点控制台
helm install -f values.yaml -n network-operator --create-namespace --wait mellanox/network-operator --generate-name NAME: network-operator LAST DEPLOYED: Sun Jul 11 23:06:54 2021 NAMESPACE: network-operator STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: Get Network Operator deployed resources by running the following commands: $ kubectl -n network-operator get pods $ kubectl -n mlnx-network-operator-resources get pods要确保Operator正确部署,请运行以下命令:
主节点控制台
kubectl -n network-operator get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES network-operator-1635068237-57c94ff559-fm7kb 1/1 Running 0 22s 10.233.90.10 node1 <none> <none> network-operator-1635068237-node-feature-discovery-master-ms57h 1/1 Running 0 22s 10.233.90.9 node1 <none> <none> network-operator-1635068237-node-feature-discovery-worker-fqw9g 1/1 Running 0 22s 10.233.96.3 node2 <none> <none> network-operator-1635068237-node-feature-discovery-worker-kh29n 1/1 Running 0 22s 10.233.90.8 node1 <none> <none> network-operator-1635068237-sriov-network-operator-5c9486726pj9 1/1 Running 0 22s 10.233.90.7 node1 <none> <none> sriov-network-config-daemon-gj7zp 1/1 Running 0 16s 10.7.215.90 node2 <none> <none>
高速网络配置
-
安装Operator后,请检查SriovNetworkNodeState CR以查看工作节点上所有支持SRIOV的设备。 在我们的部署中,选择的网络接口名称为 ens2f0, ens2f1。要查看接口状态,请使用以下命令:
主节点控制台
kubectl -n network-operator get sriovnetworknodestates.sriovnetwork.openshift.io node2 -o yaml ... - deviceID: 101d driver: mlx5_core linkSpeed: 100000 Mb/s linkType: ETH mac: 1c:34:da:54:cd:34 mtu: 1500 name: ens2f0 pciAddress: 0000:61:00.0 totalvfs: 2 vendor: 15b3 - deviceID: 101d driver: mlx5_core linkSpeed: 100000 Mb/s linkType: ETH mac: 1c:34:da:54:cd:35 mtu: 1500 name: ens2f1 pciAddress: 0000:61:00.1 totalvfs: 2 vendor: 15b3 ... -
为第一个PF接口创建SriovNetworkNodePolicy CR文件 policy-1.yaml,为第二个PF接口创建 policy-2.yaml,通过在'nicSelector'中指定接口名称和供应商(此示例包括ens2f0和ens2f1接口):
apiVersion: sriovnetwork.openshift.io/v1 kind: SriovNetworkNodePolicy metadata: name: mlnxnics-1 namespace: network-operator spec: nodeSelector: feature.node.kubernetes.io/network-sriov.capable: "true" resourceName: mlnx2f0 mtu: 1500 numVfs: 2 nicSelector: vendor: "15b3" pfNames: [ "ens2f0" ] deviceType: netdevice isRdma: trueapiVersion: sriovnetwork.openshift.io/v1 kind: SriovNetworkNodePolicy metadata: name: mlnxnics-2 namespace: network-operator spec: nodeSelector: feature.node.kubernetes.io/network-sriov.capable: "true" resourceName: mlnx2f1 mtu: 1500 numVfs: 2 nicSelector: vendor: "15b3" pfNames: [ "ens2f1" ] deviceType: netdevice isRdma: true -
部署policy-1.yaml和policy-2.yaml:
主节点控制台
kubectl apply -f policy-1.yaml kubectl apply -f policy-2.yaml -
创建SriovNetwork CR文件 network-1.yaml 和 network-2.yaml,它们引用SriovNetworkNodePolicy中为每个PF接口定义的'resourceName'(在此示例中,1.1.1.0/24和2.2.2.0/24定义为高速网络的CIDR范围):
apiVersion: sriovnetwork.openshift.io/v1 kind: SriovNetwork metadata: name: "netmlnx2f0" namespace: network-operator spec: ipam: | { "datastore": "kubernetes", "kubernetes": { "kubeconfig": "/etc/cni/net.d/whereabouts.d/whereabouts.kubeconfig" }, "log_file": "/tmp/whereabouts.log", "log_level": "debug", "type": "whereabouts", "range": "1.1.1.0/24" } vlan: 0 networkNamespace: "default" spoofChk: "off" resourceName: "mlnx2f0" linkState: "enable" metaPlugins: | { "type": "rdma" }apiVersion: sriovnetwork.openshift.io/v1 kind: SriovNetwork metadata: name: "netmlnx2f1" namespace: network-operator spec: ipam: | { "datastore": "kubernetes", "kubernetes": { "kubeconfig": "/etc/cni/net.d/whereabouts.d/whereabouts.kubeconfig" }, "log_file": "/tmp/whereabouts.log", "log_level": "debug", "type": "whereabouts", "range": "2.2.2.0/24" } vlan: 0 networkNamespace: "default" spoofChk: "off" resourceName: "mlnx2f1" linkState: "enable" metaPlugins: | { "type": "rdma" } -
部署network-1.yaml和network-2.yaml:
主节点控制台
kubectl apply -f network-1.yaml kubectl apply -f network-2.yaml -
验证部署
检查已部署的网络:
主节点控制台
kubectl get network-attachment-definitions.k8s.cni.cncf.io NAME AGE netmlnx2f0 3m netmlnx2f1 3m检查工作节点资源:
kubectl describe nodes node2 ... Addresses: InternalIP: 192.168.200.2 Hostname: node2 Capacity: cpu: 24 ephemeral-storage: 45093936Ki hugepages-1Gi: 32Gi
hugepages-2Mi: 0 memory: 96076Mi nvidia.com/mlnx2f0: 2 nvidia.com/mlnx2f1: 2 pods: 110 Allocatable: cpu: 23900m ephemeral-storage: 41558571349 hugepages-1Gi: 32Gi hugepages-2Mi: 0 memory: 62952Mi nvidia.com/mlnx2f0: 2 nvidia.com/mlnx2f1: 2 pods: 110 ...
Topology manager settings
性能敏感的应用程序需要拓扑对齐的资源分配,以避免引入额外的延迟。 拓扑管理器确保从统一内存访问(NUMA)节点分配设备。 更多信息请参考控制节点上的CPU管理策略和控制节点上的拓扑管理策略。
-
定义保留给K8s操作系统服务的核心:
Worker节点控制台
numactl --hardware available: 1 nodes (0) node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 node 0 size: 92611 MB node 0 free: 53873 MB node distances: node 0 0: 10在我们的示例中,Numa节点0包含核心0-23。 要为核心0-3保留给K8s,请将以下行添加到kubelet-config.yaml文件中:
/etc/kubernetes/kubelet-config.yaml
reservedSystemCPUs:0-3现在核心4-23可供Pod使用。确保grub文件设置了相同的隔离核心列表(isolcpus=4-23),如前提条件部分所述。
-
通过将以下行添加到Worker节点的kubelet配置文件
/etc/kubernetes/kubelet-config.yaml中启用拓扑管理器:/etc/kubernetes/kubelet-config.yaml
cpuManagerPolicy: static reservedSystemCPUs: 0-3 topologyManagerPolicy: single-numa-node -
由于cpuManagerPolicy的更改,您应该删除
/var/lib/kubelet/cpu_manager_state并重启Worker节点上的kubelet服务,如下所示:rm -f /var/lib/kubelet/cpu_manager_state systemctl daemon-reload && systemctl restart kubelet
Configuring VFs to be trusted
-
使用以下命令提取PF网络接口的PCI插槽号(在我们的示例中,PCI插槽号为61:00.0):
Worker节点控制台
lspci | grep "Mellanox" | grep -v "Virtual" | cut -d " " -f 1 -
使用mlxreg配置所有VF被固件信任:
Worker节点控制台
mlxreg -d 61:00.0 --reg_id 0xc007 --reg_len 0x40 --indexes "0x0.0:32=0x80000000" --yes --set "0x4.0:32=0x1" mlxreg -d 61:00.1 --reg_id 0xc007 --reg_len 0x40 --indexes "0x0.0:32=0x80000000" --yes --set "0x4.0:32=0x1" -
从驱动侧设置VF为信任(在我们的示例中,VF属于接口ens2f0,ensf1):
Worker节点控制台
PF=($(ibdev2netdev | grep mlx5_ | awk '{print $5}')); for i in ${PF[@]}; do cat /sys/class/net/${i}/settings/vf_roce &> /dev/null; RESULT=$?; if [ $RESULT -eq 0 ]; then VFS=($(cat /sys/class/net/${i}/settings/vf_roce | awk '{print $2}' | tr -d :)); for j in ${VFS[@]}; do ip link set $i vf $j trust on; echo "Set inteface $i VF $j to trusted"; done fi; done -
绑定和解绑VF的以太网驱动:
Worker节点控制台
VFS_PCI=($(lspci | grep "Mellanox" | grep "Virtual" | cut -d " " -f 1)); for i in ${VFS_PCI[@]}; do echo "unbinding VF $i"; echo "0000:${i}" >> /sys/bus/pci/drivers/mlx5_core/unbind; echo "binding VF $i"; echo "0000:${i}" >> /sys/bus/pci/drivers/mlx5_core/bind; done
TRex server installation with L2 interfaces
要构建Trex服务器,请按照本指南中的步骤操作:使用Nvidia ConnectX适配器在几个步骤中安装TRex。
- 确保配置基于MAC的配置。
- 设置第一个TRex端口的目标MAC地址为testpmd或VPP端口0的MAC地址。
- 设置第二个TRex端口的目标MAC地址为testpmd或VPP端口1的MAC地址。
安装过程示例如下:
./dpdk_setup_ports.py -i
By default, IP based configuration file will be created. Do you want to use MAC based config? (y/N)y
+----+------+---------+-------------------+-----------------------------------------+-----------+----------+--------+
| ID | NUMA | PCI | MAC | Name | Driver | Linux IF | Active |
+====+======+=========+===================+=========================================+===========+==========+========+
| 0 | 0 | 02:00.0 | 38:63:bb:33:29:9c | NetXtreme BCM5719 Gigabit Ethernet PCIe | tg3 | eno1 | |
+----+------+---------+-------------------+-----------------------------------------+-----------+----------+--------+
| 1 | 0 | 02:00.1 | 38:63:bb:33:29:9d | NetXtreme BCM5719 Gigabit Ethernet PCIe | tg3 | eno2 | |
+----+------+---------+-------------------+-----------------------------------------+-----------+----------+--------+
| 2 | 0 | 02:00.2 | 38:63:bb:33:29:9e | NetXtreme BCM5719 Gigabit Ethernet PCIe | tg3 | eno3 | |
+----+------+---------+-------------------+-----------------------------------------+-----------+----------+--------+
| 3 | 0 | 02:00.3 | 38:63:bb:33:29:9f | NetXtreme BCM5719 Gigabit Ethernet PCIe | tg3 | eno4 | |
+----+------+---------+-------------------+-----------------------------------------+-----------+----------+--------+
| 4 | 0 | 08:00.0 | ec:0d:9a:8a:27:ba | MT28800 Family [ConnectX-5 Ex] | mlx5_core | ens1f0 | |
+----+------+---------+-------------------+-----------------------------------------+-----------+----------+--------+
| 5 | 0 | 08:00.1 | ec:0d:9a:8a:27:bb | MT28800 Family [ConnectX-5 Ex] | mlx5_core | ens1f1 | |
+----+------+---------+-------------------+-----------------------------------------+-----------+----------+--------+
Please choose even number of interfaces from the list above, either by ID , PCI or Linux IF
Stateful will use order of interfaces: Client1 Server1 Client2 Server2 etc. for flows.
Stateless can be in any order.
Enter list of interfaces separated by space (for example: 1 3) : 4 5
For interface 4, assuming loopback to it's dual interface 5.
Destination MAC is ec:0d:9a:8a:27:bb. Change it to MAC of DUT? (y/N).y
Please enter new destination MAC of interface 4: 22:2F:60:A5:F8:01
For interface 5, assuming loopback to it's dual interface 4.
Destination MAC is ec:0d:9a:8a:27:ba. Change it to MAC of DUT? (y/N).y
Please enter new destination MAC of interface 5: 22:2F:60:A5:F8:02
Print preview of generated config? (Y/n)y
### Config file generated by dpdk_setup_ports.py ###
- version: 2
interfaces: ['08:00.0', '08:00.1']
port_info:
- dest_mac: 22:2f:60:a5:f8:01
src_mac: ec:0d:9a:8a:27:ba
- dest_mac: 22:2f:60:a5:f8:02
src_mac: ec:0d:9a:8a:27:bb
platform:
master_thread_id: 0
latency_thread_id: 6
dual_if:
- socket: 0
threads: [1,2,3,4,5,12,13,14,15,16,17]
Save the config to file? (Y/n)y
Default filename is /etc/trex_cfg.yaml
Performance Evaluation
在本节中,我们将评估执行NAT和IPv4路由的CNF的性能。软件实现将基于VPP,而加速用例将使用Testpmd将相同的数据平面编程到SmartNIC加速器中,与VPP应用程序相同。
Testing bidirectional IPv4 Routing and NAT Scheme

- VPP/Testpmd Pod配置了两个接口:1.1.1.1/24 和 2.2.2.1/24
- TRex服务器配置了两个接口:1.1.1.2/24 和 2.2.2.2/24
- 当数据包目标为Trex IP 1.1.1.2时,源子网2.2.2.0/24隐藏在网关IP 1.1.1.1之后
- 源子网1.1.1.0/24
隐藏在网关IP 2.2.2.1后面,当数据包目的地为Trex IP 2.2.2.2时。
- 来自Trex子网2.2.2.0/24且目的地为Trex IP 1.1.1.2的数据包由Testpmd/VPP Pod路由并NAT回Trex。
- 来自Trex子网1.1.1.0/24且目的地为Trex IP 2.2.2.2的数据包由Testpmd/VPP Pod路由并NAT回Trex。
VPP启动
使用CentOS启动CNF
在此Pod CR示例中:
- 已附加network-1和network-2
- 已挂载8Gb巨页
- 已从nvidia.com/mlnx2f0、nvidia.com/mlnx2f1资源附加2个SRIOV接口。
- 已分配8个核心
apiVersion: v1
kind: Pod
metadata:
name: vpp
annotations:
k8s.v1.cni.cncf.io/networks: netmlnx2f0 , netmlnx2f1
spec:
containers:
- name: vpp
image: centos:8.3.2011
securityContext:
privileged: true
allowPrivilegeEscalation: true
capabilities:
add:
- IPC_LOCK
- SYS_RAWIO
- NET_RAW
volumeMounts:
- mountPath: /dev/hugepages
name: hugepage
resources:
requests:
memory: 8Gi
hugepages-1Gi: 2Gi
cpu: 8
nvidia.com/mlnx2f0: 1
nvidia.com/mlnx2f1: 1
limits:
memory: 8Gi
hugepages-1Gi: 2Gi
cpu: 8
nvidia.com/mlnx2f0: 1
nvidia.com/mlnx2f1: 1
command:
- sh
- -c
- sleep inf
volumes:
- name: hugepage
emptyDir:
medium: HugePages
启动Pod:
主节点控制台
kubectl create -f VPP_PodCR.yaml
安装VPP应用程序
本指南假设没有预构建的容器镜像可供下载。
要安装VPP,请登录到容器bash shell:
主节点控制台
kubectl exec --stdin --tty vpp -- /bin/bash
继续按照此指南安装VPP:Build and Run Vector Packet Processing (VPP) Applications Over NVIDIA SmartNICs.
VPP性能评估
配置VPP执行NAT
-
使用L3接口配置VPP - 确保使用正确的IP配置L3 IPv4接口。在我们的示例中,第一个接口设置为1.1.1.1/24,第二个接口设置为2.2.2.1/24。
VPP容器控制台
## 启动VPP CLI ## $HOME-PATH/vpp/build-root/build-vpp-native/vpp/bin/./vppctl ## 设置2个L3接口及其IP和子网 ## set int ip address HundredGigabitEthernet61/0/2 1.1.1.1/24 set interface state HundredGigabitEthernet61/0/2 up set int ip address HundredGigabitEthernet61/0/3 2.2.2.1/24 set interface state HundredGigabitEthernet61/0/3 up -
设置指向Trex服务器接口的静态ARP条目
VPP容器控制台
set ip neighbor HundredGigabitEthernet61/0/2 1.1.1.2 <Trex_first_port_MAC_Address> static set ip neighbor HundredGigabitEthernet61/0/3 2.2.2.2 <Trex_second_port_MAC_Address> static -
为2个接口设置NAT
- 将来自端口HundredGigabitEthernet61/0/3的流量NAT到端口HundredGigabitEthernet61/0/2的网关IP 1.1.1.1后面。
- 将来自端口HundredGigabitEthernet61/0/2的流量NAT到端口HundredGigabitEthernet61/0/3的网关IP 2.2.2.1后面。
VPP容器控制台
nat44 add interface address HundredGigabitEthernet61/0/2 nat44 add interface address HundredGigabitEthernet61/0/3 set interface nat44 out HundredGigabitEthernet61/0/2 output-feature set interface nat44 out HundredGigabitEthernet61/0/3 output-feature nat44 forwarding enable nat addr-port-assignment-alg default
Testpmd SmartNIC加速启动
为Testpmd应用程序创建容器镜像
-
克隆Dockerfile镜像构建器:
git clone https://github.com/Mellanox/mlnx_docker_dpdk cd mlnx_docker_dpdk/ git checkout origin/ninja-build -
在本地Docker服务器中创建镜像:
- OS版本:CentOS 8.4.2105
- DPDK版本:20.11
- Nvidia OFED:5.4-1.0.3.0
docker build --build-arg OS_VER=8.4.2105 --build-arg DPDK_VER=20.11 --build-arg OFED_VER=5.4-1.0.3.0 -t centos8.4/dpdk20.11:ofed5.4 . -
创建镜像后,建议将其推送到Docker Hub账户仓库。有关说明,请访问此链接:Pushing docker image to Docker hub。Docker Hub中的仓库将包含所需的镜像,可以在Pod定义yaml文件中指定(在我们的示例文件TestpmdPodCR.yaml中)。
使用Testpmd应用程序启动CNF
在此Pod CR示例中:
- 已附加network-1和network-2
- 已挂载8Gb巨页
- 已从nvidia.com/mlnx2f0、nvidia.com/mlnx2f1资源附加2个SRIOV接口。
- 已分配8个核心
apiVersion: v1
kind: Pod
metadata:
name: testpmd
annotations:
k8s.v1.cni.cncf.io/networks: netmlnx2f0 , netmlnx2f1
spec:
containers:
- name: testpmd
image: <repository_name>:<Testpmd_image_name>
securityContext:
capabilities:
add:
- IPC_LOCK
- SYS_RAWIO
- NET_RAW
volumeMounts:
- mountPath: /dev/hugepages
name: hugepage
resources:
requests:
memory: 8Gi
hugepages-1Gi: 2Gi
cpu: 8
nvidia.com/mlnx2f0: 1
nvidia.com/mlnx2f1: 1
limits:
memory: 8Gi
hugepages-1Gi: 2Gi
cpu: 8
nvidia.com/mlnx2f0: 1
nvidia.com/mlnx2f1: 1
command:
- sh
- -c
- sleep inf
volumes:
- name: hugepage
emptyDir:
medium: HugePages
启动Pod:
主节点控制台
kubectl create -f TestpmdPodCR.yaml
Testpmd SmartNIC加速性能评估
Testpmd流组成
本节说明如何通过构建流来运行testpmd应用程序,加速路由和NAT操作。
Testpmd使用DPDK标准RTE_FLOW API加速流。以下是模拟NAT和路由的示例和说明:

此外,IPv4头部校验和以及数据包CRC由NIC自动完成。
单流测试
使用2个hairpin队列和单个核心在2个VF上运行Testpmd应用程序:
TestPMD POD CLI
# 命令示例
(注意:原始内容中TestPMD POD CLI部分未提供具体命令,此处保留占位符。)
cd /usr/src/dpdk-20.11/build/app/
./dpdk-testpmd -c 0x30 -n 4 -m 1024 -w 0000:61:00.2 -w 0000:61:00.4 -- --hairpinq=2 --hairpin-mode=0x12 --burst=64 --txd=1024 --rxd=1024 --mbcache=512 --rxq=1 --txq=1 --nb-cores=1 --flow-isolate-all -a -i
参数定义:
- 核心数掩码(例如2个核心)
-c 0x3 - NUMA通道数
-n 4 - 巨页内存量
-m 1024 - PCI插槽
-w <number> - RX/TX队列数
--rxq=2 --txq=2 --nb-cores=1 - 设置2个hairpin队列
--hairpinq=2 - Hairpin模式
--hairpin-mode值:0x10 - 显式tx规则,0x02 - hairpin端口配对 --flow-isolate-all确保所有流量仅通过配置的流规则接收- 单包处理PMD核心
--nb-cores=1 - 设置端口RX/TX描述符大小
--txd=1024 --rxd=1024
从Testpmd CLI插入第一个方向的加速流:
Testpmd CLI
testpmd> flow create 1 ingress pattern eth / ipv4 src is <Src_IP2> / udp src is <S_port> / end actions set_tp_src port <New_S_port> / queue index 1 / end
testpmd> flow create 0 egress pattern eth / ipv4 / udp / end actions set_mac_dst mac_addr <TREX_MAC0> / set_mac_src mac_addr <TESTPMD_MAC0> / set_ipv4_src ipv4_addr <NAT_IP2> / dec_ttl / end
参数值:
<Src_IP2>== 2.2.2.2<S_port>== 1002<New_S_port>== 1003<TREX_MAC0>← 第一个Trex端口0的MAC地址<TESTPMD_MAC0>← 第一个testpmd端口0的MAC地址<NAT_IP2>== 2.2.2.1
从Testpmd CLI插入第二个方向的加速流:
Testpmd CLI
testpmd> flow create 0 ingress pattern eth / ipv4 src is <Src_IP1> / udp src is <S_port> / end actions set_tp_src port <New_S_port> / queue index 2 / end
testpmd> flow create 1 egress pattern eth / ipv4 / udp / end actions set_mac_dst mac_addr <TREX_MAC1> / set_mac_src mac_addr <TESTPMD_MAC1> / set_ipv4_src ipv4_addr <NAT_IP1> / dec_ttl / end
参数值:
<Src_IP1>== 1.1.1.2<S_port>== 1002<New_S_port>== 1003<TREX_MAC1>← 第二个Trex端口1的MAC地址<TESTPMD_MAC1>← 第二个testpmd端口1的MAC地址<NAT_IP1>== 1.1.1.1
多流测试
对于大量流,可以将所有所需流列在一个文件中。Testpmd应用程序将通过读取该文件加载所有流。 本节说明如何通过bash脚本将流写入文件来创建"flow"文件。 修改此脚本"create_flows.sh",通过在每个方向上迭代单个用户的源UDP端口来生成多个NAT流会话。
NAT_IP1← 第一个Testpmd端口的IPNAT_IP2← 第二个Testpmd端口的IPMIN_PORT← 最小源UDP端口范围MAX_PORT← 最大源UDP端口范围TREX_MAC0← 第一个Trex端口0的MAC地址TREX_MAC1← 第二个Trex端口1的MAC地址TESTPMD_MAC0← 第一个testpmd端口0的MAC地址TESTPMD_MAC1← 第二个testpmd端口1的MAC地址Src_IP1← 第一个Trex端口的IPSrc_IP2← 第二个Trex端口的IPPATH← 流文件目录路径(本例中为/usr/src/dpdk-20.11/build/app/flows.txt)
#!/bin/bash -e
MIN_PORT=1002
MAX_PORT=6002
Src_IP1="1.1.1.2"
Src_IP2="2.2.2.2"
NAT_IP1="1.1.1.1"
NAT_IP2="2.2.2.1"
TESTPMD_MAC0="4A:AC:4F:29:87:80"
TESTPMD_MAC1="86:19:A0:B8:10:BD"
TREX_MAC0="ec:0d:9a:8a:27:ba"
TREX_MAC1="ec:0d:9a:8a:28:3a"
PATH="/usr/src/dpdk-20.11/build/app/flows.txt"
echo "flow create 0 group 0 ingress pattern eth / ipv4 / udp / end actions jump group 1 / end" > $PATH
echo "flow create 1 group 0 ingress pattern eth / ipv4 / udp / end actions jump group 1 / end" >> $PATH
echo "flow create 0 group 0 egress pattern eth / ipv4 / udp / end actions set_mac_dst mac_addr ${TREX_MAC0} / set_mac_src mac_addr ${TESTPMD_MAC0} / set_ipv4_src ipv4_addr ${NAT_IP2} / dec_ttl / end" >> $PATH
echo "flow create 1 group 0 egress pattern eth / ipv4 / udp / end actions set_mac_dst mac_addr ${TREX_MAC1} / set_mac_src mac_addr ${TESTPMD_MAC1} / set_ipv4_src ipv4_addr ${NAT_IP1} / dec_ttl / end" >> $PATH
for ((i = ${MIN_PORT}; i <= ${MAX_PORT}; i++ ));
do
echo "flow create 1 group 1 ingress pattern eth / ipv4 src is ${Src_IP2} / udp src is ${i} / end actions set_tp_src port $((i+1)) / queue index 1 / end" >> $PATH
echo "flow create 0 group 1 ingress pattern eth / ipv4 src is ${Src_IP1} / udp src is ${i} / end actions set_tp_src port $((i+1)) / queue index 2 / end" >> $PATH
done
在Testpmd二进制目录中执行流创建脚本:
TestPMD POD CLI
chmod +x create_flows.sh
./create_flows.sh
运行testpmd并从文件加载所有流:
TestPMD POD CLI
cd /usr/src/dpdk-20.11/build/app/
./dpdk-testpmd -c 0x30 -n 4 -m 1024 -w 0000:61:00.2 -w 0000:61:00.4 -- --hairpinq=2 --hairpin-mode=0x12 --burst=64 --txd=1024 --rxd=1024 --mbcache=512 --rxq=1 --txq=1 --nb-cores=1 --flow-isolate-all -a -i
从文件加载流(此操作可能需要几分钟):
Testpmd CLI
testpmd>load flows.txt
使用Trex测试性能
将Trex_UDP_L3_P0.py python脚本上传到Trex根目录:
from trex_stl_lib.api import *
class STLS1(object):
def __init__ (self):
pass;
def create_stream (self):
pkt = Ether()/IP(src="https://networking-docs.nvidia.com/sol/1.1.1.2", dst="2.2.2.2")/UDP()/(22*'x')
vm = STLScVmRaw( [
STLVmTupleGen ( ip_min="1.1.1.2", ip_max="1.1.1.2",
port_min=1002, port_max=6002,
name="tuple"),
STLVmWrFlowVar(fv_name="tuple.ip", pkt_offset="IP.src"), # write ip to packet IP.src
STLVmFixIpv4(offset="IP"), # fix checksum
STLVmWrFlowVar(fv_name="tuple.port", pkt_offset="UDP.sport")
]
)
return STLStream(
packet = STLPktBuilder(pkt = pkt ,vm = vm ) ,
mode = STLTXCont(pps = 3000) )
def get_streams (self, direction = 0, **kwargs):
# create 1 stream
return [ self.create_stream() ]
def register():
return STLS1()
将Trex_UDP_L3_P1.py python脚本上传到Trex根目录:
from trex_stl_lib.api import *
class STLS1(object):
def __init__ (self):
pass;
def create_stream (self):
pkt = Ether()/IP(src="https://networking-docs.nvidia.com/sol/2.2.2.2", dst="1.1.1.2")/UDP()/(22*'x')
vm = STLScVmRaw( [
STLVmTupleGen ( ip_min="2.2.2.2", ip_max="2.2.2.2",
port_min=1002, port_max=6002,
name="tuple"),
STLVmWrFlowVar(fv_name="tuple.ip", pkt_offset="IP.src"), # write ip to packet IP.src
STLVmFixIpv4(offset="IP"), # fix checksum
STLVmWrFlowVar(fv_name="tuple.port", pkt_offset="UDP.sport")
]
)
return STLStream(
packet = STLPktBuilder(pkt = pkt ,vm = vm ) ,
mode = STLTXCont(pps = 3000) )
def get_streams (self, direction = 0, **kwargs):
# create 1 stream
return [ self.create_stream() ]
def register():
return STLS1()
请确保遵循以下设置:单个用户,每个方向5k会话(总共10k会话):
- 用户指源IPv4地址
- 会话指源UDP端口范围

单会话测试 如果测试单用户单会话,'port_min'应等于'port_max'(在我们的示例中,两者都应等于1002)。
运行UDP_L3_P0.py和UDP_L3_P1.py脚本向DUT发送数据包: 增加每秒数据包数量,直到发送数据包数等于接收数据包数。
Trex控制台
./trex-console
trex>start -f UDP_L3_P0.py -m 74mpps -p 0
trex>start -f UDP_L3_P1.py -m 74mpps -p 1
参数定义:
-f:指定所需的Trex脚本-m:指定每秒数据包数量-p:设置所需端口
验证从端口0发送的数据包是否在端口1上接收:
Trex控制台
trex>tui
单向测试结果示例:

性能总结
双向测试结果显示,在利用SmartNIC加速能力的情况下,使用相同的x86主机单核资源,性能提升超过12倍。 这将突出SmartNIC硬件处理网络数据包相比仅使用x86 CPU软件处理的卓越价值。
单向测试结果

双向测试结果

作者
![]() |
Amir Zeidner 多年来,Amir主要作为电信领域的解决方案架构师,领导先进解决方案以满足5G、NFV和SDN网络基础设施需求。Amir在数据平面加速技术(如加速交换和网络处理(ASAP²)和DPDK)方面的专业知识,加上对开源云基础设施的深入了解,使他能够在电信领域推广和交付独特的端到端NVIDIA网络解决方案。 |


