- 云原生
- 容器运行时
【免费下载链接】kata-containers
Kata Containers is an open source project and community working to build a standard implementation of lightweight Virtual Machines (VMs) that feel and perform like containers, but provide the workload isolation and security advantages of VMs. https://katacontainers.io/
Kata Containers 以轻量级虚拟机承载容器工作负载,天然具备设备直通(VFIO passthrough)能力,但在虚拟化环境中,被扁平化、模糊化的 PCI Express 拓扑会让 NVIDIA 驱动栈拒绝启用 GPU 之间的 GPUDirect P2P 通信。本文基于 Kata Containers 的虚拟化参考架构(Virtualization Reference Architecture,VRA)设计文档,系统讲解如何在 Kata 中通过 PCI Express 根端口(root port)热插拔、宿主拓扑复制以及 CDI(Container Device Interface)元数据,为 GPU 与网卡(NIC)组合构建可用的 GPUDirect P2P / GPUDirect RDMA 拓扑,并给出完整的配置示例、lspci验证输出与资源上限分析。读完本文,你将掌握 Kata 中 PCI Express 拓扑的两种构建方式、pcie_root_port/pcie_switch_port等关键配置项的使用,以及如何用 CDI 注解解决多设备直通的资源瓶颈。
GPUDirect 使用场景:先看设备组合矩阵
在设计虚拟化拓扑之前,需要先厘清不同设备组合下 GPUDirect 的可用模式。Kata VRA 文档将设备分为两大类:直通设备(passthrough)与虚拟化设备(virtualized,即 VM 获得虚拟功能 VF 而非物理功能 PF),并允许 PF 与 VF 混合搭配:
| Device #1(passthrough) | Device #2(passthrough) | P2P 兼容性与模式 |
|---|---|---|
| GPU PF | GPU PF | GPUDirect P2P |
| GPU PF | NIC PF | GPUDirect RDMA |
| MIG-slice | MIG-slice | 无 GPUDirect P2P |
| MIG-slice | NIC PF | GPUDirect RDMA |
| Device #1(virtualized) | Device #2(virtualized) | P2P 兼容性与模式 |
| Time-slice vGPU VF | Time-slice vGPU VF | 无 GPUDirect P2P,但可用 NVLINK P2P |
| Time-slice vGPU VF | NIC VF | GPUDirect RDMA |
| MIG-slice vGPU | MIG-slice vGPU | 无 GPUDirect P2P |
| MIG-slice vGPU | NIC VF | GPUDirect RDMA |
从上表可以看出:GPU PF 与 NIC PF 组合可获得 GPUDirect RDMA,GPU PF 与 GPU PF 组合可获得 GPUDirect P2P;而 MIG-slice 之间没有 GPUDirect P2P,但 MIG-slice 与 NIC PF/VF 组合仍支持 GPUDirect RDMA。这就是本文后续所有拓扑设计的出发点:为 VM 内的两个端点提供能够让驱动栈认可的 PCI Express 拓扑,从而解锁 P2P 通信。
虚拟化环境中的 P2P 障碍:IOMMU、ACS 与 ATS
PCI Express 拓扑中的两个端点要直接进行 Peer-to-Peer(P2P)通信,在虚拟化环境里会遇到几个关键机制的限制。
IOMMU 的地址翻译与隔离。IOMMU 将 IO 虚拟地址(IOVA)翻译为物理地址(PA)。每个挂在 IOMMU 之后的设备拥有独立的 IOVA 内存空间,通常没有任何两个设备共享同一个 IOVA 空间(具体由 hypervisor 或 OS 决定如何把设备映射到 IOVA 空间)。任何 PCI Express DMA 事务都使用 IOVA,必须经过 IOMMU 翻译。默认情况下,所有流量都会被路由到根复合体(root complex),而不会直接发给对端设备——这是阻碍 P2P 的首要原因。即便不启用虚拟化,IOMMU 也常被用于设备隔离与保护:设备只能访问为其映射的内存区域,因此一个设备无法对另一个设备发起 DMA。DPDK 正是利用 IOMMU 获得更好的设备间隔离,另一个好处是即使 PA 空间高度碎片化,IOVA 空间也可以呈现为连续内存。而在虚拟化场景下,IOMMU 负责在 VM 之间隔离设备与内存,从而在不危及宿主和其他客户 OS 的前提下实现安全的设备直通;若没有 IOMMU,任何设备都可以访问整个系统并"任意"执行 DMA 事务。
ACS(Access Control Services)的路由控制。ACS 控制哪些设备被允许相互通信,从而避免数据包的不当路由,无论 IOMMU 是否启用。当 IOMMU 启用时,ACS 通常被配置为强制所有 PCI Express DMA 都经过根复合体,以便 IOMMU 完成翻译——代价是端到端延迟更高、带宽降低。
ATS(Address Translation Services)是绕过性能瓶颈的关键。支持 ATS 的端点可以从 IOMMU 预取 IOVA→PA 翻译,然后直接向另一个端点发起 DMA 事务。Hypervisor 的做法是:在这些端点中启用 ATS,将 ACS 配置为允许 Direct Translated P2P,并让 IOMMU 允许地址翻译请求。也就是说,P2P 能否工作,本质上取决于 hypervisor 是否在设备、ACS 与 IOMMU 三层同时做了正确配置。
另一个重要事实是:NVIDIA 驱动栈会根据运行所在系统的 PCI Express 拓扑,来判断硬件是否支持 P2P。驱动栈会对特定的芯片组和 PCI Express 交换机进行资质认证(qualify)。在虚拟环境中,PCI Express 拓扑被扁平化和模糊化,以给 VM 内软件呈现统一的环境——但这恰恰破坏了 GPUDirect P2P 用例。在裸金属机器上,驱动栈会把 GPU 划分为可执行 GPUDirect P2P 通信的 clique 组,并排除无法 P2P 的 peer 映射(典型情况是 GPU 挂在多个 CPU socket 上)。
CPU 和本地内存条被称为NUMA 节点。在两 socket 服务器中,每个 CPU 各有一个本地内存条,共两个 NUMA 节点;部分服务器允许每个 CPU 配置更多 NUMA 节点(每 socket 两个甚至四个 NUMA 节点),以改善本地内存和 L3 NUMA 域的性能。NUMA 拓扑对 GPU 分组(clique)的划分有直接影响,这在后续"宿主拓扑复制"一节还会再次出现。
当前的解决思路之一是:hypervisor 提供额外的拓扑信息,让驱动栈即使面对虚拟化环境也能启用 GPU 间的 GPUDirect P2P。PCI 配置空间中的PCI Express virtual P2P approval capability 结构(虚拟 P2P 审批能力)完全由 hypervisor 为直通的 GPU 设备模拟。hypervisor 提供clique ID——拥有相同 clique ID 的 GPU 属于同一组可进行 P2P 通信的 GPU。
在 vSphere、Azure 等云平台上,hypervisor 会下发一份topologies.xml,NCCL 可以读取它并推导出正确的 P2P 级别(NCCL 利用 InfiniBand 和/或 UCX 进行通信,此时 GPUDirect P2P 与 GPUDirect RDMA 应该可以直接工作)。问题在于:不使用该 XML 文件推导拓扑的软件或应用会失败,无法启用 GPUDirect(可参考 NCCL 的nccl-p2p-level环境变量)。这正凸显了在 VM 内直接呈现正确拓扑的价值——Kata 的方案从根源上规避了这类对额外文件的依赖。
Kata 的两部分方案:虚拟 P2P 审批能力 + 宿主拓扑复制
为了让任何 hypervisor都能启用 GPUDirect P2P 和 GPUDirect RDMA,Kata 提出了一个虚拟化参考架构,思路拆成两部分:
- 扩展 PCI Express virtual P2P approval capability 结构:把它扩展到每一个想做 P2P 的设备,并按 clique ID 对设备分组;
- 复制宿主拓扑的子集:在 VM 内呈现一部分宿主拓扑,让 VM 中运行的应用程序无需读取额外信息,就能像裸金属场景一样自动推导 P2P 能力——驱动栈可以直接判断 VM 中呈现的拓扑是否支持 P2P 通信。
文档以一个具体宿主拓扑为例:一台带两个 converged DPU(融合 DPU)的系统,每个 DPU 各挂一个A100XGPU 和两个ConnectX-6网络端口,全部连接到 PCI Express 交换机的下行端口。其拓扑如下(绿色路径是高效 P2P 通信的最优路径):
+-00.0-[d8-df]----00.0-[d9-df]--+-00.0-[da-db]--+-00.0 Mellanox Tech MT42822 BlueField-2 integrated ConnectX-6 Dx network | +-00.1 Mellanox Tech MT42822 BlueField-2 integrated ConnectX-6 Dx network | \-00.2 Mellanox Tech MT42822 BlueField-2 SoC Management Interface \-01.0-[dc-df]----00.0-[dd-df]----08.0-[de-df]----00.0 NVIDIA Corporation GA100 [A100X] +-00.0-[3b-42]----00.0-[3c-42]--+-00.0-[3d-3e]--+-00.0 Mellanox Tech MT42822 BlueField-2 integrated ConnectX-6 Dx network | +-00.1 Mellanox Tech MT42822 BlueField-2 integrated ConnectX-6 Dx network | \-00.2 Mellanox Tech MT42822 BlueField-2 SoC Management Interface \-01.0-[3f-42]----00.0-[40-42]----08.0-[41-42]----00.0 NVIDIA Corporation GA100 [A100X]第一部分:PCI Express 虚拟 P2P 审批能力
用pcie_root_port配置根端口热插拔
多数情况下,PCI Express 拓扑会被扁平化和模糊化,以保证 VM 镜像能在不同的物理硬件拓扑之间轻松迁移。在 Kata 中,可以配置 hypervisor 使用PCI Express 根端口来热插拔要直通的 VFIO 设备,用户按直通设备数量决定分配多少个根端口。Kata 的一个较新特性是自动检测需要热插拔的 PCI Express 设备数量,如果根端口数量不足会直接报错退出;但 Kata不会自动增加根端口数量——它把拓扑控制权完全交给用户。对应的配置项在src/runtime/config/configuration-qemu.toml.in中:
# /etc/kata-containers/configuration.toml # Before hot plugging a PCIe device, you need to add a pcie_root_port device. # Use this parameter when using some large PCI bar devices, such as NVIDIA GPU # The value means the number of pcie_root_port # This value is valid when machine_type is "q35" # Default 0 pcie_root_port = 8该参数在源码中的定义位于 src/runtime/pkg/katautils/config.go:
PCIeRootPort uint32 `toml:"pcie_root_port"` PCIeSwitchPort uint32 `toml:"pcie_switch_port"`配置了根端口后,Kata 的 QEMU 实现会根据设备数量自动补齐根端口。在 src/runtime/virtcontainers/qemu.go 中可以看到完整逻辑:遍历 VFIO 设备、用drivers.IsPCIeDevice()统计需要热插拔的 PCIe 设备数,再与pcie_root_port配置取较大值;若超出上限则直接报错Number of PCIe Root Ports exceeed allowed max of 16(上限常量定义在 src/runtime/virtcontainers/qemu_arch_base.go):
maxPCIeRootPort = 16 // Limitation from QEMU maxPCIeSwitchPort = 16 // Limitation from QEMUVFIO 设备默认热插拔到PCIe-PCI 桥(PCIe-PCI bridge)上。而 PCI Express 设备的热插拔只支持在 PCI Express 根端口或下行端口上。因此,配置pcie_root_port = 8后启动一个 Kata 容器,即可在 VM 内用lspci -tv检查被分配的根端口与已热插拔设备:
$ lspci -tv -[0000:00]-+-00.0 Intel Corporation 82G33/G31/P35/P31 Express DRAM Controller +-01.0 Red Hat, Inc. Virtio console +-02.0 Red Hat, Inc. Virtio SCSI +-03.0 Red Hat, Inc. Virtio RNG +-04.0-[01]----00.0 Mellanox Technologies MT42822 BlueField-2 integrated ConnectX-6 +-05.0-[02]----00.0 Mellanox Technologies MT42822 BlueField-2 integrated ConnectX-6 +-06.0-[03]----00.0 NVIDIA Corporation Device 20b8 +-07.0-[04]----00.0 NVIDIA Corporation Device 20b8 +-08.0-[05]-- +-09.0-[06]-- +-0a.0-[07]-- +-0b.0-[08]-- +-0c.0 Red Hat, Inc. Virtio socket +-0d.0 Red Hat, Inc. Virtio file system +-1f.0 Intel Corporation 82801IB (ICH9) LPC Interface Controller +-1f.2 Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller \-1f.3 Intel Corporation 82801I (ICH9 Family) SMBus Controller可以看到:04.0–07.0四个根端口各挂了一个 Mellanox 网卡或 NVIDIA GPU,08.0–0b.0四个根端口留作备用。这种扁平拓扑下,GPU 与 NIC 都直接挂在根端口上,彼此之间没有 PCI Express 交换机端口。
大 BAR 设备的地址映射启发式
对于拥有超大 BAR(Base Address Registers,基地址寄存器)的设备(如 GPU),需要正确配置 PCI Express 根端口并分配足够的内存用于映射。Kata 为此加入了一个启发式算法来推导正确的设置,从而保证 BAR 能正确映射。该功能被添加到了nvidia/go-nvlib库中,现在是 Kata 的一部分。启动后可通过dmesg验证 BAR 分配情况:
$ sudo dmesg | grep BAR [ 0.179960] pci 0000:00:04.0: BAR 7: assigned [io 0x1000-0x1fff] [ 0.179962] pci 0000:00:05.0: BAR 7: assigned [io 0x2000-0x2fff] [ 0.179963] pci 0000:00:06.0: BAR 7: assigned [io 0x3000-0x3fff] [ 0.179964] pci 0000:00:07.0: BAR 7: assigned [io 0x4000-0x4fff] [ 0.179966] pci 0000:00:08.0: BAR 7: assigned [io 0x5000-0x5fff] [ 0.179967] pci 0000:00:09.0: BAR 7: assigned [io 0x6000-0x6fff] [ 0.179968] pci 0000:00:0a.0: BAR 7: assigned [io 0x7000-0x7fff] [ 0.179969] pci 0000:00:0b.0: BAR 7: assigned [io 0x8000-0x8fff] [ 2.115912] pci 0000:01:00.0: BAR 0: assigned [mem 0x13000000000-0x13001ffffff 64bit pref] [ 2.116203] pci 0000:01:00.0: BAR 2: assigned [mem 0x13002000000-0x130027fffff 64bit pref] [ 2.683132] pci 0000:02:00.0: BAR 0: assigned [mem 0x12000000000-0x12001ffffff 64bit pref] [ 2.683419] pci 0000:02:00.0: BAR 2: assigned [mem 0x12002000000-0x120027fffff 64bit pref] [ 2.959155] pci 0000:03:00.0: BAR 1: assigned [mem 0x11000000000-0x117ffffffff 64bit pref] [ 2.959345] pci 0000:03:00.0: BAR 3: assigned [mem 0x11800000000-0x11801ffffff 64bit pref] [ 2.959523] pci 0000:03:00.0: BAR 0: assigned [mem 0xf9000000-0xf9ffffff] [ 2.966119] pci 0000:04:00.0: BAR 1: assigned [mem 0x10000000000-0x107ffffffff 64bit pref] [ 2.966295] pci 0000:04:00.0: BAR 3: assigned [mem 0x10800000000-0x10801ffffff 64bit pref] [ 2.966472] pci 0000:04:00.0: BAR 0: assigned [mem 0xf7000000-0xf7ffffff]从日志可见每个根端口都被分配了独立的 4K IO 范围(io 0x1000-0x1fff等),而 GPU 的 64bit prefetch 内存 BAR 则被映射到大片高位地址空间——这正是启发式算法的功劳。
用 CDI 提供 clique 元数据
即便 BAR 映射成功,这种扁平拓扑下 NVIDIA 驱动栈依然会拒绝 P2P 通信,原因有二:(1) 拓扑不是它期望的样子;(2) 没有获得资质的芯片组。由于 P2P 设备没有连接到 PCI Express 交换机端口,就需要提供额外的元信息来支持 P2P 功能。
一种做法是给容器打注解——Kata 配置文件中大部分设置都可以通过注解覆盖——但这限制了灵活性,用户必须更新所有想用 Kata 运行的容器。Kata 的目标是让这类事情尽量透明,因此引入了CDI(Container Device Interface)。CDI 是容器运行时支持第三方设备的规范,它把设备信息(而非容器信息)作为元数据来源。既然 clique ID 是绑定在设备上的,就不需要改动容器——只需分配正确的资源,GPUDirect RDMA 就会被正确配置。
例如,用户想让同一 DPU 上的第一个 GPU 与 NIC 做 GPUDirect RDMA,就可以在 CDI 规范中告诉 hypervisor 它们属于同一个 clique:
# /etc/cdi/nvidia.yaml cdiVersion: 0.4.0 kind: nvidia.com/gpu devices: - name: gpu0 annotations: bdf: "41:00.0" clique-id: "0" containerEdits: deviceNodes: - path: "/dev/vfio/71" # /etc/cdi/mellanox.yaml cdiVersion: 0.4.0 kind: mellanox.com/nic devices: - name: nic0 annotations: bdf: "3d:00.0" clique-id: "0" attach-pci: "true" containerEdits: deviceNodes: - path: "/dev/vfio/66"这里clique-id: "0"把gpu0与nic0归入同一 P2P 组,hypervisor 会在 VM 内据此设置设备。一个更进一步的构想是:不单独暴露 GPU 与 NIC,而是通过NFD(Node Feature Discovery)暴露一个组合了二者的 GPUDirect RDMA 设备,从而在 Kubernetes 部署中确保"正确的一对"被分配和使用(下一节会涉及)。
需要指出的是:GPU 驱动栈已经在利用 PCI Express virtual P2P approval capability,但 NIC 驱动栈(MOFED)目前还不使用它。文档列出的行动项之一,就是让 MOFED 读取 P2P 审批能力,并按前述方式启用 ATS 与 ACS 设置。这样,无论向 VM 应用呈现什么拓扑,都能启用 GPUDirect P2P 与 GPUDirect RDMA——提供正确信息(注解或 CDI 规范)的责任在管理员或基础设施工程师一方。
第二部分:宿主拓扑复制
另一种在 VM 中呈现 PCI Express 拓扑的方式,是复制支撑 P2P 用例所需的宿主拓扑子集。与根端口配置类似,可以轻松配置使用PCI Express 交换机端口(switch port)来热插拔设备:
# /etc/kata-containers/configuration.toml # Before hot plugging a PCIe device via a PCI Express switch, you need to add # pcie_switch_port devices. Use this parameter when replicating host PCIe switch # topology inside the VM. The value means the number of pcie_switch_port # This value is valid when machine_type is "q35" # Default 0 pcie_switch_port = 8每个被直通的设备都挂到一个 PCI Express 下行端口上,如下面的lspci -tv输出所示。甚至可以结合 CDI 添加的元数据,完整复刻宿主两个 DPU 的拓扑。大多数情况下,一个容器只需要一对 GPU+NIC 做 GPUDirect RDMA;但这是 Kata+CDI 能力的展示——甚至可以设想把支持 P2P 的设备组(即使来自不同 CPU socket 或 NUMA 节点)放进同一个容器:第一组是 NUMA 节点 0(红色),第二组是 NUMA 节点 1(绿色)。由于分组正确(同一 clique ID),组内 P2P 自然启用:
$ lspci -tv -[0000:00]-+-00.0 Intel Corporation 82G33/G31/P35/P31 Express DRAM Controller +-01.0 Red Hat, Inc. Virtio console +-02.0 Red Hat, Inc. Virtio SCSI +-03.0 Red Hat, Inc. Virtio RNG +-04.0-[01-04]----00.0-[02-04]--+-00.0-[03]----00.0 NVIDIA Corporation Device 20b8 | \-01.0-[04]----00.0 Mellanox Tech MT42822 BlueField-2 integrated ConnectX-6 Dx +-05.0-[05-08]----00.0-[06-08]--+-00.0-[07]----00.0 Mellanox Tech MT42822 BlueField-2 integrated ConnectX-6 Dx | \-01.0-[08]----00.0 NVIDIA Corporation Device 20b8 +-06.0 Red Hat, Inc. Virtio socket +-07.0 Red Hat, Inc. Virtio file system +-1f.0 Intel Corporation 82801IB (ICH9) LPC Interface Controller +-1f.2 Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode] \-1f.3 Intel Corporation 82801I (ICH9 Family) SMBus Controller \-1f.3 Intel Corporation 82801I (ICH9 Family) SMBus Controller注意04.0-[01-04]与05.0-[05-08]各自带了一个 PCI Express 交换机([02-04]与[06-08]),两个下行端口分别连接 NVIDIA GPU 与 Mellanox ConnectX-6 网卡——这与宿主的 DPU 拓扑结构一一对应。使用根端口还是交换机端口的配置可按容器或 Pod 粒度应用,也就是说每次运行应用都可以切换 PCI Express 拓扑。
这两个方案对应的热插拔/冷插拔入口,在 Kata 源码中通过hot_plug_vfio与cold_plug_vfio两个配置项控制(定义于 src/runtime/virtcontainers/pkg/annotations/annotations.go,对应注解为io.kata.containers.config.hypervisor.hot_plug_vfio/io.kata.containers.config.hypervisor.cold_plug_vfio/pcie_root_port/pcie_switch_port)。配置值类型定义在 src/runtime/pkg/device/config/config.go:
const ( RootPort PCIePort = "root-port" // 将 VFIO 设备挂到 root-port SwitchPort = "switch-port" // 将 VFIO 设备挂到 switch-port BridgePort = "bridge-port" // 默认值 NoPort = "no-port" // 禁用 VFIO 热插拔/冷插拔 InvalidPort = "invalid-port" )默认配置(configuration-qemu.toml.in中hot_plug_vfio = "no-port"、cold_plug_vfio = "no-port")表示不启用端口热插拔,此时 VFIO 设备按旧行为挂到 PCIe-PCI 桥上。在 src/runtime/virtcontainers/sandbox.go 的coldOrHotPlugVFIO中,Kata 会依据该配置把每个设备的Port设置为对应的端口类型,进而驱动 QEMU 命令行生成根端口或交换机端口设备。机密计算(confidential compute)环境下热插拔可能危及安全,因此还提供了cold_plug_vfio冷插拔入口,两者默认都是"no-port"。
Hypervisor 资源限制与多设备直通的解法
端口数量的硬上限
每个 hypervisor 在可创建的 PCI Express 根端口、交换机端口或桥端口数量上都有资源限制,尤其是需要按 PCI 规范为设备保留 4K IO 范围的设备。每个根端口或交换机端口实例都会消耗 4K IO,而 IO 容量上限是 64K。简单的计算即可得出结论:在 QEMU 中,若 PCI Express 层级中使用带 IO BAR 的设备,最多只能创建 16 个 PCI Express 根端口或 16 个 PCI Express 交换机端口——这与源码中maxPCIeRootPort = 16、maxPCIeSwitchPort = 16的常量定义完全一致。
此外,PCI 根总线上最多有32 个槽位,完整 PCI(e) 拓扑最多256 个槽位。
默认占用的槽位
默认情况下,QEMU 会在 PCI 根总线的最后一个槽位挂载一个多功能设备(ICH9 LPC/ SATA/ SMBus 组合):
+-1f.0 Intel Corporation 82801IB (ICH9) LPC Interface Controller +-1f.2 Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode] \-1f.3 Intel Corporation 82801I (ICH9 Family) SMBus ControllerKata 还会额外添加virtio-xxx-pci设备(消耗 5 个槽位)、一个 PCIe-PCI 桥(1 个槽位)和一个 DRAM 控制器(1 个槽位),也就是说默认情况下根总线已用掉 8 个槽位,剩余 24 个槽位可用来添加其他设备。
典型案例:8 块 RTX GPU 直通一台容器
文档记录了一个真实客户用例:使用较新的 RTX GPU 与 Kata,希望把 8 块 GPU 直通进同一个容器,结果遇到了资源问题。原因在于:这些显卡往往由四个独立设备节点组成——GPU、Audio 以及两个 USB 控制器设备(有些卡带 USB-C 输出)。这些设备被归入同一个 IOMMU 组。由于必须把整个 IOMMU 组直通进 VM,就需要分配32 个 PCI Express 根端口或 32 个交换机端口——这在上述资源限制下技术上是不可能的(上限 16)。而且这些设备都表现为 PCI Express 设备,必须热插拔到根端口或交换机端口。
解法是借助 CDI:为每个设备标注它将被热插拔为 PCI Express 设备还是 PCI 设备,从而决定使用 PCI Express 根/交换机端口还是普通 PCI 桥。PCI 桥不受有限 IO 范围的影响。这样,GPU 作为 PCI Express 设备挂到根/交换机端口,其余三个 PCI 设备挂到 PCI 桥,从而为需要的 PCI Express 根/交换机端口留出资源。例如:把 GPU 挂到 PCI Express 根端口,把 NIC 挂到 PCI 桥:
# /etc/cdi/mellanox.json cdiVersion: 0.4.0 kind: mellanox.com/nic devices: - name: nic0 annotations: bdf: "3d:00.0" clique-id: "0" attach-pci: "true" containerEdits: deviceNodes: - path: "/dev/vfio/66" - name: nic1 annotations: bdf: "3d:00.1" clique-id: "1" attach-pci: "true" containerEdits: deviceNodes: - path: "/dev/vfio/67"关键字段是attach-pci: "true":它告诉 Kata 该设备应作为普通 PCI 设备挂到 PCI 桥上,而不是占用宝贵的 PCI Express 端口。配置为 GPU 使用 8 个根端口、NIC 挂到 PCI 桥(PCI 桥通过 PCI Express-PCI 桥连接——这是在 PCI Express 机器中引入 PCI 拓扑的首选方式)之后,VM 内拓扑如下:
$ lspci -tv -[0000:00]-+-00.0 Intel Corporation 82G33/G31/P35/P31 Express DRAM Controller +-01.0 Red Hat, Inc. Virtio console +-02.0 Red Hat, Inc. Virtio SCSI +-03.0 Red Hat, Inc. Virtio RNG +-04.0-[01]----00.0 NVIDIA Corporation Device 20b8 +-05.0-[02]----00.0 NVIDIA Corporation Device 20b8 +-06.0-[03]-- +-07.0-[04]-- +-08.0-[05]-- +-09.0-[06]-- +-0a.0-[07]-- +-0b.0-[08]-- +-0c.0-[09-0a]----00.0-[0a]--+-00.0 Mellanox Tech MT42822 BlueField-2 ConnectX-6 | \-01.0 Mellanox Tech MT42822 BlueField-2 ConnectX-6 +-0d.0 Red Hat, Inc. Virtio socket +-0e.0 Red Hat, Inc. Virtio file system +-1f.0 Intel Corporation 82801IB (ICH9) LPC Interface Controller +-1f.2 Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller \-1f.3 Intel Corporation 82801I (ICH9 Family) SMBus Controller两块 GPU(04.0、05.0)各自独占一个根端口,两块 Mellanox ConnectX-6 网卡则挂在一个 PCIe-PCI 桥(0c.0-[09-0a])下——PCI 设备消耗的是 PCI(e) 拓扑中总量 256 的槽位,把稀缺的端口资源留给了真正需要 PCI Express 的设备。这正是 Kata 结合 CDI 化解"IOMMU 组整体直通导致端口耗尽"问题的完整闭环。
总结与落地路径
Kata Containers 的虚拟化参考架构为在轻量级 VM 中启用 GPUDirect P2P 与 GPUDirect RDMA 提供了两条互补路径:
| 维度 | PCI Express 虚拟 P2P 审批能力 | 宿主拓扑复制 |
|---|---|---|
| 核心机制 | hypervisor 模拟 virtual P2P approval capability,按 clique ID 分组 | 在 VM 内复制宿主 PCIe 交换机拓扑子集 |
| 关键配置 | pcie_root_port = N(q35 机型) | pcie_switch_port = N(q35 机型) |
| 元数据载体 | 容器注解或 CDI 规范(clique-id) | CDI 规范(clique-id+attach-pci) |
| 适用场景 | 驱动栈通过 P2P 审批能力判断拓扑 | 需要呈现真实交换机层级、跨 NUMA 分组 |
| 上限 | 最多 16 个根端口 | 最多 16 个交换机端口 |
落地时需要注意的要点:
- 先评估设备数量再决定端口数:Kata 会为 PCIe VFIO 设备自动计算所需端口,超出
maxPCIeRootPort/maxPCIeSwitchPort(均为 16)会直接报错,不会静默扩容; - IO 范围是稀缺资源:每个根/交换机端口消耗 4K IO(总上限 64K),带 IO BAR 的设备组合下 16 就是硬上限;
- IOMMU 组整体直通:多设备节点(GPU+Audio+USB)属于同一 IOMMU 组,需用 CDI 的
attach-pci: "true"把非 PCIe 关键设备分流到 PCI 桥,把端口留给 GPU; - 配置按 Pod/容器粒度生效:根端口与交换机端口拓扑可在每次运行应用时切换,管理员或基础设施工程师负责通过注解或 CDI 提供正确的 clique 元数据。
本文所涉配置与源码均位于当前仓库:配置模板见 src/runtime/config/configuration-qemu.toml.in(hot_plug_vfio/cold_plug_vfio/pcie_root_port),端口解析逻辑见 src/runtime/pkg/katautils/config.go,QEMU 侧端口创建与上限校验见 src/runtime/virtcontainers/qemu.go 与 src/runtime/virtcontainers/qemu_arch_base.go,端口类型定义见 src/runtime/pkg/device/config/config.go。需要强调,本文方案基于设计文档与源码现状,具体生效情况受 hypervisor 版本、QEMU 机型(仅q35支持根/交换机端口)与 NVIDIA/MOFED 驱动版本影响,实践时请以实际环境验证为准。
- 云原生
- 容器运行时
【免费下载链接】kata-containers
Kata Containers is an open source project and community working to build a standard implementation of lightweight Virtual Machines (VMs) that feel and perform like containers, but provide the workload isolation and security advantages of VMs. https://katacontainers.io/
相关推荐
SkyPilot 在 GCP 与 GKE 上启用 GPUDirect-TCPX / GPUDirect-RDMA 高性能 GPU 网络实战指南
SkyPilot 在 GCP 与 GKE 上启用 GPUDirect TCPX / GPUDirect RDMA 高性能 GPU 网络实战指南 导读 本文基于
后端任务调度MLOps集群管理使用SkyPilot在GCP A3虚拟机上部署GPUDirect-TCPX加速方案
使用SkyPilot在GCP A3虚拟机上部署GPUDirect TCPX加速方案 技术背景与概述 在现代高性能计算和深度学习领域,GPU之间的高效通信至关重要
后端任务调度MLOps集群管理Kata Containers - 高性能虚拟化容器解决方案
Kata Containers 高性能虚拟化容器解决方案 Kata Containers 是一个创新的开源项目,其目标是提供一种既拥有容器轻量级特性和速度优势,
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考