RDG for DPF Host Trusted with HBN DPU Service

Created on December 31, 2025 Scope This Reference Deployment Guide (RDG) provides detailed instructions for deploying a Kubernetes (K8s) cluster using the DOCA

文档目录

Created on December 31, 2025

Scope

This Reference Deployment Guide (RDG) provides detailed instructions for deploying a Kubernetes (K8s) cluster using the DOCA Platform Framework (DPF). The guide focuses on setting up an accelerated Host-Based Networking (HBN) service on NVIDIA® BlueField®-3 DPU to deliver secure, isolated, and hardware-accelerated environments.

This guide is designed for experienced system administrators, system engineers, and solution architects who seek to deploy high-performance Kubernetes clusters with Host-Based Networking enabled on NVIDIA BlueField DPU.

Warning

  • This reference implementation, as the name implies, is a specific, opiniated deployment example designed to address the use case described above.
  • While other approaches may exist to implement similar solutions, this document provides a detailed guide for this particular method.

Abbreviations and Acronyms

Term Definition Term Definition
BFB BlueField Bootstream (OS Image) MAAS Metal as a Service
BGP Border Gateway Protocol RDG Reference Deployment Guide
CNI Container Network Interface RDMA Remote Direct Memory Access
CSI Container Storage Interface SFC Service Function Chaining
DOCA Data Center Infrastructure-on-a-Chip Architecture SR-IOV Single Root Input/Output Virtualization
DPF DOCA Platform Framework TOR Top of Rack
DPU Data Processing Unit VLAN Virtual LAN (Local Area Network)
GENEVE Generic Network Virtualization Encapsulation VNI Virtual Network Interface
HBN Host Based Networking VRF Virtual Router/Forwarder
IPAM IP Address Management VRR Virtual Router Redundancy
K8S Kubernetes VTEP Virtual Tunnel End Point

Introduction

The NVIDIA BlueField-3 Data Processing Unit (DPU) is a 400 Gb/s infrastructure compute platform designed for line-rate processing of software-defined networking, storage, and cybersecurity workloads. It combines powerful compute resources, high-speed networking, and advanced programmability to deliver hardware-accelerated, software-defined solutions for modern data centers.

NVIDIA DOCA unleashes the full potential of the BlueField platform by enabling rapid development of applications and services that offload, accelerate, and isolate data center workloads.

One such service is Host-Based Networking (HBN) - a DOCA-enabled solution that allows network architects to design networks based on Layer 3 (L3) protocols. HBN enables routing on the server side by using BlueField as a BGP router. It encapsulates key networking functions in a containerized service pod, deployed directly on the BlueField’s ARM cores.

However, deploying and managing DPU and their associated DOCA services, especially at scale, presents operational challenges. Without a robust provisioning and orchestration system, tasks such as lifecycle management, service deployment, and network configuration for service function chaining (SFC) can quickly become complex and error prone. This is where the DOCA Platform Framework (DPF) comes into play.

DPF automates the full DPU lifecycle, streamlines the deployment of DOCA services, and simplifies advanced network configurations. With DPF, services such as HBN can be deployed seamlessly, allowing for efficient offloading and intelligent routing of traffic through the DPU data plane.

By leveraging DPF, users can scale and automate DPU management across Kubernetes customer environments - optimizing performance while simplifying operations.

As part of the reference implementation, open-source components outside the scope of DPF (e.g., MAAS, pfSense, Kubespray) are used to simulate a realistic customer deployment environment.

The guide includes the full end-to-end deployment process, including:

  • Infrastructure provisioning
  • DPF deployment
  • DPU provisioning
  • Service configuration and deployment
  • Service chaining

It also demonstrates some performance optimizations, with results validated through standard RDMA and TCP workload tests.

References

Solution Architecture

Key Components and Technologies

  • NVIDIA BlueField® Data Processing Unit (DPU) The NVIDIA® BlueField® data processing unit (DPU) ignites unprecedented innovation for modern data centers and supercomputing clusters. With its robust compute power and integrated software-defined hardware accelerators for networking, storage, and security, BlueField creates a secure and accelerated infrastructure for any workload in any environment, ushering in a new era of accelerated computing and AI.

  • NVIDIA DOCA Software Framework NVIDIA DOCA™ unlocks the potential of the NVIDIA® BlueField® networking platform. By harnessing the power of BlueField DPU and SuperNICs, DOCA enables the rapid creation of applications and services that offload, accelerate, and isolate data center workloads. It lets developers create software-defined, cloud-native, DPU- and SuperNIC-accelerated services with zero-trust protection, addressing the performance and security demands of modern data centers.

NVIDIA ConnectX SmartNICs

10/25/40/50/100/200和400G以太网网卡

业界领先的NVIDIA® ConnectX®系列智能网卡(SmartNICs)提供先进的硬件卸载和加速功能。

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® LinuxSONiCNVIDIA Onyx®

NVIDIA Cumulus Linux

NVIDIA® Cumulus® Linux是业界最具创新性的开放网络操作系统,可让您像其他操作系统一样自动化、定制和扩展数据中心网络。

NVIDIA Network Operator

NVIDIA Network Operator简化了Kubernetes集群中NVIDIA网络资源的配置和管理。该运算符自动安装所需的主机网络软件,汇集所有必要组件以提供高速网络连接。这些组件包括NVIDIA网络驱动程序、Kubernetes设备插件、CNI插件、IP地址管理(IPAM)插件等。NVIDIA Network Operator与NVIDIA GPU Operator协同工作,为可扩展的GPU计算集群提供高吞吐量、低延迟的网络。

Kubernetes

Kubernetes是一个开源容器编排平台,用于容器化应用程序的部署自动化、扩展和管理。

Kubespray

Kubespray由Ansible剧本、清单、配置工具和通用OS/Kubernetes集群配置管理任务的领域知识组成,并提供:

  • 高可用集群
  • 可组合属性
  • 支持大多数流行的Linux发行版

RDMA

RDMA是一种允许网络中的计算机在不涉及任一计算机的处理器、缓存或操作系统的情况下交换数据的技术。

与本地DMA类似,RDMA提高了吞吐量和性能,并释放了计算资源。

Solution Design

Solution Logical Design

逻辑设计包括以下组件:

  • 1个Hypervisor节点(基于KVM),配备ConnectX-7
    • 1个防火墙VM
    • 1个跳板VM
    • 1个MaaS VM
    • 3个K8s Master VM,运行所有K8s管理组件
  • 2个工作节点(PCI Gen5),每个配备1个BlueField-3网卡
  • 单个高速(HS)交换机
  • 1 Gb主机管理网络

image-2025-9-9_12-57-18.png

HBN service Logical Design

在本文档中,我们将在每个工作节点上创建两个隔离网络;一个基于PF0的虚拟功能VF10,另一个基于PF1的虚拟功能VF10

每个网络通过HBN服务连接到单独的VLAN/VNI,位于单独的VRF - RED和BLUE上。

我们将使用hostdev插件将这些虚拟功能作为辅助网络分配给工作负载Pod。然后,我们将演示同一网络内(例如RED网络)不同工作节点上Pod之间的加速RDMA和TCP流量,并验证连接到不同网络(RED vs BLUE)的Pod之间的网络隔离

如果您对加速主Kubernetes网络感兴趣,请参阅此RDG,其中涵盖了DPF与HBN和OVN-Kubernetes服务以及额外DOCA服务的部署。

image-2025-5-18_14-51-48.png

K8s Cluster Logical Design

以下K8s逻辑设计图突出了DPF系统的主要组件,包括:

  • 3个K8s Master Node VM,运行所有K8s管理组件
  • 2个K8s Worker Nodes(x86)
  • 2个K8s DPU Workers,运行DOCA服务(HBN)
  • 1个Kamaji(K8s控制平面管理器)
  • 1个Tenant DPU Control Plane(租户集群)
  • 连接到高速和1GbE网络

image-2025-5-29_11-44-18-1.png

Firewall Design

本解决方案中的pfSense防火墙扮演两个关键角色:

  • 防火墙 – 为DPF系统提供隔离环境,确保安全操作
  • 路由器 – 为管理网络提供互联网访问

防火墙上配置了SSH和RDP的端口转发规则,将流量路由到跳板节点在主机管理网络上的IP地址。从跳板节点,管理员可以管理和访问设置中的各种设备,以及处理Kubernetes(K8s)集群和DPF组件的部署。

下图说明了本解决方案中使用的防火墙设计:

image-2025-5-29_11-45-16-1.png

Software Stack Components

HBC_STACK.png

注意: 确保使用上述软件堆栈的完全相同版本

Bill of Materials

物料清单

物料清单

image-2025-6-26_16-0-10-1.png

部署与配置

节点与交换机定义

以下定义和参数用于部署演示的组网:

交换机端口使用

Hostname Rack ID Ports
hs-switch 1 swp1-4
mgmt-switch 1 swp1-3

主机

Rack Server Type Server Name Switch Port IP and NICs Default Gateway
Rack1 Hypervisor Node hypervisor mgmt-switch: swp1 mgmt-br (interface eno2): -lab-br (interface eno1): Trusted LAN IP Trusted LAN GW
Rack1 Worker Node worker1 mgmt-switch: swp2hs-switch: swp1-swp2 ens15f0: 10.0.110.21/24ens5f0np0/ens5f1np1: 10.0.120.0/22 10.0.110.254
Rack1 Worker Node worker2 mgmt-switch: swp3hs-switch: swp3-swp4 ens15f0: 10.0.110.22/24ens5f0np0/ens5f1np1: 10.0.120.0/22 10.0.110.254
Rack1 Firewall (Virtual) fw - LAN (mgmt-br): 10.0.110.254/24WAN (lab-br): Trusted LAN IP Trusted LAN GW
Rack1 Jump Node (Virtual) jump - enp1s0: 10.0.110.253/24 10.0.110.254
Rack1 MaaS (Virtual) maas - enp1s0: 10.0.110.252/24 10.0.110.254
Rack1 Master Node (Virtual) master1 - enp1s0: 10.0.110.1/24 10.0.110.254
Rack1 Master Node (Virtual) master2 - enp1s0: 10.0.110.2/24 10.0.110.254
Rack1 Master Node (Virtual) master3 - enp1s0: 10.0.110.3/24 10.0.110.254

布线

Hypervisor节点

image-2025-5-29_11-57-21-1.png

K8s Worker节点

image-2025-5-11_17-20-54-1.png

组网配置

更新Cumulus Linux

作为最佳实践,请确保使用最新发布的Cumulus Linux NOS版本。

有关如何升级Cumulus Linux的信息,请参阅Cumulus Linux用户指南

配置Cumulus Linux交换机

按如下方式配置SN3700交换机(hs-switch):

# 配置命令示例
nv set interface lo ip address 11.0.0.101/32
nv set interface lo type loopback
nv set interface swp1-4 link state up
nv set interface swp1-4 type swp
nv set router bgp autonomous-system 65001
nv set router bgp enable on
nv set router bgp graceful-restart mode full
nv set router bgp router-id 11.0.0.101
nv set vrf default router bgp address-family ipv4-unicast enable on
nv set vrf default router bgp address-family ipv4-unicast redistribute connected enable on
nv set vrf default router bgp address-family ipv6-unicast enable on
nv set vrf default router bgp address-family ipv6-unicast redistribute connected enable on
nv set vrf default router bgp enable on
nv set evpn enable on
nv set vrf default router bgp neighbor swp1-4 peer-group hbn
nv set vrf default router bgp neighbor swp1-4 type unnumbered
nv set vrf default router bgp path-selection multipath aspath-ignore on
nv set vrf default router bgp peer-group hbn remote-as external
nv set vrf default router bgp address-family l2vpn-evpn enable on
nv set vrf default router bgp peer-group hbn address-family l2vpn-evpn enable on
nv config apply -y

按如下方式配置 SN2201 交换机(mgmt-switch):

nv set bridge domain br_default untagged 1
nv set interface swp1-3 link state up
nv set interface swp1-3 type swp
nv set interface swp1-3 bridge domain br_default
nv config apply -y

主机配置

注意: 确保工作节点服务器的 BIOS 设置中已启用 SR-IOV,并且服务器已调优至最佳性能。

注意: 确保所有工作节点的 BlueField-3 网卡具有相同的 PCIe 位置,并显示相同的接口名称。

虚拟机管理程序安装与配置

本参考部署指南(RDG)使用的虚拟机管理程序基于 Ubuntu 24.04 和 KVM。

本文档不详细说明 KVM 安装过程,但请注意,部署防火墙、跳板机和 MaaS 虚拟机(VM)需要以下 ISO:

  • Ubuntu 24.04
  • pfSense-CE-2.7.2

为实现该解决方案,必须在虚拟机管理程序上创建两个 Linux 桥接:

警告: 确保在受信任的 LAN 中为 lab-br 桥接接口配置 DHCP 记录,以分配 IP 地址。

  • lab-br – 将防火墙 VM 连接到受信任的 LAN。
  • mgmt-br – 将各个 VM 连接到主机管理网络。
虚拟机管理程序 netplan 配置
network:
    ethernets:
        eno1:
            dhcp4: false
        eno2:
            dhcp4: false
    bridges:
      lab-br:
         interfaces: [eno1]
         dhcp4: true
      mgmt-br:
         interfaces: [eno2]
         dhcp4: false
    version: 2

应用配置:

虚拟机管理程序控制台
$ sudo netplan apply

准备基础设施服务器

防火墙 VM - pfSense 安装与接口配置

将 pfSense CE(社区版)ISO 下载到虚拟机管理程序,然后进行软件安装。

建议规格:

  • vCPU:2
  • 内存:2GB
  • 存储:10GB
  • 网络接口
    • 连接到 lab-br 的桥接设备
    • 连接到 mgmt-br 的桥接设备

防火墙 VM 必须连接到虚拟机管理程序上的两个 Linux 桥接。在开始安装之前,请确保配置了三个类型为 “桥接设备” 的虚拟网络接口。每个接口应连接到不同的桥接(lab-brmgmt-br),如下图所示。

image-2025-2-12_16-9-24-1.png

安装完成后,设置向导会显示一个包含多个选项的菜单,例如“分配接口”和“重启系统”。在此阶段,配置防火墙 VM 的网络接口:

  1. 选择 选项 2:“设置接口 IP 地址”,并按如下方式配置接口:
    • WAN – 受信任的 LAN IP(静态/DHCP)
    • LAN – 静态 IP 10.0.110.254/24
  2. 接口配置完成后,使用主机管理网络内的 Web 浏览器访问防火墙 Web 界面,完成配置。

接下来,安装 跳板机 VM。该 VM 将作为运行浏览器的平台,用于访问防火墙的 Web 界面进行安装后配置。

跳板机 VM

建议规格:

  • vCPU:4
  • 内存:8GB
  • 存储:25GB
  • 网络接口:桥接设备,连接到 mgmt-br

步骤:

  1. 进行标准 Ubuntu 24.04 安装。在此设置的所有主机上使用以下登录凭据:

    用户名 密码
    depuser user
  2. 通过创建以下 Netplan 配置,启用互联网连接和 DNS 解析:

    警告: 在 MaaS VM 安装并配置完成之前,使用 10.0.110.254 作为临时 DNS 名称服务器。完成 MaaS 安装后,更新 Netplan 文件,将该地址替换为 MaaS IP:10.0.110.252

    跳板节点 netplan

    network:
        ethernets:
            enp1s0:
                dhcp4: false
                addresses: [10.0.110.253/24]
                nameservers:
                  search: [dpf.rdg.local.domain]
                  addresses: [10.0.110.254]
                routes:
                  - to: default
                    via: 10.0.110.254
        version: 2
    
  3. 应用配置:

    跳板节点控制台

    depuser@jump:~$ sudo netplan apply
    
  4. 更新并升级系统:

    跳板节点控制台

    depuser@jump:~$ sudo apt update -y
    depuser@jump:~$ sudo apt upgrade -y
    
  5. 安装并配置 Xfce 桌面环境XRDP(RDP 的补充包):

    跳板节点控制台

    depuser@jump:~$ sudo apt install -y xfce4 xfce4-goodies
    depuser@jump:~$ sudo apt install -y lightdm-gtk-greeter
    depuser@jump:~$ sudo apt install -y xrdp
    depuser@jump:~$ echo "xfce4-session" | tee .xsession
    depuser@jump:~$ sudo systemctl restart xrdp
    
  6. 安装 Firefox 以访问防火墙 Web 界面:

    跳板节点控制台

    $ sudo apt install -y firefox
    
  7. 安装并配置 NFS 服务器,使用 /mnt/dpf_share 目录:

    跳板节点控制台

    $ sudo apt install -y nfs-server
    $ sudo mkdir -m 777 /mnt/dpf_share
    $ sudo vi /etc/exports
    
  8. 将以下行添加到 /etc/exports

    跳板节点控制台

    /mnt/dpf_share
    

10.0.110.0/24(rw,sync,no_subtree_check)


1.  Restart the NFS server:

    **Jump Node Console**

    ```bash
    $ sudo systemctl restart nfs-server
    ```

1.  Create the directory `bfb` under `/mnt/dpf_share` with the same permissions as the parent directory:

    **Jump Node Console**

    ```bash
    $ sudo mkdir -m 777 /mnt/dpf_share/bfb
    ```

1.  Generate an SSH key pair for `depuser` in the jump node (later on will be imported to the admin user in MaaS to enable password-less login to the provisioned servers):

    **Jump Node Console**

    ```bash
    depuser@jump:~$ ssh-keygen -t rsa
    ```

1.  Finally, reboot the VM to load the graphical interface:

    **Jump Node Console**

    ```bash
    depuser@jump:~$ sudo reboot
    ```

> **Warning**
> After setting up the port-forwarding rules on the firewall (see next steps), you will be able to remotely log into the graphical interface of the Jump node via RDP and SSH.
> Please note that you can not be logged in to both the local graphical console and the RDP client at the same time. Be sure to log out before switching to an RDP connection.

##### Firewall VM – Web Configuration

From the Jump node graphical interface, open a Firefox web browser and go to the pfSense web UI (`http://10.0.110.254`; default credentials are `admin/pfsense`). You should see a page similar to the following:

> **Warning**
> The IP addresses from the trusted LAN network under "DNS servers" and "Interfaces - WAN" are blurred.

![image-2025-2-12_16-12-2-1.png](https://networking-docs.nvidia.com/sol/__attachments/a_43ff1f6aac9fd1326ad45117f4322ff50b4938c62f9d79b1b50bf4bc118a42a1/image-2025-2-12_16-12-2-1.png?cb=72847669dc817587926768fa9c1c950e)

Proceed with the following configurations:

> **Warning**
> The following screenshots display only part of the configuration view. Be sure to follow all of the steps mentioned below!

- Interfaces
  - WAN (lab-br) – mark "Enable interface", unmark "Block private networks and loopback addresses"
  - LAN (mgmt-br) – mark "Enable interface", "IPv4 configuration type": **Static IPv4** ("IPv4 Address": **10.0.110.254/24**, "IPv4 Upstream Gateway": **None**)
    ![image-2025-4-2_14-17-50.png](https://networking-docs.nvidia.com/sol/__attachments/a_44d1725bc0681f0aa001bbaffecd1ce9956d74357971faa4e5a29086ef43b312/image-2025-4-2_14-17-50.png?cb=4c59d549c54f2fc65c9714e4beea2143)
- Firewall:
  - NAT -> Port Forward -> Add rule -> "Interface": **WAN**, "Address Family": **IPv4**, "Protocol": **TCP**, "Destination": **WAN address**, "Destination port range": ("From port": **SSH**, "To port": **SSH**), "Redirect target IP": ("Type": **Address or Alias**, "Address": **10.0.110.253**), "Redirect target port": **SSH**, "Description": **NAT SSH**
  - NAT -> Port Forward -> Add rule -> "Interface": **WAN**, "Address Family": **IPv4**, "Protocol": **TCP**, "Destination": **WAN address**, "Destination port range": ("From port": **MS RDP**, "To port": **MS RDP**), "Redirect target IP": ("Type": **Address or Alias**, "Address": **10.0.110.253**), "Redirect target port": **MS RDP**, "Description": **NAT RDP**

##### MaaS VM

Suggested specifications:

- vCPU: 4
- RAM: 4GB
- Storage: 50GB
- Network interface: Bridge device, connected to `mgmt-br`

Procedure:

1.  Perform a regular Ubuntu installation on the MaaS VM.

1.  Create the following Netplan configuration to enable internet connectivity and DNS resolution:

    > **Warning**
    > Use `10.0.110.254` as a temporary DNS nameserver. After the MaaS installation, replace this with the MaaS IP address (`10.0.110.252`) in both the Jump and MaaS VM Netplan files.

    **MaaS netplan**

    ```yaml
    network:
        ethernets:
            enp1s0:
                dhcp4: false
                addresses: [10.0.110.252/24]
                nameservers:
                  search: [dpf.rdg.local.domain]
                  addresses: [10.0.110.254]
                routes:
                  - to: default
                    via: 10.0.110.254
        version: 2
    ```

1.  Apply the netplan configuration:

    **MaaS Console**

    ```bash
    depuser@maas:~$ sudo netplan apply
    ```

1.  Update and upgrade the system:

    **MaaS Console**

    ```bash
    depuser@maas:~$ sudo apt update -y
    depuser@maas:~$ sudo apt upgrade -y
    ```

1.  Install PostgreSQL and configure the database for MaaS:

    **MaaS Console**

    ```bash
    $ sudo -i
    # apt install -y postgresql
    # systemctl disable --now systemd-timesyncd
    # export MAAS_DBUSER=maasuser
    # export MAAS_DBPASS=maaspass
    # export MAAS_DBNAME=maas
    # sudo -i -u postgres psql -c "CREATE USER \"$MAAS_DBUSER\" WITH ENCRYPTED PASSWORD '$MAAS_DBPASS'"
    # sudo -i -u postgres createdb -O "$MAAS_DBUSER" "$MAAS_DBNAME"
    ```

1.  Install MaaS:

    **MaaS Console**

    ```bash
    # snap install maas
    ```

1.  Initialize MaaS:

    **MaaS Console**

    ```bash
    # maas init region+rack --maas-url http://10.0.110.252:5240/MAAS --database-uri "postgres://$MAAS_DBUSER:$MAAS_DBPASS@localhost/$MAAS_DBNAME"
    ```

1.  Create an admin account:

    **MaaS Console**

    ```bash
    # maas createadmin --username admin --password admin --email admin@example.com
    ```

1.  Save the admin API key:

    **MaaS Console**

    ```bash
    # maas apikey --username admin > admin-apikey
    ```

1.  Log in to the MaaS server:

    **MaaS Console**

    ```bash
    # maas login admin http://localhost:5240/MAAS "$(cat admin-apikey)"
    ```

1.  Configure MaaS (Substitute <Trusted_LAN_NTP_IP> and <Trusted_LAN_DNS_IP> with the IP addresses in your environment):

    **MaaS Console**

    ```bash
    # maas admin domain update maas name="dpf.rdg.local.domain"
    # maas admin maas set-config name=ntp_servers value="<Trusted_LAN_NTP_IP>"
    # maas admin maas set-config name=network_discovery value="disabled"
    # maas admin maas set-config name=upstream_dns value="<Trusted_LAN_DNS_IP>"
    # maas admin maas set-config name=dnssec_validation value="no"
    # maas admin maas set-config name=default_osystem value="ubuntu"
    ```

1.  Define and configure IP ranges and subnets:

    **MaaS Console**

    ```bash
    # maas admin ipranges create type=dynamic start_ip="10.0.110.51" end_ip="10.0.110.120"
    # maas admin ipranges create type=dynamic start_ip="10.0.110.21" end_ip="10.0.110.30"
    # maas admin ipranges create type=reserved start_ip="10.0.110.10" end_ip="10.0.110.10" comment="c-plane VIP"
    # maas admin ipranges create type=reserved start_ip="10.0.110.200" end_ip="10.0.110.200" comment="kamaji VIP"
    # maas admin ipranges create type=reserved start_ip="10.0.110.251" end_ip="10.0.110.254" comment="dpfmgmt"
    # maas admin vlan

update 0 untagged dhcp_on=True primary_rack=maas
# maas admin dnsresources create fqdn=kube-vip.dpf.rdg.local.domain ip_addresses=10.0.110.10
# maas admin dnsresources create fqdn=jump.dpf.rdg.local.domain ip_addresses=10.0.110.253
# maas admin dnsresources create fqdn=fw.dpf.rdg.local.domain ip_addresses=10.0.110.254

- Configure static DHCP leases for the worker nodes (replace MAC address as appropriate with your workers MGMT interface MAC):

  **MaaS Console**
  ```bash
  # maas admin reserved-ips create ip="10.0.110.21" mac_address="04:32:01:60:0d:da" comment="worker1"
  # maas admin reserved-ips create ip="10.0.110.22" mac_address="04:32:01:5f:cb:e0" comment="worker2"
  • Complete MaaS setup:

    1. Connect to the Jump node GUI and access the MaaS UI at http://10.0.110.252:5240/MAAS.
    2. On the first page, verify the "Region Name" and "DNS Forwarder," then continue.
    3. Make sure Ubuntu 24.04 LTS (amd64) and sync the image is selected. maas_setup_images_sync.png
    4. Import the previously generated SSH key (id_rsa.pub) for the depuser into the MaaS admin user profile and finalize the setup. import_sshkey.png
  • Update the DNS nameserver IP address in both Jump and MaaS VM Netplan files from 10.0.110.254 to 10.0.110.252 and reapply the configuration.

K8s Master VMs

Suggested specifications:

  • vCPU: 8
  • RAM: 16GB
  • Storage: 100GB
  • Network interface: Bridge device, connected to mgmt-br
  1. Before provisioning the Kubernetes (K8s) Master VMs with MaaS, create the required virtual disks with empty storage. Use the following one-liner to create three 100 GB QCOW2 virtual disks:

    Hypervisor Console

    $ for i in $(seq 1 3); do qemu-img create -f qcow2 /var/lib/libvirt/images/master$i.qcow2 100G; done
    

    This command generates the following disks in the /var/lib/libvirt/images/ directory:

    • master1.qcow2
    • master2.qcow2
    • master3.qcow2
  2. Configure VMs in virt-manager:

    1. Open virt-manager and create three virtual machines:
      • Assign the corresponding virtual disk (master1.qcow2, master2.qcow2, or master3.qcow2) to each VM.
      • Configure each VM with the suggested specifications (vCPU, RAM, storage, and network interface).
    2. During the VM setup, ensure the NIC is selected under the Boot Options tab. This ensures the VMs can PXE boot for MaaS provisioning.
    3. Once the configuration is complete, shut down all the VMs.
  3. After the VMs are created and configured, proceed to provision them via the MaaS interface. MaaS will handle the OS installation and further setup as part of the deployment process.

Provision Master VMs and Worker Nodes Using MaaS

Master VMs

Install virsh and Set Up SSH Access
  1. SSH to the MaaS VM from the Jump node:

    MaaS Console

    depuser@jump:~$ ssh maas
    depuser@maas:~$ sudo -i
    
  2. Install the virsh client to communicate with the hypervisor:

    MaaS Console

    # apt install -y libvirt-clients
    
  3. Generate an SSH key for the root user and copy it to the hypervisor user in the libvirtd group:

    MaaS Console

    # ssh-keygen -t rsa
    # ssh-copy-id ubuntu@<hypervisor_MGMT_IP>
    
  4. Verify SSH access and virsh communication with the hypervisor:

    MaaS Console

    # virsh -c qemu+ssh://ubuntu@<hypervisor_MGMT_IP>/system list --all
    

    Expected output:

    MaaS Console

     Id   Name          State
    ------------------------------
     1    fw     running
     2    jump   running
     3    maas   running
     -    master1       shut off
     -    master2       shut off
     -    master3       shut off
    
  5. Copy the SSH key to the required MaaS directory (for snap-based installations):

    MaaS Console

    # mkdir -p /var/snap/maas/current/root/.ssh
    # cp .ssh/id_rsa* /var/snap/maas/current/root/.ssh/
    
Get MAC Addresses of the Master VMs

Retrieve the MAC addresses of the Master VMs:

MaaS Console

# for i in $(seq 1 3); do virsh -c qemu+ssh://ubuntu@<hypervisor_MGMT_IP>/system dumpxml master$i | grep 'mac address'; done

Example output:

MaaS Console

<mac address='52:54:00:a9:9c:ef'/>
<mac address='52:54:00:19:6b:4d'/>
<mac address='52:54:00:68:39:7f'/>
Add Master VMs to MaaS
  1. Add the Master VMs to MaaS:

    Once added, MaaS will automatically start the newly added VMs commissioning (discovery and introspection).

    MaaS Console

    # maas admin machines create hostname=master1 architecture=amd64/generic mac_addresses='52:54:00:a9:9c:ef' power_type=virsh power_parameters_power_address=qemu+ssh://ubuntu@<hypervisor_MGMT_IP>/system power_parameters_power_id=master1 skip_bmc_config=1 testing_scripts=none
    Success.
    Machine-readable output follows:
    {
        "description": "",
        "status_name": "Commissioning",
    ...
        "status": 1,
    ...
        "system_id": "c3seyq",
    ...
        "fqdn": "master1.dpf.rdg.local.domain",
        "power_type": "virsh",
    ...
        "status_message": "Commissioning",
        "resource_uri": "/MAAS/api/2.0/machines/c3seyq/"
    }
    
    # maas admin machines create hostname=master2 architecture=amd64/generic mac_addresses='52:54:00:19:6b:4d' power_type=virsh power_parameters_power_address=qemu+ssh://ubuntu@<hypervisor_MGMT_IP>/system power_parameters_power_id=master2 skip_bmc_config=1 testing_scripts=none
    
    # maas admin machines create hostname=master3 architecture=amd64/generic mac_addresses='52:54:00:68:39:7f' power_type=virsh power_parameters_power_address=qemu+ssh://ubuntu@<hypervisor_MGMT_IP>/system power_parameters_power_id=master3 skip_bmc_config=1 testing_scripts=none
    
  2. Repeat the command for master2 and master3 with their respective MAC addresses.

  3. Verify commissioning by waiting for the status to change to "Ready" in MaaS. maas_masters_commission_virsh_updated.png

    After commissioning, the next phase is deployment (OS provisioning).

Configure Master VMs Network

To ensure persistence across reboots, assign a static IP address to the management interface of the master nodes.

For each Master VM:

  1. Navigate to Network and click "actions" near the management interface (a small arrowhead pointing down). Then select "Edit Physical".
    1. Configure as follows:
      1. Subnet: 10.0.110.0/24
  • IP Mode: Static Assign
  • Address: Assign 10.0.110.1 for master1, 10.0.110.2 for master2, and 10.0.110.3 for master3. image-2025-1-29_16-46-20.png
  1. Save the interface settings for each VM.

Deploy Master VMs Using Cloud-Init

  1. Use the following cloud-init script to configure the necessary software and ensure persistency:

    Master nodes cloud-init

    #cloud-config
    system_info:
      default_user:
        name: depuser
        passwd: "$6$jOKPZPHD9XbG72lJ$evCabLvy1GEZ5OR1Rrece3NhWpZ2CnS0E3fu5P1VcZgcRO37e4es9gmriyh14b8Jx8gmGwHAJxs3ZEjB0s0kn/"
        lock_passwd: false
        groups: [adm, audio, cdrom, dialout, dip, floppy, lxd, netdev, plugdev, sudo, video]
        sudo: ["ALL=(ALL) NOPASSWD:ALL"]
        shell: /bin/bash
    ssh_pwauth: True
    package_upgrade: true
    runcmd:
        - apt-get update
        - apt-get -y install nfs-common
    
  2. Deploy the master VMs:

    1. Select all three Master VMs → ActionsDeploy.
    2. Toggle Cloud-init user-data and paste the cloud-init script.
    3. Start the deployment and wait until the status to changes to "Ubuntu 24.04 LTS". maas_master_vms_deployment_before.png maas_master_vms_deployment_complete_updated.png

Verify Deployment

  • SSH into the Master VMs from the Jump node:

    Jump Node Console

    depuser@jump:~$ ssh master1
    depuser@master1:~$
    
  • Run sudo without a password:

    Master1 Console

    depuser@master1:~$ sudo -i
    root@master1:~#
    
  • Verify the installed packages:

    Master1 Console

    root@master1:~# apt list --installed | egrep 'nfs-common'
    nfs-common/noble,now 1:2.6.4-3ubuntu5 amd64 [installed]
    

Finalize Setup

Reboot the Master VMs to complete the provisioning.

Master1 Console

root@master1:~# reboot

Worker Nodes

Create Worker Machines in MaaS

  1. Add the worker nodes to MaaS using ipmi as the power type. Replace placeholders with your specific IPMI credentials and IP addresses:

    MAAS Console

    # maas admin machines create hostname=worker1 architecture=amd64 power_type=ipmi power_parameters_power_driver=LAN_2_0 power_parameters_power_user=<IPMI_username_worker1> power_parameters_power_pass=<IPMI_password_worker1> power_parameters_power_address=<IPMI_address_worker1>
    

    Output example:

    MaaS Console

    ...
    Success.
    Machine-readable output follows:
    {
        "description": "",
        "status_name": "Commissioning",
    ...
        "status": 1,
    ...
        "system_id": "pbskd3",
    ...
        "fqdn": "worker1.dpf.rdg.local.domain",
    ...
        "power_type": "ipmi",
    ...
        "resource_uri": "/MAAS/api/2.0/machines/pbskd3/"
    }
    
  2. Repeat the command for worker2 with its respective credentials:

    MAAS Console

    # maas admin machines create hostname=worker2 architecture=amd64 power_type=ipmi power_parameters_power_driver=LAN_2_0 power_parameters_power_user=<IPMI_username_worker2> power_parameters_power_pass=<IPMI_password_worker2> power_parameters_power_address=<IPMI_address_worker2>
    

Once added, MaaS automatically starts commissioning the worker nodes (discovery and introspection).

Warning: Please ensure the worker nodes are properly configured to allow PXE booting on their management interface. Specific BIOS settings might be required.

Create a Tag for Kernel Parameters

Create an entity called "Tag" to configure kernel parameters for the worker nodes.

  1. In the MaaS UI sidebar, go to Organization → Tags → Create New Tag and define:

    • "Tag name": compute_performance
    • "Kernel options":
  2. Substitute the values for isolcpus, nohz_full, and rcu_nocbs with the CPU cores from the NUMA node which the BlueField-3 is connected to:

    Warning: If you are not sure in which NUMA node BlueField is connected to, you can later perform this step after the worker node is deployed (although redeployment would be necessary).

    MAAS Console

    intel_iommu=on iommu=pt numa_balancing=disable processor.max_cstate=0 isolcpus=28-55,84-111 nohz_full=28-55,84-111 rcu_nocbs=28-55,84-111
    
  3. Apply the tag:

    1. Go to Machines → Select a worker node → ConfigurationEdit Tag → Select compute_performance → Save.
    2. Repeat for the other worker node.

Adjust Network Settings

For each worker node, configure the network interfaces:

  • Management Adapter:
    • Go to Network → Select the host management adapter (e.g., ens15f0) → Create Bridge
    • Name: br-dpu
    • Bridge Type: Standard
    • Subnet: 10.0.110.0/24
    • IP Mode: Dynamic
    • Save the interface

Repeat the previous steps for the second worker node.

Deploy Worker Nodes Using Cloud-Init

  1. Use the following cloud-init script for deployment:

    Worker node cloud-init

    #cloud-config
    system_info:
      default_user:
        name: depuser
        passwd: "$6$jOKPZPHD9XbG72lJ$evCabLvy1GEZ5OR1Rrece3NhWpZ2CnS0E3fu5P1VcZgcRO37e4es9gmriyh14b8Jx8gmGwHAJxs3ZEjB0s0kn/"
        lock_passwd: false
        groups: [adm, audio, cdrom, dialout, dip, floppy, lxd, netdev, plugdev, sudo, video]
        sudo: ["ALL=(ALL) NOPASSWD:ALL"]
        shell: /bin/bash
    ssh_pwauth: True
    package_upgrade: true
    
    runcmd:
      - apt-get update
      - apt-get -y install nfs-common
    
  2. Deploy the worker nodes by selecting the worker nodes in MaaS → Actions → Deploy → Customize options → Enable Cloud-init user-data → Paste the cloud-init script → Deploy.

Verify Deployment

After the deployment is complete, verify that the worker nodes have been deployed successfully using the following commands:

  • SSH without password from the jump node:

    Jump Node Console

    depuser@jump:~$ ssh worker1
    depuser@worker1:~$
    
  • Run sudo without a password:

    Worker1 Console

    depuser@worker1:~$ sudo -i
    root@worker1:~#
    
  • Validate that the nfs-common package was installed:

    Worker1 Console

    root@worker1:~# apt list --installed | grep 'nfs-common'
    nfs-common/noble,now 1:2.6.4-3ubuntu5 amd64 [installed]
    
  • Validate that /proc/cmdline is configured with the correct parameters and that IOMMU is indeed in passthrough mode:

    Worker1 Console

    root@worker1:~# cat /proc/cmdline
    BOOT_IMAGE=/boot/vmlinuz-6.8.0-90-generic root=UUID=5b74560e-130e-42db-a939-58a8d3003cbd ro intel_iommu=on iommu=pt numa_balancing=disable processor.max_cstate=0 isolcpus=28-55,84-111 nohz_full=28-55,84-111 rcu_nocbs=28-55,84-111
    
    root@worker1:~# dmesg | grep 'type: Passthrough'
    [    5.068360] iommu: Default domain type: Passthrough (set via kernel command line)
    

Finalize Deployment

Reboot the worker nodes:

Jump Node Console

root@worker1:~# reboot

The infrastructure is now ready for the K8s deployment.

maas_worker_nodes_after_deployment_updated_2.png

K8s Cluster Deployment and Configuration

Kubespray Deployment and Configuration

In this solution, the Kubernetes (K8s) cluster is deployed using a modified Kubespray (based on release v2.28.1) from the Jump Node, utilizing a non-root depuser account. These Kubespray modifications align with the DPF prerequisites as described in the User Manual and facilitate both cluster deployment and scaling.

Our modified Kubespray installs Flannel CNI as the primary Kubernetes network plugin.

  1. Download the modified Kubespray archive: modified_kubespray_v2.28.1.tar.gz.

  2. Extract the contents and navigate to the extracted directory:

    Jump Node Console

    $ tar -xzf /home/depuser/modified_kubespray_v2.28.1.tar.gz
    $ cd kubespray/
    depuser@jump:~/kubespray$
    
  3. Set the K8s API VIP address and DNS record. Replace the values with your own IP address and DNS record if they differ:

    Jump Node Console

    depuser@jump:~/kubespray$ sed -i '/# kube_vip_address:/s/.*/kube_vip_address: 10.0.110.10/' inventory/mycluster/group_vars/k8s_cluster/addons.yml
    depuser@jump:~/kubespray$ sed -i '/apiserver_loadbalancer_domain_name:/s/.*/apiserver_loadbalancer_domain_name: "kube-vip.dpf.rdg.local.domain"/' roles/kubespray_defaults/defaults/main/main.yml
    
  4. Install the necessary dependencies and set up the Python virtual environment:

    Jump Node Console

    depuser@jump:~/kubespray$ sudo apt -y install python3-pip jq python3.12-venv
    depuser@jump:~/kubespray$ python3 -m venv .venv
    depuser@jump:~/kubespray$ source .venv/bin/activate
    (.venv) depuser@jump:~/kubespray$ python3 -m pip install --upgrade pip
    (.venv) depuser@jump:~/kubespray$ pip install -U -r requirements.txt
    
  5. Review and edit the inventory/mycluster/hosts.yaml file to define the cluster nodes. The following is the configuration for this deployment:

    Warning

    • All of the nodes are already labeled and annotated by Kubespray as required by DPF prerequisites.
    • The worker nodes include additional kubelet configuration which will be applied during their deployment to achieve best performance, allowing:
      • Container in Guaranteed pods, requesting an integer number of CPUs, will have dedicated CPU cores on the node.
      • The NIC in our example is wired to NUMA node 1. To achieve maximum performance, we would need to prevent the pods from getting cores from NUMA 0, so we reserve these cores for the system using the reservedSystemCPUs option.
    • The workers under the kube_node group are marked with # to only deploy the cluster with control plane nodes at the beginning (worker nodes will be added later on after the various components that are necessary for the DPF system are installed).

    inventory/mycluster/hosts.yaml

    all:
      hosts:
        master1:
          ansible_host: 10.0.110.1
          ip: 10.0.110.1
          access_ip: 10.0.110.1
        master2:
          ansible_host: 10.0.110.2
          ip: 10.0.110.2
          access_ip: 10.0.110.2
        master3:
          ansible_host: 10.0.110.3
          ip: 10.0.110.3
          access_ip: 10.0.110.3
        worker1:
          ansible_host: 10.0.110.21
          ip: 10.0.110.21
          access_ip: 10.0.110.21
          node_labels:
            node-role.kubernetes.io/worker: ""
          kubelet_cpu_manager_policy: static
          kubelet_reservedSystemCPUs: 0-27,56-83
        worker2:
          ansible_host: 10.0.110.22
          ip: 10.0.110.22
          access_ip: 10.0.110.22
          node_labels:
            node-role.kubernetes.io/worker: ""
          kubelet_cpu_manager_policy: static
          kubelet_reservedSystemCPUs: 0-27,56-83
      children:
        kube_control_plane:
          hosts:
            master1:
            master2:
            master3:
        kube_node:
          hosts:
    #       worker1:
    #       worker2:
        etcd:
          hosts:
            master1:
            master2:
            master3:
        k8s_cluster:
          children:
            kube_control_plane:
            kube_node:
    

Deploying Cluster Using Kubespray Ansible Playbook

  1. Run the following command from the Jump Node to initiate the deployment process:

    Warning Ensure you are in the Python virtual environment (.venv) when running the command.

    Jump Node Console

    (.venv) depuser@jump:~/kubespray$ ansible-playbook -i inventory/mycluster/hosts.yaml --become --become-user=root cluster.yml
    
  2. It takes a while for this deployment to complete. Make sure there are no errors. Successful result example:

    image-2025-9-8_15-24-20.png

    Success It is recommended to keep the shell where Kubespray has been running open, as it will be useful later for scaling out the cluster and adding worker nodes.

K8s Deployment Verification

To simplify K8s cluster management from the Jump Host, set up kubectl with bash auto-completion.

  1. Copy kubectl and the kubeconfig file from master1 to the Jump Host:

    Jump Node Console

    ## Connect to master1
    depuser@jump:~$ ssh master1
    depuser@master1:~$ cp /usr/local/bin/kubectl /tmp/
    depuser@master1:~$ sudo cp /root/.kube/config /tmp/kube-config
    depuser@master1:~$ sudo chmod 644 /tmp/kube-config
    
  2. In another terminal tab, copy the files to the Jump Host:

    Jump Node Console

    depuser@jump:~$ scp master1:/tmp/kubectl /tmp/
    depuser@jump:~$ sudo chown root:root /tmp/kubectl
    depuser@jump:~$ sudo mv /tmp/kubectl /usr/local/bin/
    depuser@jump:~$ mkdir -p ~/.kube
    depuser@jump:~$ scp master1:/tmp/kube-config ~/.kube/config
    depuser@jump:~$ chmod 600 ~/.kube/config
    
  3. Enable bash auto-completion for kubectl:

    1. Verify if bash-completion is installed:

      Jump Node

Console

depuser@jump:~$ type _init_completion

如果已安装,输出将包含:

Jump Node Console

_init_completion is a function
  • 如果未安装,请安装:

    Jump Node Console

    depuser@jump:~$ sudo apt install -y bash-completion
    
  • 设置 kubectl 补全脚本:

    Jump Node Console

    depuser@jump:~$ kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl > /dev/null
    depuser@jump:~$ bash
    
  • 检查集群中节点的状态:

    Jump Node Console

    depuser@jump:~$ kubectl get nodes
    

    预期输出:

    Jump Node Console

    NAME      STATUS     ROLES           AGE   VERSION
    master1   Ready      control-plane   42m   v1.31.12
    master2   Ready      control-plane   41m   v1.31.12
    master3   Ready      control-plane   41m   v1.31.12
    
  • 检查所有命名空间中的Pod:

    Jump Node Console

    depuser@jump:~$ kubectl get pods -A
    

    预期输出:

    Jump Node Console

    NAMESPACE     NAME                              READY   STATUS    RESTARTS   AGE
    kube-system   coredns-776bb9db5d-cr56m          1/1     Running   0          12m
    kube-system   coredns-776bb9db5d-dnhct          1/1     Running   0          12m
    kube-system   dns-autoscaler-6ffb84bd6-5kvfc    1/1     Running   0          12m
    kube-system   kube-apiserver-master1            1/1     Running   0          14m
    kube-system   kube-apiserver-master2            1/1     Running   0          14m
    kube-system   kube-apiserver-master3            1/1     Running   0          13m
    kube-system   kube-controller-manager-master1   1/1     Running   1          14m
    kube-system   kube-controller-manager-master2   1/1     Running   1          14m
    kube-system   kube-controller-manager-master3   1/1     Running   1          13m
    kube-system   kube-flannel-fm7fr                1/1     Running   0          13m
    kube-system   kube-flannel-gtv6l                1/1     Running   0          13m
    kube-system   kube-flannel-nqvxs                1/1     Running   0          13m
    kube-system   kube-proxy-dspz6                  1/1     Running   0          14m
    kube-system   kube-proxy-tntld                  1/1     Running   0          13m
    kube-system   kube-proxy-ttfct                  1/1     Running   0          14m
    kube-system   kube-scheduler-master1            1/1     Running   1          14m
    kube-system   kube-scheduler-master2            1/1     Running   1          13m
    kube-system   kube-scheduler-master3            1/1     Running   1          13m
    kube-system   kube-vip-master1                  1/1     Running   0          14m
    kube-system   kube-vip-master2                  1/1     Running   0          13m
    kube-system   kube-vip-master3                  1/1     Running   0          13m
    

DPF Installation

Software Prerequisites and Required Variables

首先安装剩余的软件先决条件

Jump Node Console

## 连接到master1以复制kubespray部署期间安装的helm客户端工具
$ depuser@jump:~$ ssh master1
depuser@master1:~$ cp /usr/local/bin/helm /tmp/

## 在另一个标签页中
depuser@jump:~$ scp master1:/tmp/helm /tmp/
depuser@jump:~$ sudo chown root:root /tmp/helm
depuser@jump:~$ sudo mv /tmp/helm /usr/local/bin/

## 验证envsubst工具是否已安装
depuser@jump:~$ which envsubst
/usr/bin/envsubst

继续克隆doca-platform Git仓库(确保使用标签v25.10.0):

Jump Node Console

$ git clone https://github.com/NVIDIA/doca-platform.git
$ cd doca-platform
$ git checkout v25.10.0

切换到包含hbn-only readme.md的目录,所有命令都将从此位置运行:

Jump Node Console

$ cd docs/public/user-guides/host-trusted/use-cases/hbn

使用以下文件定义安装所需的变量:

警告:将以下文件中的变量值替换为适合您设置的值。特别注意 DPU_P0DPUCLUSTER_INTERFACE

manifests/00-env-vars/envvars.env

## DPU集群负载均衡器使用的虚拟IP。必须是管理子网中的保留IP,且不由DHCP分配。
export DPUCLUSTER_VIP=10.0.110.200

## DPU_P0是DPU的第一个端口名称。此名称在所有工作节点上必须相同。
export DPU_P0=ens5f0np0

## DPUCluster负载均衡器监听的接口。应为控制平面节点的管理接口。
export DPUCLUSTER_INTERFACE=ens160

## 用作BFB存储的NFS服务器的IP地址。
export NFS_SERVER_IP=10.0.110.253

## NVIDIA Helm图表仓库的仓库URL。
## 通常是NVIDIA Helm NGC仓库。出于开发目的,可以设置为其他仓库。
export HELM_REGISTRY_REPO_URL=https://helm.ngc.nvidia.com/nvidia/doca

## HBN容器镜像的仓库URL。
## 通常是NVIDIA NGC仓库。出于开发目的,可以设置为其他仓库。
export HBN_NGC_IMAGE_URL=nvcr.io/nvidia/doca/doca_hbn

## DPF REGISTRY是DPF Operator Chart所在的Helm仓库URL。
## 通常是NVIDIA Helm NGC仓库。出于开发目的,可以设置为其他仓库。
export REGISTRY=https://helm.ngc.nvidia.com/nvidia/doca

## DPF TAG是本指南中部署的DPF组件的版本。
export TAG=v25.10.0

## 在`bfb.yaml`中使用并由DPUSet链接的BFB的URL。
export BFB_URL="https://content.mellanox.com/BlueField/BFBs/Ubuntu24.04/bf-bundle-3.2.1-34_25.11_ubuntu-24.04_64k_prod.bfb"

导出安装所需的环境变量:

Jump Node Console

$ source manifests/00-env-vars/envvars.env

DPF Operator Installation

Create Storage Required by the DPF Operator

以下YAML文件定义了DPF Operator所需的存储(用于BFB镜像)。

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: bfb-pv
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  nfs:
    path: /mnt/dpf_share/bfb
    server: $NFS_SERVER_IP
  persistentVolumeReclaimPolicy: Delete
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: bfb-pvc
  namespace: dpf-operator-system
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  volumeMode: Filesystem
  storageClassName: ""

运行以下命令创建DPF Operator的命名空间。使用 envsubst 替换环境变量并应用YAML文件:

Jump Node Console

$ kubectl create namespace dpf-operator-system
$ cat manifests/01-dpf-operator-installation/*.yaml | envsubst | kubectl apply -f -

Additional Dependencies

  1. DPF Operator需要在Kubernetes环境中正常运行多个先决组件。从DPF v25.7开始,所有Helm依赖项已从DPF chart中移除。这意味着所有

dependencies must be installed manually before installing the DPF chart itself. The following commands describe an opiniated approach to install those dependencies (for more information, check: Helm Prerequisites - NVIDIA Docs).

  1. Install helmfile binary:

    Jump Node Console

    $ wget https://github.com/helmfile/helmfile/releases/download/v1.1.2/helmfile_1.1.2_linux_amd64.tar.gz
    $ tar  -xvf helmfile_1.1.2_linux_amd64.tar.gz
    $ sudo mv ./helmfile /usr/local/bin/
    
  2. Change directory to doca-platform:

    Use another shell from the one where you run all the other installation commands for DPF.

    Jump Node Console

    $ cd doca-platform/
    
  3. Install Helm dependencies using the following command:

    Jump Node Console

    $ make HELMFILE_FILE=deploy/helmfiles/prereqs.yaml test-deploy-helmfile
    

DPF Operator Deployment

Run the following commands to install the DPF Operator:

Jump Node Console

$ helm repo add --force-update dpf-repository ${REGISTRY}
$ helm repo update
$ helm upgrade --install -n dpf-operator-system dpf-operator dpf-repository/dpf-operator --version=$TAG

Verify the DPF Operator installation by ensuring the deployment is available and all pods are ready:

You may need to run the following verification commands multiple times to confirm that all conditions are met.

Jump Node Console

$ kubectl rollout status deployment --namespace dpf-operator-system dpf-operator-controller-manager
deployment "dpf-operator-controller-manager" successfully rolled out

$ kubectl wait --for=condition=ready --namespace dpf-operator-system pods --all
pod/argo-cd-argocd-application-controller-0 condition met
pod/argo-cd-argocd-redis-6c6b84f6fb-dhfqx condition met
pod/argo-cd-argocd-repo-server-65cfb96746-fjqhn condition met
pod/argo-cd-argocd-server-5bbdb4b6b9-hbbsw condition met
pod/dpf-operator-controller-manager-5dd7555c6d-gpbss condition met
pod/kamaji-95587fbc7-xzfmn condition met
pod/kamaji-etcd-0 condition met
pod/kamaji-etcd-1 condition met
pod/kamaji-etcd-2 condition met
pod/maintenance-operator-74bd5774b7-v4kct condition met
pod/node-feature-discovery-gc-6b48f49cc4-l4p2w condition met
pod/node-feature-discovery-master-747d789485-x5hcb condition met

DPF System Installation

This section involves creating the DPF system components and some basic infrastructure required for a functioning DPF-enabled cluster.

The following YAML files define the DPFOperatorConfig to install the DPF System components and the DPUCluster to serve as Kubernetes control plane for DPU nodes.

Note that to achieve high performance results you need to adjust the operatorconfig.yaml to support MTU 9000.

---
apiVersion: operator.dpu.nvidia.com/v1alpha1
kind: DPFOperatorConfig
metadata:
  name: dpfoperatorconfig
  namespace: dpf-operator-system
spec:
  provisioningController:
    bfbPVCName: "bfb-pvc"
    dmsTimeout: 900
  kamajiClusterManager:
    disable: false
  networking:
    highSpeedMTU: 9000
---
apiVersion: provisioning.dpu.nvidia.com/v1alpha1
kind: DPUCluster
metadata:
  name: dpu-cplane-tenant1
  namespace: dpu-cplane-tenant1
spec:
  type: kamaji
  maxNodes: 10
  clusterEndpoint:
    # deploy keepalived instances on the nodes that match the given nodeSelector.
    keepalived:
      # interface on which keepalived will listen. Should be the oob interface of the control plane node.
      interface: $DPUCLUSTER_INTERFACE
      # Virtual IP reserved for the DPU Cluster load balancer. Must not be allocatable by DHCP.
      vip: $DPUCLUSTER_VIP
      # virtualRouterID must be in range [1,255], make sure the given virtualRouterID does not duplicate with any existing keepalived process running on the host
      virtualRouterID: 126
      nodeSelector:
        node-role.kubernetes.io/control-plane: ""

Create a namespace for the Kubernetes control plane of the DPU nodes:

Jump Node Console

$ kubectl create ns dpu-cplane-tenant1

Apply the previous YAML files:

Jump Node Console

$ cat manifests/02-dpf-system-installation/*.yaml | envsubst | kubectl apply -f -

Verify the DPF system by ensuring that the provisioning and DPUService controller manager deployments are available, all other deployments in the DPF Operator system are available, and the DPUCluster is ready for nodes to join.

Jump Node Console

$ kubectl rollout status deployment --namespace dpf-operator-system dpf-provisioning-controller-manager dpuservice-controller-manager
deployment "dpf-provisioning-controller-manager" successfully rolled out
deployment "dpuservice-controller-manager" successfully rolled out

$ kubectl rollout status deployment --namespace dpf-operator-system
deployment "argo-cd-argocd-applicationset-controller" successfully rolled out
deployment "argo-cd-argocd-redis" successfully rolled out
deployment "argo-cd-argocd-repo-server" successfully rolled out
deployment "argo-cd-argocd-server" successfully rolled out
deployment "dpf-operator-controller-manager" successfully rolled out
deployment "dpf-provisioning-controller-manager" successfully rolled out
deployment "dpuservice-controller-manager" successfully rolled out
deployment "kamaji" successfully rolled out
deployment "kamaji-cm-controller-manager" successfully rolled out
deployment "maintenance-operator" successfully rolled out
deployment "node-feature-discovery-gc" successfully rolled out
deployment "node-feature-discovery-master" successfully rolled out

$ kubectl wait --for=condition=ready --namespace dpu-cplane-tenant1 dpucluster --all
dpucluster.provisioning.dpu.nvidia.com/dpu-cplane-tenant1 condition met

Install components to enable Accelerated Interfaces

The HBN service can accelerate pod traffic by attaching a VF to each pod, which offloads flows to the DPU. This section details the components needed to connect pods to HBN.

Install Multus and SRIOV Network Operator using NVIDIA Network Operator.

Start by adding the NVIDIA Network Operator Helm repository:

Jump Node Console

$ helm repo add nvidia https://helm.ngc.nvidia.com/nvidia --force-update

The following network-operator.yaml values file are applied:

nfd:
  enabled: false
  deployNodeFeatureRules: false
sriovNetworkOperator:
  enabled: true
sriov-network-operator:
  operator:
    affinity:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
            - matchExpressions:
                - key: node-role.kubernetes.io/master
                  operator: Exists
            - matchExpressions:
                - key: node-role.kubernetes.io/control-plane

operator: Exists crds: enabled: true sriovOperatorConfig: deploy: true configDaemonNodeSelector: null operator: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: node-role.kubernetes.io/master operator: Exists - matchExpressions: - key: node-role.kubernetes.io/control-plane operator: Exists

Deploy the operator:

Jump Node Console

$ helm upgrade --no-hooks --install --create-namespace --namespace nvidia-network-operator network-operator nvidia/network-operator --version 25.7.0 -f ./manifests/03-enable-accelerated-interfaces/helm-values/network-operator.yml

Ensure that all the pods in the nvidia-network-operator namespace are ready:

Jump Node Console

$ kubectl wait --for=condition=ready --namespace nvidia-network-operator pods --all
pod/network-operator-66b5cdbc79-8bv9c condition met
pod/network-operator-sriov-network-operator-6b87b5cf96-j6xx5 condition met

The following NicClusterPolicy and SriovNetworkNodePolicy configuration files should be applied.

---
apiVersion: mellanox.com/v1alpha1
kind: NicClusterPolicy
metadata:
  name: nic-cluster-policy
spec:
  secondaryNetwork:
    multus:
      image: multus-cni
      imagePullSecrets: []
      repository: ghcr.io/k8snetworkplumbingwg
      version: v3.9.3
---
apiVersion: sriovnetwork.openshift.io/v1
kind: SriovNetworkNodePolicy
metadata:
  name: bf3-p0-vfs
  namespace: nvidia-network-operator
spec:
  nicSelector:
    deviceID: "a2dc"
    vendor: "15b3"
    pfNames:
    - $DPU_P0#2-45
  nodeSelector:
    node-role.kubernetes.io/worker: ""
  numVfs: 46
  resourceName: bf3-p0-vfs
  isRdma: true
  externallyManaged: true
  deviceType: netdevice
  linkType: eth

Apply the following configuration files:

Jump Node Console

$ cat manifests/03-enable-accelerated-interfaces/*.yaml | envsubst | kubectl apply -f -

Verify the DPF system by ensuring that all pods in nvidia-network-operator namespace are ready, and that the following DaemonSets have been successfully rolled out:

Jump Node Console

$ kubectl wait --for=condition=ready --namespace nvidia-network-operator pods --all
pod/network-operator-7bc7b45d67-jftqg condition met
pod/network-operator-sriov-network-operator-86c9cd4899-5blhf condition met

$ kubectl rollout status daemonset --namespace nvidia-network-operator kube-multus-ds sriov-network-config-daemon sriov-device-plugin
daemon set "kube-multus-ds" successfully rolled out
daemon set "sriov-network-config-daemon" successfully rolled out
daemon set "sriov-device-plugin" successfully rolled out

DPU Deployment Installation

Before deploying the objects under manifests/04-dpudeployment-installation/ directory, a few adjustments are needed to achieve better performance results.

Edit the DPUFlavor using the following YAML:

Warning: The parameter NUM_VF_MSIX is set to 48 in the provided example, which is suitable for the servers used in this RDG. Set this value to match the physical number of cores in the NUMA node where the NIC is located.

---
apiVersion: provisioning.dpu.nvidia.com/v1alpha1
kind: DPUFlavor
metadata:
  name: hbn-$TAG
  namespace: dpf-operator-system
spec:
  bfcfgParameters:
  - UPDATE_ATF_UEFI=yes
  - UPDATE_DPU_OS=yes
  - WITH_NIC_FW_UPDATE=yes
  configFiles:
  - operation: override
    path: /etc/mellanox/mlnx-bf.conf
    permissions: "0644"
    raw: |
      ALLOW_SHARED_RQ="no"
      IPSEC_FULL_OFFLOAD="no"
      ENABLE_ESWITCH_MULTIPORT="yes"
  - operation: override
    path: /etc/mellanox/mlnx-ovs.conf
    permissions: "0644"
    raw: |
      CREATE_OVS_BRIDGES="no"
      OVS_DOCA="yes"
  - operation: override
    path: /etc/mellanox/mlnx-sf.conf
    permissions: "0644"
    raw: ""
  grub:
    kernelParameters:
    - console=hvc0
    - console=ttyAMA0
    - earlycon=pl011,0x13010000
    - fixrttc
    - net.ifnames=0
    - biosdevname=0
    - iommu.passthrough=1
    - cgroup_no_v1=net_prio,net_cls
    - hugepagesz=2048kB
    - hugepages=8072
  nvconfig:
  - device: '*'
    parameters:
    - PF_BAR2_ENABLE=0
    - PER_PF_NUM_SF=1
    - PF_TOTAL_SF=20
    - PF_SF_BAR_SIZE=10
    - NUM_PF_MSIX_VALID=0
    - PF_NUM_PF_MSIX_VALID=1
    - PF_NUM_PF_MSIX=228
    - INTERNAL_CPU_MODEL=1
    - INTERNAL_CPU_OFFLOAD_ENGINE=0
    - SRIOV_EN=1
    - NUM_OF_VFS=46
    - LAG_RESOURCE_ALLOCATION=1
    - LINK_TYPE_P1=ETH
    - LINK_TYPE_P2=ETH
    - NUM_VF_MSIX=48
  ovs:
    rawConfigScript: |
      _ovs-vsctl() {
        ovs-vsctl --no-wait --timeout 15 "$@"
      }

      _ovs-vsctl set Open_vSwitch . other_config:doca-init=true
      _ovs-vsctl set Open_vSwitch . other_config:dpdk-max-memzones=50000
      _ovs-vsctl set Open_vSwitch . other_config:hw-offload=true
      _ovs-vsctl set Open_vSwitch . other_config:pmd-quiet-idle=true
      _ovs-vsctl set Open_vSwitch . other_config:max-idle=20000
      _ovs-vsctl set Open_vSwitch . other_config:max-revalidator=5000
      _ovs-vsctl --if-exists del-br ovsbr1
      _ovs-vsctl --if-exists del-br ovsbr2
      _ovs-vsctl --may-exist add-br br-sfc
      _ovs-vsctl set bridge br-sfc datapath_type=netdev
      _ovs-vsctl set bridge br-sfc fail_mode=secure
      _ovs-vsctl --may-exist add-port br-sfc p0
      _ovs-vsctl set Interface p0 type=dpdk
      _ovs-vsctl set Interface p0 mtu_request=9216
      _ovs-vsctl set Port p0 external_ids:dpf-type=physical
      _ovs-vsctl --may-exist add-port br-sfc p1
      _ovs-vsctl set Interface p1 type=dpdk
      _ovs-vsctl set Interface p1 mtu_request=9216
      _ovs-vsctl set Port p1 external_ids:dpf-type=physical
      _ovs-vsctl --may-exist add-br br-hbn
      _ovs-vsctl set bridge br-hbn datapath_type=netdev
      _ovs-vsctl set bridge br-hbn fail_mode=secure

The rest of the configuration files remain the same.

As explained in the introduction, they create service chains to connect two virtual functions (VF10 on PF0 and VF10 on PF1) to the outer fabric through HBN, providing EVPN VXLAN overlay, VNI based isolation, and ECMP redundancy through both DPU uplinks (p0 and p1).

These are the configuration files:

  • BFB to download BlueField BFB to a shared volume.

    ---
    apiVersion: provisioning.dpu.nvidia.com/v1alpha1
    kind: BFB
    metadata:
      name: bf-bundle-$TAG
      namespace: dpf-operator-system
    spec:
      url: $BFB_URL
    
  • HBN DPUServiceConfig and DPUServiceTemplate to deploy HBN workloads to the DPU.

    ---
    apiVersion: svc.dpu.nvidia.com/v1alpha1
    kind: DPUServiceConfiguration
    metadata:
      name: doca-hbn
      namespace: dpf-operator-system
    spec:
      deploymentServiceName: "doca-hbn"
      serviceConfiguration:
        serviceDaemonSet:
          annotations:
            k8s.v1.cni.cncf.io/networks: |-
              [
              {"name": "iprequest", "interface": "ip_lo", "cni-args": {"poolNames": ["loopback"], "poolType": "cidrpool"}},
              {"name": "iprequest", "interface": "ip_pf0vf10", "cni-args": {"poolNames": ["pool1"], "poolType": "cidrpool", "allocateDefaultGateway": true}},
              {"name": "iprequest", "interface": "ip_pf1vf10", "cni-args": {"poolNames": ["pool2"], "poolType": "cidrpool", "allocateDefaultGateway": true}}
              ]
        helmChart:
          values:
            configuration:
              perDPUValuesYAML: |
                - hostnamePattern: "*"
                  values:
                    bgp_peer_group: hbn
                    vrf1: RED
                    vrf2: BLUE
                    l3vni1: 100001
                    l3vni2: 100002
              startupYAMLJ2: |
                - header:
                    model: bluefield
                    nvue-api-version: nvue_v1
                    rev-id: 1.0
                    version: HBN 2.4.0
                - set:
                    evpn:
                      enable: on
                      route-advertise: {}
                    interface:
                      lo:
                        ip:
                          address:
                            {{ ipaddresses.ip_lo.ip }}/32: {}
                        type: loopback
                      p0_if,p1_if,pf0vf10_if,pf1vf10_if:
                        type: swp
                        link:
                          mtu: 9000
                      pf0vf10_if:
                        ip:
                          address:
                            {{ ipaddresses.ip_pf0vf10.cidr }}: {}
                        vrf: {{ config.vrf1 }}
                      pf1vf10_if:
                        ip:
                          address:
                            {{ ipaddresses.ip_pf1vf10.cidr }}: {}
                        vrf: {{ config.vrf2 }}
                    nve:
                      vxlan:
                        arp-nd-suppress: on
                        enable: on
                        source:
                          address:
    

{{ ipaddresses.ip_lo.ip }} router: bgp: enable: on graceful-restart: mode: full vrf: default: router: bgp: address-family: ipv4-unicast: enable: on redistribute: connected: enable: on multipaths: ebgp: 16 l2vpn-evpn: enable: on autonomous-system: {{ ( ipaddresses.ip_lo.ip.split(".")[3] | int ) + 65101 }} enable: on neighbor: p0_if: peer-group: {{ config.bgp_peer_group }} type: unnumbered address-family: l2vpn-evpn: enable: on add-path-tx: off p1_if: peer-group: {{ config.bgp_peer_group }} type: unnumbered address-family: l2vpn-evpn: enable: on add-path-tx: off path-selection: multipath: aspath-ignore: on peer-group: {{ config.bgp_peer_group }}: address-family: ipv4-unicast: enable: on l2vpn-evpn: enable: on remote-as: external router-id: {{ ipaddresses.ip_lo.ip }} {{ config.vrf1 }}: evpn: enable: on vni: {{ config.l3vni1 }}: {} router: bgp: address-family: ipv4-unicast: enable: on redistribute: connected: enable: on route-export: to-evpn: enable: on autonomous-system: {{ ( ipaddresses.ip_lo.ip.split(".")[3] | int ) + 65101 }} enable: on {{ config.vrf2 }}: evpn: enable: on vni: {{ config.l3vni2 }}: {} router: bgp: address-family: ipv4-unicast: enable: on redistribute: connected: enable: on route-export: to-evpn: enable: on autonomous-system: {{ ( ipaddresses.ip_lo.ip.split(".")[3] | int ) + 65101 }} enable: on

interfaces:

  • name: p0_if network: mybrhbn
  • name: p1_if network: mybrhbn
  • name: pf0vf10_if network: mybrhbn
  • name: pf1vf10_if network: mybrhbn

```yaml
---
apiVersion: svc.dpu.nvidia.com/v1alpha1
kind: DPUServiceTemplate
metadata:
  name: doca-hbn
  namespace: dpf-operator-system
spec:
  deploymentServiceName: "doca-hbn"
  helmChart:
    source:
      repoURL: $HELM_REGISTRY_REPO_URL
      version: 1.0.5
      chart: doca-hbn
    values:
      image:
        repository: $HBN_NGC_IMAGE_URL
        tag: 3.2.1-doca3.2.1
      resources:
        memory: 6Gi
        nvidia.com/bf_sf: 4
  • Physical Interfaces for physical ports on the DPU.
---
apiVersion: svc.dpu.nvidia.com/v1alpha1
kind: DPUServiceInterface
metadata:
  name: p0
  namespace: dpf-operator-system
spec:
  template:
    spec:
      template:
        metadata:
          labels:
            uplink: "p0"
        spec:
          interfaceType: physical
          physical:
            interfaceName: p0
---
apiVersion: svc.dpu.nvidia.com/v1alpha1
kind: DPUServiceInterface
metadata:
  name: p1
  namespace: dpf-operator-system
spec:
  template:
    spec:
      template:
        metadata:
          labels:
            uplink: "p1"
        spec:
          interfaceType: physical
          physical:
            interfaceName: p1
---
apiVersion: svc.dpu.nvidia.com/v1alpha1
kind: DPUServiceInterface
metadata:
  name: pf0vf10-rep
  namespace: dpf-operator-system
spec:
  template:
    spec:
      template:
        metadata:
          labels:
            vf: "pf0vf10"
        spec:
          interfaceType: vf
          vf:
            parentInterfaceRef: p0
            pfID: 0
            vfID: 10
---
apiVersion: svc.dpu.nvidia.com/v1alpha1
kind: DPUServiceInterface
metadata:
  name: pf1vf10-rep
  namespace: dpf-operator-system
spec:
  template:
    spec:
      template:
        metadata:
          labels:
            vf: "pf1vf10"
        spec:
          interfaceType: vf
          vf:
            parentInterfaceRef: p1
            pfID: 1
            vfID: 10
  • DPU Service IPAM objects to set up IP Address Management on the DPUCluster.
---
apiVersion: svc.dpu.nvidia.com/v1alpha1
kind: DPUServiceIPAM
metadata:
  name: pool1
  namespace: dpf-operator-system
spec:
  ipv4Network:
    network: "10.0.121.0/24"
    gatewayIndex: 2
    prefixSize: 29
---
apiVersion: svc.dpu.nvidia.com/v1alpha1
kind: DPUServiceIPAM
metadata:
  name: pool2
  namespace: dpf-operator-system
spec:
  ipv4Network:
    network: "10.0.122.0/24"
    gatewayIndex: 2
    prefixSize: 29
---
apiVersion: svc.dpu.nvidia.com/v1alpha1
kind: DPUServiceIPAM
metadata:
  name: loopback
  namespace: dpf-operator-system
spec:
  ipv4Network:
    network: "11.0.0.0/24"
    prefixSize: 32

Apply all of the YAML files mentioned above using the following command:

Jump Node Console
$ cat manifests/04-dpudeployment-installation/*.yaml | envsubst | kubectl apply -f -

Verify the DPUService installation by ensuring that:

  • HBN DPUService is created and reconciled
  • DPUServiceIPAMs are reconciled
  • DPUServiceInterfaces are reconciled, and
  • DPUServiceChains are reconciled.

Notes These verification commands may need to be run multiple times to ensure the conditions are met.

Jump Node Console
## Successful output will require some few minutes of waiting##
$ kubectl wait --for=condition=ApplicationsReconciled --namespace dpf-operator-system dpuservices -l svc.dpu.nvidia.com/owned-by-dpudeployment=dpf-operator-system_hbn-only
dpuservice.svc.dpu.nvidia.com/doca-hbn-g5md8 condition met

$ kubectl wait --for=condition=DPUIPAMObjectReconciled --namespace dpf-operator-system dpuserviceipam --all
dpuserviceipam.svc.dpu.nvidia.com/loopback condition met
dpuserviceipam.svc.dpu.nvidia.com/pool1 condition met
dpuserviceipam.svc.dpu.nvidia.com/pool2 condition met

$ kubectl wait --for=condition=ServiceInterfaceSetReconciled --namespace dpf-operator-system dpuserviceinterface --all
dpuserviceinterface.svc.dpu.nvidia.com/hbn-only-doca-hbn-p0-if condition met
dpuserviceinterface.svc.dpu.nvidia.com/hbn-only-doca-hbn-p1-if condition met
dpuserviceinterface.svc.dpu.nvidia.com/hbn-only-doca-hbn-pf0vf10-if condition met
dpuserviceinterface.svc.dpu.nvidia.com/hbn-only-doca-hbn-pf1vf10-if condition met
dpuserviceinterface.svc.dpu.nvidia.com/p0 condition met
dpuserviceinterface.svc.dpu.nvidia.com/p1 condition met
dpuserviceinterface.svc.dpu.nvidia.com/pf0vf10-rep condition met
dpuserviceinterface.svc.dpu.nvidia.com/pf1vf10-rep condition met

$ kubectl wait --for=condition=ServiceChainSetReconciled --namespace dpf-operator-system dpuservicechain --all
dpuservicechain.svc.dpu.nvidia.com/hbn-only condition met

K8s Cluster Scale-out

At this point, workers should be added to the cluster. As they are added, DPU will be provisioned and DPUServices will begin to be spun up.

For the scale-out procedure, return to the shell where Kubespray was previously used to deploy the cluster. Uncomment the workers under the kube_node group in the hosts.yaml file, then add the worker nodes to the cluster:

Ensure you are in the Python virtual environment (.venv) when running the command.

Jump Node Console
(.venv) depuser@jump:~/kubespray$ cat inventory/mycluster/hosts.yaml
...
   kube_node:
     hosts:
       worker1:
       worker2:
...

(.venv) depuser@jump:~/kubespray$ ansible-playbook -i inventory/mycluster/hosts.yaml --become --become-user=root scale.yml

The scale-out process should not take long. A successful run should produce output similar to the following:

image

image-2025-9-9_10-57-40.png

要跟踪DPU配置的进度,运行以下命令检查其当前阶段:

Jump Node Console
$ watch -n10 "kubectl describe dpu -n dpf-operator-system | grep 'Node Name\|Type\|Last\|Phase'"

  Dpu Node Name:                                       worker1
    Type:       InternalIP
    Type:       Hostname
    Last Transition Time:  2025-12-25T20:29:38Z
    Type:                  Ready
    Last Transition Time:  2025-12-25T19:59:18Z
    Type:                  BFBPrepared
    Last Transition Time:  2025-12-25T19:58:41Z
    Type:                  BFBReady
    Last Transition Time:  2025-12-25T20:25:57Z
    Type:                  DPUClusterReady
    Last Transition Time:  2025-12-25T19:58:41Z
    Type:                  Initialized
    Last Transition Time:  2025-12-25T19:59:16Z
    Type:                  NodeEffectReady
    Last Transition Time:  2025-12-25T20:29:38Z
    Type:                  NodeEffectRemoved
    Last Transition Time:  2025-12-25T20:15:31Z
    Type:                  CheckedHostRebootNeed
    Last Transition Time:  2025-12-25T19:59:18Z
    Type:                  FWConfigured
    Last Transition Time:  2025-12-25T20:25:45Z
    Type:                  HostNetworkReady
    Last Transition Time:  2025-12-25T19:59:17Z
    Type:                  InterfaceInitialized
    Last Transition Time:  2025-12-25T20:15:29Z
    Type:                  OSInstalled
    Last Transition Time:  2025-12-25T20:24:25Z
    Type:                  Rebooted
  Phase:                Ready
  Dpu Node Name:                                       worker2
    Type:       InternalIP
    Type:       Hostname
    Last Transition Time:  2025-12-25T20:28:37Z
    Type:                  Ready
    Last Transition Time:  2025-12-25T19:58:38Z
    Type:                  BFBPrepared
    Last Transition Time:  2025-12-25T19:58:11Z
    Type:                  BFBReady
    Last Transition Time:  2025-12-25T20:24:31Z
    Type:                  DPUClusterReady
    Last Transition Time:  2025-12-25T19:58:11Z
    Type:                  Initialized
    Last Transition Time:  2025-12-25T19:58:36Z
    Type:                  NodeEffectReady
    Last Transition Time:  2025-12-25T20:28:37Z
    Type:                  NodeEffectRemoved
    Last Transition Time:  2025-12-25T20:15:01Z
    Type:                  CheckedHostRebootNeed
    Last Transition Time:  2025-12-25T19:58:38Z
    Type:                  FWConfigured
    Last Transition Time:  2025-12-25T20:24:12Z
    Type:                  HostNetworkReady
    Last Transition Time:  2025-12-25T19:58:36Z
    Type:                  InterfaceInitialized
    Last Transition Time:  2025-12-25T20:14:58Z
    Type:                  OSInstalled
    Last Transition Time:  2025-12-25T20:22:51Z
    Type:                  Rebooted
  Phase:                Ready

一旦两个DPU都进入"Ready"阶段,验证DPU已成功配置:

Jump Node Console
$ kubectl wait --for=condition=ready --namespace dpf-operator-system dpu --all
dpu.provisioning.dpu.nvidia.com/worker1-mt2414600gq5 condition met
dpu.provisioning.dpu.nvidia.com/worker2-mt2412600eq8 condition met

确保以下DaemonSet有2个就绪副本:

Jump Node Console
$ kubectl wait ds --for=jsonpath='{.status.numberReady}'=2 --namespace nvidia-network-operator kube-multus-ds sriov-network-config-daemon sriov-device-plugin
daemonset.apps/kube-multus-ds condition met
daemonset.apps/sriov-network-config-daemon condition met
daemonset.apps/sriov-device-plugin condition met

最后,验证所有DPUServicesDPUServiceIPAMsDPUServiceInterfacesDPUServiceChains对象现在都处于Ready状态:

Jump Node Console
$ kubectl wait --for=condition=ApplicationsReady --namespace dpf-operator-system dpuservices -l svc.dpu.nvidia.com/owned-by-dpudeployment=dpf-operator-system_hbn-only
dpuservice.svc.dpu.nvidia.com/doca-hbn-g5md8 condition met
$ kubectl wait --for=condition=DPUIPAMObjectReady --namespace dpf-operator-system dpuserviceipam --all
dpuserviceipam.svc.dpu.nvidia.com/loopback condition met
dpuserviceipam.svc.dpu.nvidia.com/pool1 condition met
dpuserviceipam.svc.dpu.nvidia.com/pool2 condition met

$ kubectl wait --for=condition=ServiceInterfaceSetReady --namespace dpf-operator-system dpuserviceinterface --all
dpuserviceinterface.svc.dpu.nvidia.com/doca-hbn-p0-if-xgrcl condition met
dpuserviceinterface.svc.dpu.nvidia.com/doca-hbn-p1-if-hpcbz condition met
dpuserviceinterface.svc.dpu.nvidia.com/doca-hbn-pf0vf10-if-52jnw condition met
dpuserviceinterface.svc.dpu.nvidia.com/doca-hbn-pf1vf10-if-4djh6 condition met
dpuserviceinterface.svc.dpu.nvidia.com/p0 condition met
dpuserviceinterface.svc.dpu.nvidia.com/p1 condition met
dpuserviceinterface.svc.dpu.nvidia.com/pf0vf10-rep condition met
dpuserviceinterface.svc.dpu.nvidia.com/pf1vf10-rep condition met

$ kubectl wait --for=condition=ServiceChainSetReady --namespace dpf-operator-system dpuservicechain --all
dpuservicechain.svc.dpu.nvidia.com/hbn-only-ljcqz condition met

$ kubectl -n dpf-operator-system exec deployment/dpf-operator-controller-manager -- /dpfctl describe all --show-resources=dpu --show-conditions=dpu
NAME NAMESPACE STATUS REASON SINCE MESSAGE
DPFOperatorConfig/dpfoperatorconfig dpf-operator-system Ready: True Success 1h
└─DPU
└─2 DPU... dpf-operator-system Ready: True DPUReady 1h See worker1-mt2414600gq5, worker2-mt2412600eq8

恭喜,DPF系统已成功安装!

基础设施延迟与带宽验证

通过使用各种测试验证部署,并确保在DPF系统上达到链路速度性能和良好的延迟结果:

  1. RDMA - 用于延迟测量
  2. Iperf TCP - 用于带宽测量

每个测试都有详细描述。在每个测试结束时,您将看到所达到的性能。

注意 确保服务器已调优以获得最大性能(本文档未涵盖)。

性能测试

RoCE延迟测试

创建网络附加定义(NAD)和测试部署,使用以下YAML文件创建4个跨两个不同工作节点运行的Pod。

每个Pod将使用hostdev插件将主机虚拟功能编号10(VF10)设备插入其中——一个来自PF0,一个来自PF1。

此部署演示了在每个节点上,Pod被隔离并连接到不同VNI上的不同网络。

我们将展示:

  • 我们可以在同一网络上不同工作节点上运行的Pod之间通信并实现加速流量(性能测试)
  • 我们无法在不同网络上的Pod之间通信(隔离测试)

以下是用于创建NAD的yaml文件。将<PF0_VF_DEVICE>和<PF1_VF_DEVICE>替换为工作节点上的正确设备名称(例如ens5f0v10和ens5f1v10):

nad-hostdev.yaml
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: hostdev-pf0vf10-worker1
spec:
  config: '{
    "cniVersion": "0.3.1",
    "name": "hostpf0vf10",
    "type": "host-device",
    "device": "<PF0_VF_DEVICE>",
    "ipam": {
        "type": "static",
        "addresses": [
          {
            "address": "10.0.121.1/29"
          }
        ],
        "routes": [
          {
            "dst": "10.0.121.8/29",
            "gw": "10.0.121.2"
          }
        ]
    }
  }'
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: hostdev-pf1vf10-worker1
spec:
  config: '{
    "cniVersion": "0.3.1",
    "name": "hostpf1vf10",
    "type": "host-device",
    "device": "<PF1_VF_DEVICE>",
    "ipam": {
        "type": "static",
        "addresses": [
          {
            "address": "10.0.122.1/29"
          }
        ],
        "routes": [
          {
            "dst": "10.0.122.8/29",
            "gw": "10.0.122.2"
          }
        ]
    }
  }'
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: hostdev-pf0vf10-worker2
spec:
  config: '{
    "cniVersion": "0.3.1",
    "name": "hostpf0vf10",
    "type": "host-device",
    "device": "<PF0_VF_DEVICE>",
    "ipam": {
        "type": "static",
        "addresses": [
          {
            "address": "10.0.121.9/29"
          }
        ],
        "routes": [
          {
            "dst": "10.0.121.0/29",
            "gw": "10.0.121.10"
          }
        ]
    }
  }'
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: hostdev-pf1vf10-worker2
spec:
  config: '{
    "cniVersion": "0.3.1",
    "name": "hostpf1vf10",
    "type": "host-device",
    "device": "<PF1_VF_DEVICE>",
    "ipam": {
        "type": "static",
        "addresses": [
          {
            "address": "10.0.122.9/29"
          }
        ],
        "routes": [
          {
            "dst": "10.0.122.0/29",
            "gw": "10.0.122.10"
          }
        ]
    }
  }'
"10.0.122.0/29",
            "gw": "10.0.122.10"
          }
        ]
    }
  }'

This is the YAML file used to create the deployments for PF0 (RED network). Replace <IMAGE_URL> with a relevant container image URL (The container image must include NVIDIA user space drivers and the perftest and iperf3 packages):

testapp-performance-test-deployment-pf0.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sriov-hostdev-pf0vf10-test-worker1
  labels:
    app: sriov-hostdev-pf0vf10-test-worker1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sriov-hostdev-pf0vf10-test-worker1
  template:
    metadata:
      labels:
        app: sriov-hostdev-pf0vf10-test-worker1
      annotations:
        k8s.v1.cni.cncf.io/networks: hostdev-pf0vf10-worker1
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: sriov-test-worker
      nodeSelector:
        feature.node.kubernetes.io/dpu-enabled: "true"
        kubernetes.io/hostname: "worker1"
      containers:
      - name: testapp
        securityContext:
          privileged: true
          capabilities:
            add:
            - NET_ADMIN
            - IPC_LOCK
        image: <IMAGE_URL>
        command: ["sleep", "infinity"]
        ports:
        - containerPort: 5000
          name: tcp-server
        resources:
          requests:
            cpu: 24
            memory: 6Gi
          limits:
            cpu: 24
            memory: 6Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sriov-hostdev-pf0vf10-test-worker2
  labels:
    app: sriov-hostdev-pf0vf10-test-worker2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sriov-hostdev-pf0vf10-test-worker2
  template:
    metadata:
      labels:
        app: sriov-hostdev-pf0vf10-test-worker2
      annotations:
        k8s.v1.cni.cncf.io/networks: hostdev-pf0vf10-worker2
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: sriov-test-worker
      nodeSelector:
        feature.node.kubernetes.io/dpu-enabled: "true"
        kubernetes.io/hostname: "worker2"
      containers:
      - name: testapp
        securityContext:
          privileged: true
          capabilities:
            add:
            - NET_ADMIN
            - IPC_LOCK
        image: <IMAGE_URL>
        command: ["sleep", "infinity"]
        ports:
        - containerPort: 5000
          name: tcp-server
        resources:
          requests:
            cpu: 24
            memory: 6Gi
          limits:
            cpu: 24
            memory: 6Gi

Apply the following resources:

Jump Node Console

$ kubectl apply -f nad-hostdev.yaml

$ kubectl apply -f testapp-performance-test-deployment-pf0.yaml

Validate that the deployment is running successfully:

Jump Node Console

$ kubectl get pods -o wide
NAME                                              READY   STATUS    RESTARTS   AGE    IP              NODE      NOMINATED NODE   READINESS GATES
sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb    1/1     Running   0          3d1h   10.233.69.102   worker1   <none>           <none>
sriov-hostdev-pf0-test-worker2-649f58574c-5xrgw   1/1     Running   0          3d1h   10.233.70.177   worker2   <none>           <none>

Now that the test deployment is running, perform a latency performance test between two pods.

Connect to one of the pods in the deployment, specifically the pod running on the PF0 network on the first worker node:

Jump Node Console

$ kubectl exec -it sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb -- bash

From within the container, set an MTU value of 9000 and check the IP address on the net1 interface. Also identify the relevant RDMA device:

First Pod Console

root@sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb:/# ip link set net1 mtu 9000

root@sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb:/# ip a
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
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0@if185: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
    link/ether 8a:d6:45:6a:b4:3d brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.233.67.39/24 brd 10.233.67.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::88d6:45ff:fe6a:b43d/64 scope link
       valid_lft forever preferred_lft forever
59: net1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc mq state UP group default qlen 1000
    link/ether 0e:a4:2e:4e:1f:9f brd ff:ff:ff:ff:ff:ff
    altname enp134s0f0v10
    inet 10.0.121.1/29 brd 10.0.121.7 scope global net1
       valid_lft forever preferred_lft forever
    inet6 fe80::ca4:2eff:fe4e:1f9f/64 scope link
       valid_lft forever preferred_lft forever

root@testapp-performance-6c69b69d9b-24gpj:/# rdma link | grep net1
link mlx5_12/1 state ACTIVE physical_state LINK_UP netdev net1

Start the ib_read_lat server side:

First Pod Console

root@sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb:/# ib_read_lat -F -n 20000 -d mlx5_12

************************************
* Waiting for client to connect... *
************************************

Using another console window, reconnect to the jump node and connect to a second pod running on the same network (PF0), on the second worker node.

Jump Node Console

$ kubectl exec -it sriov-hostdev-pf0-test-worker2-649f58574c-5xrgw -- bash

From within the container, set an MTU value of 9000 on the net1 interface, then identify the relevant RDMA Device and start the ib_read_lat client using the IP address from the server-side container, finally let's check the latency results:

First Pod Console

root@sriov-hostdev-pf0-test-worker2-649f58574c-5xrgw:/# ip link set net1 mtu 9000

root@sriov-hostdev-pf0-test-worker2-649f58574c-5xrgw:/# rdma link | grep net1
link mlx5_12/1 state ACTIVE physical_state LINK_UP netdev net1

root@testapp-performance-6c69b69d9b-mrg4g:/# ib_read_lat -F -n 20000 -d mlx5_12 10.0.121.1
---------------------------------------------------------------------------------------
                    RDMA_Read Latency Test
 Dual-port       : OFF          Device         : mlx5_12
 Number of qps   : 1            Transport type : IB
 Connection type : RC           Using SRQ      : OFF
 PCIe relax order: ON
 ibv_wr* API     : ON
 TX depth        : 1
 Mtu             : 4096[B]
 Link type       : Ethernet
 GID index       : 3
 Outstand reads  : 16
 rdma_cm QPs     : OFF
 Data ex. method : Ethernet
---------------------------------------------------------------------------------------
 local address: LID 0000 QPN 0x04b9 PSN 0x4f96f3 OUT 0x10 RKey 0x078507 VAddr 0x006344ee3b0000
 GID: 00:00:00:00:00:00:00:00:00:00:255:255:10:00:121:09
 remote address: LID 0000 QPN 0x04b9 PSN 0x7fc74c OUT 0x10 RKey 0x07b607 VAddr 0x005a042c76c000
 GID: 00:00:00:00:00:00:00:00:00:00:255:255:10:00:121:01
---------------------------------------------------------------------------------------
 #bytes #iterations    t_min[usec]    t_max[usec]  t_typical[usec]    t_avg[usec]    t_stdev[usec]   99% percentile[usec]   99.9% percentile[usec]
 2       20000          3.99           166.68       4.12               8.26             9.08            44.07                   58.11
---------------------------------------------------------------------------------------

iPerf TCP Bandwidth Test

Connect to the first pods:

Jump Node Console

$ kubectl exec -it sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb -- bash
$ kubectl exec -it sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb -- bash

在启动 iperf3 服务器监听器之前,并在另一个标签页中检查 Pod 当前运行的核,以获得良好结果:

Jump Node Console

$ ssh worker1
depuser@worker1:~$ sudo -i
root@worker1:~# crictl ps | grep testapp
68fe4bc7f6854       25a2fe7e1cbce       3 hours ago          Running             testapp                 0                   4fa7100bee05e       sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb

root@worker2:~# crictl inspect 68fe4bc7f6854 | jq '.status.resources.linux.cpusetCpus'
"28-51"

使用以下脚本在不同端口上启动多个 iperf3 服务器(每个核一个):

iperf_server.sh

#!/bin/bash

# Cores to bind the iperf3 server processes to
CORES=$1

# Calculate the first_core and last_core to provide the CPU range
first_core=$(echo $CORES | cut -d "-" -f1)
last_core=$(echo $CORES | cut -d "-" -f2)
ports_num=$(($last_core - $first_core + 1))

# Loop over the ports (5201 + i*2) for i in the given CPU range and run iperf3 servers
for i in $(seq 1 $ports_num); do
   echo "Running iperf3 server $i"
   taskset -c $(($i + $first_core - 1)) iperf3 -s -p $((5201 + i * 2)) > /dev/null 2>&1 &
done

使用之前的 CPU 范围启动脚本(留 1 个核作为缓冲):

First Pod Console

root@sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb:/# chmod +x iperf_server.sh
root@sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb:/# ./iperf_server.sh 28-51
Running iperf3 server 1
Running iperf3 server 2

...
...
Running iperf3 server 23
Running iperf3 server 24

root@sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb:/# ps -ef | grep iperf3
root        2136       1  0 15:54 pts/2    00:00:00 iperf3 -s -p 5257
root        2137       1  0 15:54 pts/2    00:00:00 iperf3 -s -p 5259
...
...
root        2157       1  0 15:54 pts/2    00:00:00 iperf3 -s -p 5303
root        2158       1  0 15:54 pts/2    00:00:00 iperf3 -s -p 5305

连接到第二个 Pod:

Jump Node Console

$ kubectl exec -it sriov-hostdev-pf0-test-worker2-649f58574c-5xrgw -- bash

按照之前显示的方法识别第二个 Pod 运行的 CPU 核。

使用以下脚本启动多个 iperf3 客户端,连接到第一个 Pod 中的每个 iperf3 服务器:

注意:

  • 脚本接收 3 个参数:要连接的服务器 IP、生成 iperf3 进程的 CPU 核以及 iperf3 测试的持续时间。启动脚本时请确保提供所有 3 个参数,并将 CPU 核指定为范围(如 28-51)。
  • 确保 Pod 上安装了 jqbc,以便脚本正常运行。

iperf_client.sh

#!/bin/bash

# IP address of the server where iperf3 servers are running
SERVER_IP=$1  # Change to your server's IP

# Cores to bind the iperf3 client processes to
CORES=$2

# Duration to run the iperf3 test
DUR=$3

# Variable to accumulate the total bandwidth in Gbit/sec
total_bandwidth_Gbit=0

# Calculate the first_core and last_core to provide the CPU range
first_core=$(echo $CORES | cut -d "-" -f1)
last_core=$(echo $CORES | cut -d "-" -f2)
ports_num=$(($last_core - $first_core + 1))

# Array to store the PIDs of background tasks
pids=()

# Loop over the ports (5201 + i*2) for i in the given CPU range
for i in $(seq 1 $ports_num); do
    port=$((5201 + i * 2))
    cpu_core=$(($i + $first_core - 1))  # Assign CPU core based on the value of i
    output_file="iperf3_client_results_$port.log"

    # Run the iperf3 client in the background with CPU core binding
    timeout $(( DUR +5 )) taskset -c $cpu_core iperf3 -c $SERVER_IP -p $port -t $DUR -J > $output_file &
    pid=$!
    pids+=("$pid")
done

# Wait for all background tasks to complete and check their status
for pid in "${pids[@]}"; do
    wait $pid
    if [[ $? -ne 0 ]]; then
        echo "Process with PID $pid failed or timed out."
    fi
done

# Summarize the results from each log file
echo "Summary of iperf3 client results:"
for i in $(seq 1 $ports_num); do
    port=$((5201 + i * 2))
    output_file="iperf3_client_results_$port.log"

    if [[ -f $output_file ]]; then
        echo "Results for port $port:"

        # Parse the results and print a summary
        bandwidth_bps=$(jq '.end.sum_received.bits_per_second' $output_file)

        if [[ -n $bandwidth_bps ]]; then
           # Convert bandwidth from bps to Gbit/sec
           bandwidth_Gbit=$(echo "scale=3; $bandwidth_bps / 1000000000" | bc)
           echo "  Bandwidth: $bandwidth_Gbit Gbit/sec"

           # Accumulate the bandwidth for the total summary
           total_bandwidth_Gbit=$(echo "scale=3; $total_bandwidth_Gbit + $bandwidth_Gbit" | bc)

           # Delete current log file
           rm $output_file
        else
           echo "No bandwidth data found in $output_file"
        fi

    else
        echo "No results found for port $port"
    fi
done

# Print the total bandwidth summary
echo "Total Bandwidth across all streams: $total_bandwidth_Gbit Gbit/sec"

运行脚本并检查性能结果:

Second Pod Console

root@sriov-hostdev-pf0-test-worker2-649f58574c-5xrgw:/# chmod +x iperf_client.sh

root@sriov-hostdev-pf0-test-worker2-649f58574c-5xrgw:/# ./iperf_client.sh 10.0.121.1 28-51 30

Summary of iperf3 client results:
Results for port 5203:
  Bandwidth: 14.207 Gbit/sec
Results for port 5205:
  Bandwidth: 22.445 Gbit/sec
Results for port 5207:
  Bandwidth: 8.868 Gbit/sec
Results for port 5209:
  Bandwidth: 11.115 Gbit/sec
Results for port 5211:
  Bandwidth: 14.104 Gbit/sec
Results for port 5213:
  Bandwidth: 13.387 Gbit/sec
Results for port 5215:
  Bandwidth: 22.743 Gbit/sec
Results for port 5217:
  Bandwidth: 12.132 Gbit/sec
Results for port 5219:
  Bandwidth: 13.927 Gbit/sec
Results for port 5221:
  Bandwidth: 13.470 Gbit/sec
Results for port 5223:
  Bandwidth: 22.720 Gbit/sec
Results for port 5225:
  Bandwidth: 14.771 Gbit/sec
Results for port 5227:
  Bandwidth: 12.752 Gbit/sec
Results for port 5229:
  Bandwidth: 9.174 Gbit/sec
Results for port 5231:
  Bandwidth: 14.265 Gbit/sec
Results for port 5233:
  Bandwidth: 24.338 Gbit/sec
Results for port 5235:
  Bandwidth: 14.087 Gbit/sec
Results for port 5237:
  Bandwidth: 13.353 Gbit/sec
Results for port 5239:
  Bandwidth: 14.555 Gbit/sec
Results for port 5241:
  Bandwidth: 20.808 Gbit/sec
Results for port 5243:
  Bandwidth: 13.056 Gbit/sec
Results for port 5245:
  Bandwidth: 16.648 Gbit/sec
Results for port 5247:
  Bandwidth: 17.545 Gbit/sec
Results for port 5249:
  Bandwidth: 20.905 Gbit/sec
Total Bandwidth across all streams: 375.375 Gbit/sec

Network Isolation Test

最后,验证运行在不同网络(使用 PF0 和 PF1 上的虚拟功能)上的两个 Pod 无法相互通信。

应用以下 YAML 创建 PF1(BLUE 网络)的部署 - 确保将 <IMAGE_URL> 替换为相关的容器镜像 URL

testapp-performance-test-deployment-pf1.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sriov-hostdev-pf1vf10-test-worker1
  labels:
    app: sriov-hostdev-pf1vf10-test-worker1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sriov-hostdev-pf1vf10-test-worker1
  template:
    metadata:
      labels:
        app: sriov-hostdev-pf1vf10-test-worker1
      annotations:
        k8s.v1.cni.cncf.io/networks: hostdev-pf1vf10-worker1
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: sriov-test-worker
      nodeSelector:
        feature.node.kubernetes.io/dpu-enabled: "true"
        kubernetes.io/hostname: "worker1"
      containers:
      - name: testapp
        securityContext:
          privileged: true
          capabilities:
            add:
            - NET_ADMIN
            - IPC_LOCK
        image: <IMAGE_URL>
        command: ["sleep", "infinity"]
        ports:
        - containerPort: 5000
          name: tcp-server
        resources:
          requests:
            cpu: 24
            memory: 6Gi
          limits:
            cpu: 24
            memory: 6Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sriov-hostdev-pf1vf10-test-worker2
  labels:
    app: sriov-hostdev-pf1vf10-test-worker2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sriov-hostdev-pf1vf10-test-worker2
  template:
    metadata:
      labels:
        app: sriov-hostdev-pf1vf10-test-worker2
      annotations:
        k8s.v1.cni.cncf.io/networks: hostdev-pf1vf10-worker2
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: sriov-test-worker
      nodeSelector:
        feature.node.kubernetes.io/dpu-enabled: "true"
        kubernetes.io/hostname: "worker2"
      containers:
      - name: testapp
        securityContext:
          privileged: true
          capabilities:
            add:
            - NET_ADMIN
            - IPC_LOCK
        image: <IMAGE_URL>
        command: ["sleep", "infinity"]
        ports:
        - containerPort: 5000
          name: tcp-server
        resources:
          requests:
            cpu: 24
            memory: 6Gi
          limits:
            cpu: 24
            memory: 6Gi

add: - NET_ADMIN - IPC_LOCK image: <IMAGE_URL> command: ["sleep", "infinity"] ports: - containerPort: 5000 name: tcp-server resources: requests: cpu: 24 memory: 6Gi limits: cpu: 24 memory: 6Gi

Jump Node Console
$ kubectl apply -f testapp-performance-test-deployment-pf1.yaml

$ kubectl get pods -o wide
NAME                                              READY   STATUS    RESTARTS   AGE    IP              NODE      NOMINATED NODE   READINESS GATES
sriov-hostdev-pf0-test-worker1-76bbff86d-4gvsb    1/1     Running   0          3d1h   10.233.69.102   worker1   <none>           <none>
sriov-hostdev-pf0-test-worker2-649f58574c-5xrgw   1/1     Running   0          3d1h   10.233.70.177   worker2   <none>           <none>
sriov-hostdev-pf1-test-worker1-f9bc5f88f-msl4r    1/1     Running   0          3d1h   10.233.69.11    worker1   <none>           <none>
sriov-hostdev-pf1-test-worker2-f9bc5f88f-pqsb6    1/1     Running   0          3d1h   10.233.70.73    worker2   <none>           <none>

Connect to the pod running on the second worker node, with the PF1 network, add the required routing entry and try to ping the first pod running on the first worker node, with the PF0 network:

Jump Node Console
$ kubectl exec -it sriov-hostdev-pf1-test-worker2-f9bc5f88f-pqsb6 -- bash

root@sriov-hostdev-pf1-test-worker2-f9bc5f88f-pqsb6:/# ip route add 10.0.121.0/29 via 10.0.122.2
root@sriov-hostdev-pf1-test-worker2-f9bc5f88f-pqsb6:/# ping 10.0.121.1

This ping operation should fail due to the network isolation implemented in HBN using different VLANs, VNIs and VRFs.

Authors

GZ.jpg Guy ZilbermanGuy Zilberman is a solution architect at NVIDIA's 网络解决方案 Labs, bringing extensive experience from several leadership roles in cloud computing. He specializes in designing and implementing solutions for cloud and containerized workloads, leveraging NVIDIA's advanced networking technologies. His work primarily focuses on open-source cloud infrastructure, with expertise in platforms such as Kubernetes (K8s) and OpenStack.
SD.jpg Shachar DorShachar Dor joined the 解决方案 Lab team after working more than ten years as a software architect at NVIDIA Networking (previously Mellanox Technologies), where he was responsible for the architecture of network management products and solutions. Shachar's focus is on networking technologies, especially around fabric bring-up, configuration, monitoring, and life-cycle management.Shachar has a strong background in software architecture, design, and programming through his work on multiple projects and technologies also prior to joining the company.

NVIDIA and the NVIDIA logo, are trademarks and/or registered trademarks of NVIDIA Corporation in the U.S. and other countries. Other company and product names may be trademarks of the respective companies with which they are associated.

2025 NVIDIA Corporation. All rights reserved.©