为什么你的 RDMA 在容器里突然变慢?


容器化如何悄悄打破算力网络的物理假设。

现象:一切都“看起来正常”

某天,你把训练任务从裸机迁移到 Kubernetes,结果出现:

  • GPU 利用率从 95% 掉到 72%;
  • NCCL 没有报错;
  • 网络没有丢包;
  • NIC 带宽也能打满;
  • 延迟却明显增加。

更诡异的是:

在宿主机运行 RDMA benchmark 一切正常,换到容器里就变慢。

这就是典型的容器跨层问题。

容器里的网络和设备“可以使用”,并不意味着运行时仍然沿用了裸机上的最优物理路径。只要 CPU、GPU、NIC、PCIe、NUMA、CNI 或 cgroup 中有一层发生错位,最终都可能表现为 NCCL 变慢和 GPU 等待。

RDMA 本来依赖什么假设?

在裸机环境下,RDMA 的高性能依赖四个核心假设:

  1. GPU 与 NIC 之间的距离足够短;
  2. NUMA 绑定正确;
  3. NIC 资源完整;
  4. MTU 和优先级配置一致。

容器化后,这四个假设都可能被破坏。

问题在于,容器提供的是逻辑隔离,而 RDMA 性能依赖的是实际物理路径。Kubernetes 可以看到一个可分配的 GPU 和一个可分配的 VF,却不一定知道它们是否位于同一个 NUMA Node、同一个 PCIe Switch 或同一条 rail 上。

完整排查路径

遇到“裸机正常、容器变慢”时,可以按照下面的顺序检查:

RDMA 变慢

├── ① 是否跨 NUMA?
├── ② GPU ↔ NIC 是否仍在同一 PCIe 域?
├── ③ 是否使用 VF(SR-IOV)?
├── ④ MTU 是否被 CNI 改写?
├── ⑤ 是否启用了 hostNetwork?
├── ⑥ cgroup 是否限制 RDMA 资源?
└── ⑦ 多轨调度或 iptables / TC 是否产生干扰?

排查时不要只看“网络通不通”和“带宽能不能跑满”。容器场景里的问题经常不是硬故障,而是延迟增加、队列并行度下降、路径绕远或尾延迟抖动。

第一类问题:NUMA 亲和性被打破

裸机环境中,理想状态是:

GPU0 + NIC0 位于 NUMA0
训练进程绑定 NUMA0

进入容器后,可能出现:

  • Pod 被调度到任意 CPU;
  • 没有开启 Topology Manager;
  • 没有设置 CPU pinning;
  • CPU、GPU 和 NIC 的 NUMA 位置没有一起参与调度。

最终变成:

进程运行在 NUMA1
GPU / NIC 位于 NUMA0

这会带来:

  • 跨 UPI 访问;
  • PCIe 访问延迟增加;
  • ibv_post_send 变慢;
  • 内存分配落在远端 NUMA Node;
  • GPU、NIC 与 CPU 控制路径错位。

排查命令

在容器内检查进程 NUMA 策略:

numactl --show

检查 HCA 所属 NUMA Node:

cat /sys/class/infiniband/mlx5_0/device/numa_node

检查 GPU、NIC 与 CPU 之间的拓扑距离:

nvidia-smi topo -m

如果进程所在 NUMA Node 与 GPU、NIC 不一致,NUMA 错位就是第一嫌疑人。

第二类问题:GPU 与 NIC 拓扑被破坏

容器环境中常见的资源暴露方式是:

  • 通过 Device Plugin 暴露 GPU;
  • 通过 SR-IOV 暴露 VF。

但“GPU 可用”和“VF 可用”只代表资源已经分配给 Pod,并不代表两者物理距离最近。

裸机时可能是:

GPU0 --(PIX)-- NIC0

容器里却可能变成:

GPU0 --(SYS)-- VF3

PIX 通常表示设备间只经过单个 PCIe Bridge,而 SYS 往往意味着路径需要跨 NUMA Node 或 CPU 互连。此时 GPUDirect RDMA 的数据路径变长,延迟随之上升。

所以不能只验证 Pod 里“能看到 GPU”和“能看到 RDMA 设备”,还需要验证:

  • GPU 与 VF 是否位于同一个 NUMA Node;
  • GPU 与 VF 是否经过同一个 PCIe Switch;
  • 分配给 Pod 的 VF 是否就是这张 GPU 的近端 NIC;
  • Device Plugin 与 SR-IOV Network Device Plugin 是否使用一致的拓扑信息;
  • 调度器是否把 GPU 和 NIC 当作一个拓扑组合来分配。

第三类问题:SR-IOV 切割 NIC 资源

SR-IOV 会把一个 Physical Function(PF)切成多个 Virtual Function(VF)。它提供了隔离能力,但不会自动提升性能。

切分后可能发生:

  • QP 资源被切割;
  • NIC SRAM 被重新分配;
  • DCQCN 状态被隔离;
  • 单个 VF 可用的队列和上下文资源减少。

可能的结果包括:

  1. 单 VF 可用 QP 减少;
  2. 拥塞控制精度下降;
  3. 瞬时突发更明显;
  4. NCCL 通信并行度受限;
  5. CNP 增多,延迟抖动加剧。

排查命令

查看 RDMA 设备及其能力:

ibv_devinfo

查看网卡侧 PFC 等统计:

ethtool -S ethX | grep -i pfc

同时确认容器内使用的是 PF 还是 VF,以及 VF 的资源上限是否满足当前 NCCL 通信规模。

第四类问题:MTU 被 CNI 改写

部分 CNI 或 Overlay 网络会把容器接口的 MTU 改成 1450,甚至更小,而 RoCE 网络仍然使用 9000 MTU。

这可能导致:

  • 宿主机和容器的 MTU 不一致;
  • 容器控制路径上的报文发生碎片化;
  • 小包延迟明显上升;
  • 数据面和控制面表现不一致;
  • 问题只在容器网络命名空间里出现。

排查命令

查看容器内接口:

ip link show

查看指定接口 MTU:

cat /sys/class/net/ethX/mtu

需要同时对比宿主机和容器,而不是只检查物理 NIC 上的 MTU。

第五类问题:hostNetwork 与独立 netns

如果 Pod 没有启用 hostNetwork

  • 容器位于独立的 network namespace;
  • iptables 规则可能不同;
  • TC 可能插入额外队列;
  • conntrack、mangle 或限速规则可能进入控制路径。

RoCEv2 使用 UDP 4791。不同的 CNI、netns 和策略配置,可能让相关流量经过额外处理。即使没有丢包,也可能增加 CPU 路径开销和尾延迟。

检查时应关注:

  • Pod 是否使用 hostNetwork
  • RDMA 数据接口是否真正绕开 Overlay;
  • 容器 netns 内是否存在额外路由;
  • iptables 是否包含 conntrack、mangle 或限速规则;
  • TC 是否在接口上挂载 qdisc 或 filter;
  • 数据面和管理面是否错误地共用同一条容器网络路径。

第六类问题:cgroup RDMA 限制

Linux 支持通过 cgroup 限制 RDMA 资源:

/sys/fs/cgroup/rdma/

如果 kubelet 或运行时启用了限制,每个 Pod 可使用的 QP、CQ 等资源可能受限。

结果可能是:

  • NCCL 被迫复用 QP;
  • 通信并行度下降;
  • 大规模 rank 数下更容易触达资源上限;
  • 小规模测试正常,大规模训练变慢;
  • 性能下降却没有明显网络错误。

因此,除了确认设备已经挂载到容器,还要检查容器实际获得了多少 RDMA 资源。

第七类问题:多轨调度被打乱

在一台 8 NIC 服务器上,裸机环境可能按照下面的方式使用多轨网络:

channel0 → NIC0
channel1 → NIC1
channel2 → NIC2
...

容器化后,如果一个 Pod 只被分配到一个 VF,多 rail 的优势就会消失,通信带宽可能骤降。

即使 Pod 获得多个 VF,也需要继续确认:

  • VF 是否来自不同 rail;
  • VF 是否分别靠近对应 GPU;
  • NCCL 是否识别并使用全部 HCA;
  • 容器内的设备命名是否改变了 NCCL HCA 选择;
  • 轨道之间是否出现流量不均;
  • Pod 调度是否把所有 VF 集中到同一物理 NIC 或同一 Leaf。

多轨性能问题经常不是“少了一张网卡”这么简单,而是物理 rail、逻辑 VF 和 NCCL channel 三者的映射被打乱。

一个真实案例

场景:

  • 16 个节点;
  • 每个节点 8 张 GPU;
  • 每节点使用 8 个 SR-IOV VF;
  • 由 Kubernetes 调度。

现象:

  • GPU 利用率只有 73%;
  • 单机裸机测试可以达到 96%;
  • 容器内 RDMA latency 增加 18%;
  • NUMA 位置不一致;
  • VF 不在 GPU 最近的 PCIe Switch 下。

修复动作:

  • 启用 Topology Manager;
  • 设置 single-numa-node policy;
  • 手动对齐 GPU 与 VF。

修复后,GPU 利用率恢复到 92%。

这个案例说明,即使 NIC 带宽能够跑满、NCCL 也没有报错,错误的 NUMA 和 PCIe 路径仍然会让 GPU 在同步点等待。

容器化到底破坏了什么?

容器的本质,是把逻辑隔离叠加在物理资源之上;RDMA 则是一项极度依赖物理路径的技术。

当逻辑层忽略物理层时,性能就会悄悄下降:

  • RDMA 的性能来自物理拓扑,而不是 API;
  • 容器不会自动理解 PCIe 距离;
  • NUMA 错位是容器算力集群中最常见的隐形问题之一;
  • SR-IOV 是隔离工具,不是性能增强工具;
  • 裸机正常,不代表容器正常。

因此,容器化 RDMA 的正确验收方式不能只是“设备能看到、网络能连通”。至少还要验证:

  1. CPU、GPU、NIC 是否位于一致的 NUMA 边界;
  2. GPU 与 VF 的 PCIe 距离是否符合预期;
  3. PF / VF 的 QP、CQ 等资源是否足够;
  4. 宿主机与容器 MTU、QoS 配置是否一致;
  5. netns、iptables 和 TC 是否引入额外路径;
  6. cgroup 是否限制 RDMA 资源;
  7. 多 rail 是否仍然被 NCCL 正确使用。

真正的问题往往不在某一个组件内部,而在 Kubernetes 调度、容器网络、PCIe 拓扑、NUMA、SR-IOV 和 NCCL 的交界处。