动手:K8s 里把 RDMA 交给 Pod
network-operator、rdma/hca 资源、hostNetwork 与 MacVLAN 两种模式的实际配法。
学完这节你能做到
- 部署 network-operator 并用 NicClusterPolicy 暴露 rdma/hca 资源
- 按 IB 或 RoCE 场景写对 Pod 的资源与网络配置
- 在 Pod 内验证 RDMA 设备可用并跑通带宽测试
建议先学
跳过这几节会看不懂本节的部分推导
要解决的三个问题
裸机上 RDMA 通了,搬进 K8s 要额外解决三件事:
- 容器里看得到 RDMA 设备(
/dev/infiniband/*) - 调度器知道哪些节点有卡、并且能按数量分配
- 网络配置正确——IB 和 RoCE 在这一步要求不同
NVIDIA Network Operator 把前两件包了,第三件要你自己选。
依赖:先把驱动装好
NFD(Node Feature Discovery) ← 打标签,告诉调度器哪些节点有 Mellanox 卡
DOCA OFED ← 驱动(MLNX OFED 已迁移到 DOCA OFED)
Network Operator ← 部署设备插件与网络组件
驱动有两种装法,各有代价:
| 方式 | 优点 | 代价 |
|---|---|---|
| 宿主机预装 | 稳定、启动快、排障直接 | 要自己维护版本一致性 |
Operator 容器化下发(ofedDriver) | 版本统一、可滚动升级 | 升级要 drain 节点,首次编译慢 |
# 确认宿主机驱动可用
ofed_info -s
ibstat -l
lsmod | grep -E 'mlx5_core|mlx5_ib|ib_uverbs'
NicClusterPolicy:暴露 rdma/hca 资源
apiVersion: mellanox.com/v1alpha1
kind: NicClusterPolicy
metadata:
name: nic-cluster-policy
namespace: network-operator
spec:
tolerations:
- operator: Exists
effect: NoSchedule
# ofedDriver: # 宿主机已装驱动就注释掉
# image: doca-driver
# repository: nvcr.io/nvidia/mellanox
# version: doca3.3.0-26.01-1.0.0.0-0
rdmaSharedDevicePlugin:
image: k8s-rdma-shared-dev-plugin
repository: nvcr.io/nvidia/mellanox
version: network-operator-v26.1.0
config: |
{
"configList": [
{
"resourceName": "hca",
"rdmaHcaMax": 63,
"selectors": { "vendors": ["15b3"] }
}
]
}
kubectl apply -f nicclusterpolicy.yaml
kubectl get pods -n network-operator -o wide
验证资源真的暴露出来了
kubectl describe node <gpu-node> | grep -A8 'Allocatable'
Allocatable:
cpu: 126
nvidia.com/gpu: 8
rdma/hca: 63 ← 这一行必须有
这是最高频的第一道坎。按顺序排:
# 1. 设备插件 Pod 起来了吗,日志有没有报错
kubectl logs -n network-operator -l app=rdma-shared-dp | tail -30
# 2. 宿主机上有设备文件吗(没有 = 驱动问题,不是 K8s 问题)
ls -l /dev/infiniband/
# 3. selector 匹配到卡了吗(15b3 是 Mellanox 的 PCI vendor ID)
lspci -nn | grep 15b3第 2 步是分水岭:/dev/infiniband/ 为空说明是驱动层问题,
再怎么调 K8s 也没用,回去装 OFED。
共享设备插件的含义
rdmaHcaMax: 63 不是"有 63 张卡",而是允许最多 63 个 Pod 共享这些 HCA。
| 插件类型 | 语义 | 适合 |
|---|---|---|
| rdma-shared-device-plugin | 多个 Pod 共享同一批 HCA,rdma/hca: 1 表示"我要访问 RDMA",不独占 | AI 训练主流做法 |
| sriov-device-plugin | 每个 Pod 独占一个 VF,硬件级隔离 | 需要隔离或 QoS 的多租户 |
所以训练 Pod 写 rdma/hca: 1 就能访问全部 HCA(配合 NCCL_IB_HCA 决定实际用哪几张)——
这一点新人常误解为"只给了一张卡"。
两条路线:hostNetwork 与 MacVLAN
第三个问题——网络配置。这里要按场景分叉。
路线一:IB 场景(最简单)
纯 InfiniBand 下,建链走 IB 自己的寻址(LID/GID),不依赖 Pod 的 IP:
spec:
containers:
- name: trainer
resources:
limits:
nvidia.com/gpu: 8
rdma/hca: 1
securityContext:
capabilities:
add: ["IPC_LOCK"] # 注册内存要锁内存
只申请资源、不动网络配置就够了。
路线二:RoCE + hostNetwork
RoCE 建链要用 IP 地址,而 Pod 默认那个 overlay IP 不在 RoCE 网段上。 最省事的解法是把宿主机网卡放进 Pod 的网络命名空间:
spec:
hostNetwork: true # 关键
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: trainer
resources:
limits:
nvidia.com/gpu: 8
rdma/hca: 1
securityContext:
capabilities:
add: ["IPC_LOCK"]
# Pod 内应该能看到宿主机的 RoCE 网卡与地址
kubectl exec -it trainer-0 -- ip -brief addr | grep -E 'bond|ens'
这条规则值得单独记:hostNetwork: true 不是可选优化,是 RoCE hostnet 模式的前提。
不设的话 Pod 里看不到 RoCE 网段的地址,NCCL 会找不到可用的 IB 设备并静默回退 TCP——
功能全对,带宽只剩几分之一。
代价是同节点跑不了第二个任务:NCCL 与 MPI 的 sshd 都要监听固定端口, 第二个 Pod 会端口撞车、CrashLoopBackOff。 详见 次级 CNI 那节的取舍表。
路线三:RoCE + MacVLAN(共享集群)
要在同节点跑多个任务,就给每个 Pod 一套独立 IP:
apiVersion: mellanox.com/v1alpha1
kind: MacvlanNetwork
metadata:
name: hca
namespace: network-operator
spec:
networkNamespace: "default"
master: "bond0"
mode: "bridge"
mtu: 1500
ipam: |
{ "type": "nv-ipam", "poolName": "hca" }
---
apiVersion: nv-ipam.nvidia.com/v1alpha1
kind: IPPool
metadata:
name: hca
namespace: network-operator
spec:
subnet: 192.168.0.0/20
perNodeBlockSize: 8
gateway: 192.168.0.1
Pod 侧用注解申请:
metadata:
annotations:
k8s.v1.cni.cncf.io/networks: network-operator/hca
spec:
containers:
- name: trainer
resources:
limits:
nvidia.com/gpu: 8
rdma/hca: 1
securityContext:
capabilities:
add: ["IPC_LOCK"]
三条路线对照:
| IB | RoCE + hostNetwork | RoCE + MacVLAN | |
|---|---|---|---|
| 额外组件 | 无 | 无 | Multus/Spiderpool + IP 池 |
| Pod IP | 无关 | 宿主机 IP | 独立 IP |
| 同节点多任务 | ✅ | ❌ 端口冲突 | ✅ |
| 配置复杂度 | 低 | 低 | 中 |
在 Pod 里验证:四步
部署完不要直接跑训练,先用两个测试 Pod 对打。
kubectl apply -f tests.yaml # 起两个带 rdma/hca 的 Pod
kubectl get pods -o wide # 记下它们在哪两台节点上
第一步:设备可见
kubectl exec -it tests-rdma-hca-aaa -- ibv_devinfo | grep -E 'hca_id|state|link_layer'
kubectl exec -it tests-rdma-hca-aaa -- ibdev2netdev
第二步:拿 IP 与 GID
# hostNetwork 模式看 bond0;MacVLAN 模式看 eth0/net1
kubectl exec -it tests-rdma-hca-aaa -- ip addr show bond0
kubectl exec -it tests-rdma-hca-aaa -- show_gids | grep v2
第三步:服务端起测试
kubectl exec -it tests-rdma-hca-aaa -- \
ib_send_bw -x 3 -d mlx5_bond_0 -F --report_gbits
第四步:客户端对打
kubectl exec -it tests-rdma-hca-bbb -- \
ib_send_bw -x 3 -d mlx5_bond_0 -F --report_gbits <server_pod_ip>
合格线和裸机一样:理论线速的 90%。
kubectl delete -f tests.yaml # 测完记得删,它占着 rdma/hca 资源
先分清是配置差异还是容器开销。RDMA 数据路径绕过内核, 容器化本身几乎不引入额外开销——所以差距一定来自配置:
| 差距 | 查什么 |
|---|---|
| 完全连不上 | GID index 变了(MacVLAN 模式下 GID 表可能不同)、设备名写错 |
| 带宽只剩几分之一 | 是不是回退了 TCP:ibv_devinfo 在 Pod 内是否真能看到设备 |
| 稳定偏低 | ulimit -l 与 IPC_LOCK;或者 Pod 被调度到了跨 NUMA 的 CPU 上 |
| 延迟高、抖动大 | CPU 亲和与 NUMA:nvidia-smi topo -m 看 GPU-NIC 是不是 PIX |
注意 MacVLAN 模式下 Pod 内的 GID index 可能与宿主机不同,
每次都重新 show_gids 确认,别照抄宿主机的值。
GPU 亲和:调度器不会替你考虑
K8s 的调度器知道"这台机器有 8 张 GPU 和 63 个 rdma/hca 额度",但它不知道
哪张 GPU 该配哪张网卡。而 PCIe 那节 讲过,
GPU 与网卡不在同一个 PCIe switch 下(不是 PIX)会明显掉性能。
kubectl exec -it trainer-0 -- nvidia-smi topo -m
实践上的处理:
- 独占整机(
nvidia.com/gpu: 8)——最简单,8 张卡 8 张网卡都归你,亲和性天然满足 - 共享节点——要靠 GPU Operator 的拓扑感知调度或自定义调度器,否则可能拿到 跨 NUMA 的 GPU-NIC 组合
- 验收时一定看一眼
topo -m,别假设它是对的
排障清单
# 1. 宿主机层:驱动与设备
ofed_info -s; ls /dev/infiniband/; ibstat -l
# 2. 集群层:资源是否暴露
kubectl describe node <node> | grep -A6 Allocatable
kubectl logs -n network-operator -l app=rdma-shared-dp | tail -20
# 3. Pod 层:设备是否可见
kubectl exec -it <pod> -- ibv_devinfo | head
kubectl exec -it <pod> -- ls -l /dev/infiniband/
# 4. 网络层:地址与 GID
kubectl exec -it <pod> -- ip -brief addr
kubectl exec -it <pod> -- show_gids | grep v2
# 5. 性能层:点对点基线
kubectl exec -it <pod> -- ib_send_bw -x 3 -d <dev> -F --report_gbits <peer>
# 6. 权限层
kubectl exec -it <pod> -- sh -c 'ulimit -l'
kubectl get pod <pod> -o jsonpath='{.spec.containers[0].securityContext}'
从下往上排:宿主机 → 集群 → Pod → 网络 → 性能。跳层会浪费时间。
检查点
节点的 Allocatable 里没有 rdma/hca。第一步该确认什么?
RoCE 场景下忘记设 hostNetwork: true,最可能的后果是?
rdmaHcaMax: 63 配合 Pod 里写 rdma/hca: 1,含义是?
这节课的落点
- 搬进 K8s 要解决三件事:设备可见、资源可调度、网络配置正确
- 依赖顺序:NFD → DOCA OFED → Network Operator;驱动可宿主机预装或 Operator 下发
NicClusterPolicy暴露rdma/hca;Allocatable里有这一行才算成功- 排查资源没暴露:设备插件日志 →
/dev/infiniband/是否为空(驱动分水岭) → vendor selector rdma/hca: 1是访问许可不是卡数,共享插件下可访问全部 HCA- 三条网络路线:IB 最简单(只申请资源)、RoCE+hostNetwork(必须显式 true)、 RoCE+MacVLAN(同节点多任务)
- RDMA 容器化几乎无额外开销,测出来低一定是配置;MacVLAN 下 GID index 要重新确认
- 调度器不管 GPU-NIC 亲和,独占整机最省事,共享节点要拓扑感知调度
- 排障从下往上:宿主机 → 集群 → Pod → 网络 → 性能
延伸资料
- k8s-in-action
network/network-operator/README.md - k8s-in-action
network/network-operator/macvlan/README.md