主题
第五部分 高速网络与分布式训练
前面四个部分,我们学会了让单台 GPU 服务器跑起来、让 Kubernetes 把 GPU 调度给训练任务,还掌握了 MIG/MPS 等共享技术。但现代大模型训练从来不是"单机游戏"——一个千亿参数模型往往需要成百上千张 GPU 协同工作数月。把这些 GPU 连成一个"超级计算机"的,是 高速网络(RDMA/InfiniBand/RoCE) 和 通信库(NCCL);让它们持续"吃饱数据"的,是 高性能存储。
本部分解决三个问题:网络怎么连(RDMA/IB/RoCE 原理与运维)、GPU 怎么通信(NCCL、GPUDirect、性能测试与排障)、数据怎么供(K8s 高速网络配置、训练存储选型与"GPU 等数据"治理)。
第 18 章 RDMA 与 InfiniBand 基础
本章目标
- 理解传统 TCP/IP 网络为什么满足不了 AI 训练,讲清 RDMA 的核心原理
- 区分 InfiniBand、RoCE v1/v2 三种主流高速网络方案
- 认识 IB/RoCE 网络的硬件组件:HCA、交换机、线缆、Subnet Manager
- 熟练使用 ibstat、ib_write_bw 等运维与测速命令
- 能处理链路 down、误码增长、PFC 风暴等常见网络故障
18.1 为什么 TCP/IP 不够
先看现象:8 张 GPU 做 AllReduce,走 25G 以太网 + TCP/IP,有效带宽可能只有 10Gbps 不到,延迟上百微秒,CPU 占用还飙升。这不是网卡不行,是 TCP/IP 的传输方式天生有短板。
TCP/IP 发送一个数据包要经过什么:
发送方:应用 ──①拷贝──> 内核协议栈(TCP/IP 封装、校验)──②拷贝──> 网卡 ──> 网络
接收方:网络 ──> 网卡 ──③拷贝──> 内核协议栈(重组、校验)──④拷贝──> 应用一次收发至少 4 次数据拷贝、2 次用户态/内核态切换,全程消耗 CPU。对网页浏览无所谓,但训练时每张 GPU 每个 step 要和几十上百个对端交换几十上百 MB 的梯度数据,CPU 根本忙不过来,延迟和带宽都不可接受。
RDMA(Remote Direct Memory Access,远程直接内存访问) 的三个核心思想:
| 特性 | 含义 | 收益 |
|---|---|---|
| 内核旁路(Kernel Bypass) | 应用程序直接操作网卡硬件队列,不经过内核协议栈 | 无系统调用、无上下文切换,延迟降至微秒级 |
| 零拷贝(Zero Copy) | 网卡通过 DMA 直接读写应用注册的内存(Pin Memory),数据不再反复搬运 | CPU 几乎零消耗 |
| CPU 卸载(Offload) | 传输可靠性、分段重组等由网卡硬件完成 | CPU 可以专心做计算 |
用一句话对比两种路径:传统 TCP/IP 是"应用→拷贝→内核→拷贝→网卡"层层中转;RDMA 是"网卡 DMA 直接读写对端应用内存,硬件完成、CPU 不参与"。效果:RDMA 端到端延迟可低至 1~5 微秒(TCP/IP 通常 20~100+ 微秒),400G 网卡可以跑满线速且 CPU 占用接近 0。
一句话记忆:TCP/IP 是"寄快递要层层中转",RDMA 是"对方直接来你仓库搬货,你只管开门"。
18.2 InfiniBand 与 RoCE 对比
RDMA 是一种能力,承载它有三种主流网络技术:
RoCE(RDMA over Converged Ethernet)即"跑在以太网上的 RDMA":v1 工作在二层、不可路由;v2 走 UDP/IP、可跨网段路由。三者对比:
| 维度 | InfiniBand(IB) | RoCE v1 | RoCE v2 |
|---|---|---|---|
| 底层网络 | 专用 IB 交换机和线缆 | 以太网(同 VLAN 内) | 以太网 + IP,可跨网段路由 |
| 是否要求无损网络 | IB 协议原生保证 | 需要 | 需要 PFC + ECN 配置 |
| 延迟 | 最低(约 1μs) | 略高 | 略高(约 2~5μs) |
| 典型速率 | NDR 400G / HDR 200G | — | 100G/200G/400G |
| 成本 | 高(专用设备,基本只有 NVIDIA/Mellanox 生态) | 低 | 低(复用以太网交换机) |
| 典型用户 | 超算中心、大模型训练集群 | 少见 | 互联网大厂自建 AI 集群 |
无损以太网(Lossless Ethernet)是什么:RoCE 假定网络不丢包(RDMA 的重传机制很"笨",丢一个包会大规模重传,性能雪崩)。但以太网交换机在拥塞时天然会丢包,于是需要两个机制把以太网改造成"无损":
- PFC(Priority Flow Control,优先级流控):给 RoCE 流量打上高优先级(如优先级 3),接收方缓冲快满时向上游发送 PAUSE 帧让对端"暂停发货",避免溢出丢包。
- ECN(Explicit Congestion Notification,显式拥塞通知):交换机在拥塞早期就在 IP 头打标记,通知发送方主动降速(配合 DCQCN 拥塞控制算法)。
运维要点:PFC 是双刃剑——配置不当会出现"PFC 风暴"(见 18.5)。IB 协议原生可靠、不需要这些机制,这也是 IB 在大规模训练中更省心的原因。
18.3 硬件组件
一个 IB/RoCE 训练网络由四类硬件组成:
1)HCA / 网卡(Host Channel Adapter)
- Mellanox ConnectX 系列(现属 NVIDIA):ConnectX-5(100G)、ConnectX-6/6 Dx(200G)、ConnectX-7(400G NDR)。AI 集群事实标准,同时支持 IB 和 RoCE 模式。
- BlueField DPU:集成 ARM 核和可编程引擎的"智能网卡",可把存储、安全、虚拟化任务卸载到 DPU,常用于云厂商裸金属场景。
- 查看本机 HCA:
lspci | grep -i mellanox,或用ibstat(见 18.4)。
2)交换机:IB 用 NVIDIA Quantum 系列(NDR 400G);RoCE 用支持 PFC/ECN 的数据中心以太网交换机(NVIDIA Spectrum、华为 CE、Arista 等)。大规模集群采用 Fat-Tree(胖树)/ Dragonfly+ 拓扑,追求"无阻塞":任意两台服务器间通信带宽相等。
3)线缆与光模块
| 类型 | 全称 | 特点 | 典型用途 |
|---|---|---|---|
| DAC | Direct Attach Copper 直连铜缆 | 便宜、功耗低、无需光模块,但距离短(≤3~5 米) | 机柜内服务器到 TOR 交换机 |
| AOC | Active Optical Cable 有源光缆 | 一头一尾是固定光模块的光缆,距离中等(≤100 米),即插即用 | 跨机柜连接 |
| 光模块 + 光纤 | 如 QSFP56/OSFP 光模块 + MPO 光纤 | 距离最远(可达数公里),灵活但贵、可维护性差(插拔多) | 跨楼层/跨机房 |
4)Subnet Manager(子网管理器,SM)
IB 网络是"被管理的网络":每台 HCA 的 LID(本地标识,类似 IP 地址)、路由表都由一个 Subnet Manager 统一分配和计算。开源实现是 OpenSM(opensm),通常运行在管理节点或 IB 交换机内置。
bash
sudo systemctl enable --now opensm # 管理节点上启动 OpenSM;用 ibstat | grep -i sm 查看 SM LID注意:一个 IB 子网必须有且通常只有一个主 SM(可以有备用)。没有 SM 端口全部起不来;SM 挂了,新节点无法加入。RoCE 不需要 SM(走标准以太网配置)。
18.4 常用运维命令
bash
# ========== 状态查看 ==========
ibstat # HCA 详情:State: Active(链路+SM 正常)、Physical state: LinkUp、
# Rate: 400(协商速率)、SM lid(子网管理器存在且非 0)
ibstatus # 各端口简明 up/down 状态
show_gids # 查看 IB/RoCE 模式与 GID;NCCL_IB_GID_INDEX 选的就是这里的行号
sudo iblinkinfo | less # 整张网络的链路拓扑(哪个交换机哪个口)
sudo perfquery # 端口计数器:重点看 SymbolErrorCounter、PortRcvErrors
# ========== 连通性与性能测试(perftest 工具集)==========
sudo apt install -y perftest # 或 yum install perftest
sudo ibping -S & # 服务端先起 ibping 守护
ibping -L <对端LID> # 客户端:IB 层的 ping
ib_write_bw -d mlx5_0 --report_gbits # 服务端:RDMA Write 带宽测试(最常用)
ib_write_bw -d mlx5_0 --report_gbits <对端IP> # 客户端
# 健康值:200G 网卡应接近 190+ Gbits/sec;400G 应接近 390+
ib_write_lat -d mlx5_0 <对端IP> # 延迟测试,健康值 1~3 微秒(对端先不带参数启动)
# ========== RoCE 网卡用以太网工具 ==========
ethtool ib0 # 查看速率、链路
ethtool -S ib0 # 详细计数器(含 PFC 帧统计)
mlnx_qos -i ib0 # 查看 PFC/ECN 配置(Mellanox 工具)18.5 常见故障
故障 1:端口 State 是 Down / Initializing
排查顺序:物理链路(换光模块/线缆,看两端 LinkUp 灯)→ 对端交换机端口是否启用 → SM 是否在运行(ibstat 看 SM lid 是否为 0)→ 端口协商速率不匹配。
故障 2:误码(Symbol Error)持续增长
用 sudo perfquery | grep -i -E "symbol|rcv.*err" 观察。误码持续增长通常是光模块/线缆质量问题、接口脏污或线缆弯折过度。处理:清洁/更换线缆光模块,复位计数器(sudo perfquery -R <lid> <port>)后观察是否复发。轻微误码不可怕,持续增长才需换件。
故障 3:RoCE 网络 PFC 风暴
现象:某台机器网卡故障或配置错误,疯狂发送 PAUSE 帧,上游交换机端口被"暂停",拥塞扩散到整个 fabric,所有训练任务同时卡住。用 ethtool -S ib0 | grep -i pause 看 PAUSE 帧收发是否暴涨。
处理与预防:定位并隔离风暴源(异常节点下线);交换机启用 PFC Watchdog(持续 PAUSE 自动丢弃该队列流量);合理划分 PFC 优先级。
故障 4:OpenSM 异常
现象:新节点加入后 ibstat 一直 Initializing,或全网端口掉线。检查 systemctl status opensm、journalctl -u opensm -n 100,并确认 ibstat | grep "SM lid" 全网一致且非 0。常见原因:SM 进程崩溃、两个主 SM 抢 LID、交换机内置 SM 与节点 SM 冲突。处理:确认 SM 唯一性,重启 opensm。
本章小结
- TCP/IP 因多次拷贝、内核参与、CPU 消耗大,无法满足分布式训练;RDMA 通过内核旁路 + 零拷贝 + CPU 卸载实现微秒级延迟、线速带宽。
- InfiniBand 是专用无损网络、性能最好但贵;RoCE v2 跑在标准以太网上、可路由、成本低,但必须配置 PFC/ECN 实现无损。
- IB 网络必备组件:ConnectX 系列 HCA、IB 交换机、DAC/AOC/光模块线缆、Subnet Manager(OpenSM)。
- 运维三板斧:
ibstat看状态、perfquery看误码、ib_write_bw测带宽。 - 常见故障:链路 down 查物理层和 SM,误码增长换线缆光模块,PFC 风暴找源头并启用 Watchdog。
动手实验
- 在有两台 IB/RoCE 机器的环境中,执行
ibstat和show_gids,记录端口 State、Rate、GID,判断网卡工作在 IB 还是 RoCE 模式。 - 用
ib_write_bw -d mlx5_0 --report_gbits在两台机器间测速,对比网卡标称速率计算达成率;再用ib_write_lat记录延迟。 - 执行
sudo perfquery找到 SymbolErrorCounter 的值,隔 10 分钟再看一次,判断是否有误码增长。 - 思考题:为什么 RoCE 网络里"丢包"比"拥塞降速"更可怕?(提示:RDMA 重传机制 go-back-N)
第 19 章 NCCL 与 GPU 通信
本章目标
- 理解 AllReduce、AllGather 等集合通信原语和环形 AllReduce 的思想
- 掌握 NCCL 的 Ring/Tree 算法与机内/机间通道的自动选择
- 理解 GPUDirect RDMA 的价值与 nvidia-peermem 模块
- 熟记常用 NCCL 环境变量,会查会用
- 会用 nccl-tests 测单机与多机通信性能,读懂 busbw/algbw
- 掌握 NCCL 常见报错的排查套路
19.1 集合通信基础
分布式数据并行(DDP)训练中,每个 GPU 算出一份梯度,所有 GPU 必须把梯度"汇总求平均后再同步给所有人"——这就是最典型的 集合通信。常见原语:
| 原语 | 含义 | 训练中的用途 |
|---|---|---|
| Broadcast | 一个节点把数据发给所有节点 | 广播初始参数 |
| Reduce | 所有节点的数据按位规约(求和/求平均),结果只给根节点 | 梯度求和(少用,一般用 AllReduce) |
| AllReduce | 规约后所有人都拿到结果 | 梯度同步,DDP 的核心 |
| AllGather | 每个节点把各自的数据拼起来,所有人都拿到完整拼接结果 | ZeRO-3/FSDP 收集参数分片 |
| ReduceScatter | 规约后把结果切片分发给各节点 | ZeRO/FSDP 分发梯度分片 |
环形 AllReduce(Ring AllReduce)思想:N 张 GPU 排成一个环,每张卡只和左右邻居通信,分两个阶段:
阶段一 ReduceScatter(N-1 步):每张卡的数据切成 N 份,沿环传递并累加,
结束后每张卡持有"全局求和结果"的 1/N 切片
阶段二 AllGather(N-1 步): 各切片沿环继续传递补齐,每张卡拿到完整结果
每步每张卡只收发 (数据量/N) 大小
总通信量 ≈ 2 × 数据量 × (N-1)/N ≈ 2 × 数据量(与卡数几乎无关!)关键结论:Ring AllReduce 的每卡通信量基本不随卡数增长,因此可扩展到成百上千卡。这也是"每张卡都需要满速网卡"的原因。
19.2 NCCL 原理
NCCL(NVIDIA Collective Communications Library) 是 NVIDIA 官方的 GPU 集合通信库,PyTorch/DeepSpeed 的分布式通信后端默认都是它。运维需要理解三点:
1)拓扑感知:NCCL 启动时自动探测硬件拓扑——GPU 之间走 NVLink 还是 PCIe、GPU 和哪块网卡在同一 PCIe 交换机上——然后自动选择最优通道,无需人工配置(配置只用于纠偏)。
2)Ring / Tree 双算法:Ring 延迟随节点数线性增长但带宽利用率高,适合大消息;Tree 双二叉树结构,延迟随 log(N) 增长,适合中小消息。NCCL 按消息大小和规模自动选择,可用 NCCL_ALGO=Ring|Tree 强制指定(排障时用)。
3)机内机间分层:
机内:GPU ←NVLink(600~900GB/s,远快于网络)→ GPU
机间:GPU ←GPUDirect RDMA→ 网卡 ←IB/RoCE 网络→ 对端网卡 → GPUNCCL 自动把"机内走 NVLink、机间走 RDMA"组合成混合通信路径。运维要保证的是:机间路径上每一环(GPU→网卡、网卡→网络)都是通的、快的。
19.3 GPUDirect RDMA
没有 GPUDirect 时,GPU 数据出机要走"显存 → 拷贝到系统内存 → CPU 参与 → 网卡读内存 → 发出",两次显存↔内存拷贝,CPU 被卷入。
GPUDirect RDMA(GDRDMA) 允许网卡通过 PCIe 直接 DMA 读写 GPU 显存,CPU 和系统内存完全不参与。收益:机间通信延迟更低、带宽更高、CPU 零占用。
内核模块 nvidia-peermem:GDRDMA 需要内核允许第三方设备(网卡)对 GPU 显存做 P2P 访问,由 nvidia-peermem 模块实现(新版驱动安装 DOCA/OFED 后自动处理)。
bash
# 检查模块是否加载
lsmod | grep -E "nvidia_peermem|peer_mem"
# 手动加载
sudo modprobe nvidia-peermem
# 验证 GDR 是否生效:NCCL 日志里会出现
# NCCL INFO Channel 00 : ... via NET/IB/0/GDRDMA
# 若没有 GDRDMA 字样,说明退化为经过内存拷贝前提条件:GPU 与网卡最好在同一个 PCIe Root Complex / PCIe Switch 下(拓扑亲和),否则 P2P 可能不可用或性能差。用
nvidia-smi topo -m查看 GPU 与 NIC 的亲和关系(显示NODE/SYS等)。
19.4 NCCL 环境变量运维速查
| 变量 | 作用 | 何时用 |
|---|---|---|
NCCL_DEBUG=INFO | 打印详细初始化日志(选了哪块网卡、什么通道) | 排障第一件事,必开 |
NCCL_DEBUG_SUBSYS=INIT,NET,GRAPH | 控制日志子系统 | 配合上面定位具体环节 |
NCCL_SOCKET_IFNAME=eth0 | 指定 bootstrap(建连)用的管理网口 | 多网口机器选错口导致卡住/超时 |
NCCL_IB_HCA=mlx5_0,mlx5_1 | 指定/排除用于通信的 RDMA 网卡 | 机器有多块 HCA 或要屏蔽坏卡 |
NCCL_IB_GID_INDEX=3 | RoCE 时选择 GID(v2 通常是 IPv4/IPv6 对应行) | RoCE 环境连不通时最常调的参数 |
NCCL_IB_DISABLE=1 | 禁用 IB/RoCE,回退到 TCP socket | 快速验证"是不是网络问题" |
NCCL_P2P_DISABLE=1 | 禁用机内 GPU P2P 直连 | 排查机内通信/硬件兼容问题 |
NCCL_NET_GDR_LEVEL | 控制 GPUDirect RDMA 使用的拓扑距离阈值 | GDR 不生效或引发异常时 |
NCCL_ALGO=Ring/Tree | 强制通信算法 | 性能对比、复现问题 |
NCCL_SOCKET_NTHREADS / NCCL_NSOCKS_PERTHREAD | socket 建连并发度 | 大规模任务建连慢时调大 |
NCCL_TIMEOUT / TORCH_NCCL_BLOCKING_WAIT | 超时与等待行为 | 排查 timeout 类报错 |
排障黄金组合:NCCL_DEBUG=INFO NCCL_DEBUG_SUBSYS=INIT,NET NCCL_IB_DISABLE=0
19.5 NCCL Tests 实战
nccl-tests 是 NCCL 官方性能测试工具,是验收 GPU 集群网络的"标尺"。
bash
# 编译(需要 MPI 与 CUDA),生成 build/all_reduce_perf 等二进制
git clone https://github.com/NVIDIA/nccl-tests.git && cd nccl-tests
make MPI=1 MPI_HOME=/usr/lib/x86_64-linux-gnu/openmpi CUDA_HOME=/usr/local/cuda NCCL_HOME=/usr单机 8 卡测试:
bash
./build/all_reduce_perf -b 128M -e 4G -f 2 -g 8
# -b 起始消息大小 -e 结束大小 -f 递增倍数 -g GPU 数双机 16 卡测试(通过 MPI 拉起):
bash
mpirun -np 16 -N 8 -hostfile hosts \
-x NCCL_DEBUG=INFO -x NCCL_IB_HCA=mlx5_0 -x LD_LIBRARY_PATH \
./build/all_reduce_perf -b 128M -e 4G -f 2 -g 1
# hosts 文件每行写一个节点,-N 8 表示每节点 8 进程读懂结果(关键!):
# size count type redop time algbw busbw
# (us) (GB/s) (GB/s)
1073741824 268435456 float sum 15234 70.5 123.3- algbw(算法带宽) = 消息大小 ÷ 耗时,反映应用视角"数据传了多快"。
- busbw(总线带宽) = algbw × 算法系数(AllReduce 系数为
2×(n-1)/n),反映实际打到硬件链路上的带宽,是对标网卡速率的指标。 - 验收标准:大消息(1G 以上)busbw 应达理论带宽 85% 以上。例如双机 16 卡、每机 1 张 200G(25GB/s)网卡,机间 busbw 应 ≥ 20GB/s;小消息受延迟主导,带宽低是正常的。
19.6 NCCL 报错排查套路
套路总纲:开 NCCL_DEBUG=INFO 重跑,从第一处 WARN/ERROR 往前后看,对照下文。
1)NCCL timeout / watchdog 超时
含义:某个 rank 没在超时时间内完成通信,本质通常是"有人掉队"而非网络慢:某节点 GPU 掉卡、任务 OOM、某 rank 进程崩溃 → 看各 rank 日志找"先死的人";网络抖动/误码导致传输极慢 → perfquery 查误码、ib_write_bw 复测。临时缓解可调大 TORCH_NCCL_TIMEOUT,但治标不治本。
2)Connection refused / bootstrap 失败
rank 之间建立初始 TCP 连接失败:NCCL_SOCKET_IFNAME 选错网口 → 指定正确的管理网口;多机间管理网络不通(防火墙/安全组)→ 先 ping/nc 验证;容器内主机名/IP 不对 → 检查 MASTER_ADDR 与 hostNetwork 配置。
3)IB 自动回退到 socket,性能骤降
现象:NCCL 日志出现 NET/Socket 而不是 NET/IB,busbw 只有几 GB/s:容器内看不到 IB 设备 → 检查 /dev/infiniband 挂载、rdma device plugin;NCCL_IB_HCA 排除了所有卡 → 检查该变量;RoCE 环境 NCCL_IB_GID_INDEX 不对 → 用 show_gids 确认后显式指定。
4)NCCL WARN Call to ibv_modify_qp failed 等 IB 动词错误
多为 RoCE 配置问题:GID 选错、MTU 两端不一致。检查两端 ibstat 的 MTU、交换机 PFC 配置。
本章小结
- 集合通信是分布式训练的"血液":AllReduce 同步梯度、AllGather/ReduceScatter 支撑 FSDP/ZeRO;Ring AllReduce 让每卡通信量与规模解耦。
- NCCL 自动感知拓扑,机内走 NVLink、机间走 RDMA,Ring/Tree 自动选择。
- GPUDirect RDMA 让网卡直接读写显存,依赖 nvidia-peermem 模块,用
nvidia-smi topo -m确认 GPU-网卡亲和性。 - 排障第一步永远是
NCCL_DEBUG=INFO;RoCE 最常调NCCL_IB_GID_INDEX,多网口最常调NCCL_SOCKET_IFNAME。 - 用 nccl-tests 的 all_reduce_perf 验收网络,看大消息的 busbw,应达理论带宽 85% 以上。
动手实验
- 单机多卡运行
./build/all_reduce_perf -b 8M -e 1G -f 2 -g <卡数>,画出消息大小-busbw 曲线,找出带宽"爬坡"拐点。 - 双机环境用 mpirun 跑 16 卡测试,对比加/不加
NCCL_IB_DISABLE=1的 busbw 差异,直观感受 RDMA 与 TCP 的差距。 - 用
NCCL_DEBUG=INFO重跑,在日志中找到GDRDMA字样确认 GPUDirect 生效;用nvidia-smi topo -m分析 GPU 与 NIC 拓扑关系。 - 思考题:为什么小消息的 busbw 远低于大消息?这对训练框架的梯度桶(bucket)切分有什么启发?
第 20 章 K8s 中的 RDMA 与高性能网络
本章目标
- 理解容器中使用 RDMA 的两种模式:共享与独占
- 会用 Multus 给 Pod 挂载第二张 RDMA 网卡
- 了解 SR-IOV 的完整配置流程与 Network Operator
- 能编写带高速网络的分布式训练任务 YAML
- 建立"模型规模 ↔ 网络带宽"的估算直觉
20.1 容器网络方案对比
K8s 默认的 CNI(Calico/Flannel 等)只提供普通 TCP/IP 网络,训练流量不能走它。让 Pod 用上 RDMA 有两条路线:
| 维度 | 共享模式(Macvlan / Host-network) | 独占模式(SR-IOV + RDMA Device Plugin) |
|---|---|---|
| 原理 | 多个 Pod 共享物理网卡(PF);或直接共用宿主机网络栈 | 网卡虚拟出多个 VF,每个 VF 独占分配给一个 Pod |
| 资源管理 | K8s 看不见 RDMA 资源,无法按卡调度 | 以 rdma/xxx: 1 形式纳入 K8s 资源调度,精确分配 |
| 隔离性 | 弱(共享带宽、共享 QP 资源) | 强(硬件级 VF 隔离) |
| 性能 | 接近裸机,但多 Pod 争抢 | 接近裸机,独占带宽 |
| 配置复杂度 | 低 | 高(网卡固件、VF、交换机都要配) |
| 适用场景 | 训练任务整卡整机独占、快速落地 | 多租户、训练与推理混部、云上裸金属 |
还有一种 RDMA Shared Device Plugin(
rdma/shared_dp):共享模式下也能把 RDMA 设备注册成 K8s 资源(按 HCA 数限额分配),是"轻量版资源管理"。
训练场景经验:大模型训练通常整节点独占,hostNetwork + 直通 /dev/infiniband 是最常见、问题最少的组合;需要多租户隔离时才上 SR-IOV。
20.2 Multus 多网卡
Multus 是一个"CNI 的调度器",让一个 Pod 同时拥有多块网卡:eth0 走默认 CNI(管理/存储),net1 走高速 RDMA 网络。
步骤 1:部署 Multus:kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset.yml
步骤 2:定义第二张网卡(NetworkAttachmentDefinition)
以 Macvlan 共享 IB/RoCE 网卡为例(RoCE 场景,net1 走 RoCE):
yaml
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
name: roce-net
spec:
config: |
{
"cniVersion": "0.3.1",
"type": "macvlan",
"master": "ib0", # 宿主机 RoCE 网卡名
"mode": "bridge",
"ipam": {
"type": "whereabouts", # 集群级 IP 分配
"range": "192.168.100.0/24"
}
}bash
kubectl apply -f roce-net.yaml步骤 3:Pod 挂载第二张网卡
yaml
apiVersion: v1
kind: Pod
metadata:
name: rdma-test
annotations:
k8s.v1.cni.cncf.io/networks: roce-net # 挂第二张网卡
spec:
containers:
- name: test
image: nvcr.io/nvidia/pytorch:24.05-py3
command: ["sleep", "infinity"]
securityContext:
capabilities:
add: ["IPC_LOCK"] # RDMA 需要锁定内存
resources:
limits:
nvidia.com/gpu: 2
volumeMounts:
- name: infiniband
mountPath: /dev/infiniband # 关键:把宿主机 RDMA 设备挂进容器(共享模式)
volumes:
- name: infiniband
hostPath:
path: /dev/infiniband验证:kubectl exec -it rdma-test -- ip addr 应看到 net1;kubectl exec -it rdma-test -- ibstat 容器内能看到 HCA,之后可在两个 Pod 间跑 ib_write_bw 测速。
20.3 SR-IOV 配置流程
SR-IOV 让一块物理网卡(PF)虚拟出多块虚拟网卡(VF),每个 VF 有独立的 PCIe 地址,可独占分配给 Pod。
步骤 1:网卡开启 SR-IOV 并创建 VF(以 Mellanox 为例)
bash
# 确认固件已开启 SR-IOV
sudo mst start
sudo mlxconfig -d /dev/mst/mt4125_pciconf0 q | grep -i sriov
# 创建 8 个 VF(重启会丢失,需写入启动脚本持久化)
echo 8 | sudo tee /sys/class/net/ib0/device/sriov_numvfs
lspci | grep -i mellanox # 能看到新增的 Virtual Function步骤 2:部署 SR-IOV Device Plugin 与 CNI
yaml
# SR-IOV device plugin 的 ConfigMap:把 VF 注册成 K8s 资源
apiVersion: v1
kind: ConfigMap
metadata:
name: sriovdp-config
namespace: kube-system
data:
config.json: |
{
"resourceList": [{
"resourceName": "mlnx_rdma_vf",
"selectors": { "vendors": ["15b3"], "isRdma": true }
}]
}部署后用 kubectl describe node gpu-node-01 | grep rdma 应看到可调度资源 rdma/mlnx_rdma_vf: 8。
步骤 3:SR-IOV 模式的 NetworkAttachmentDefinition + Pod
yaml
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
name: sriov-rdma-net
annotations:
k8s.v1.cni.cncf.io/resourceName: rdma/mlnx_rdma_vf
spec:
config: |
{
"cniVersion": "0.3.1",
"type": "sriov",
"ipam": { "type": "whereabouts", "range": "192.168.200.0/24" }
}Pod 里申请 rdma/mlnx_rdma_vf: 1,调度器就会自动把任务调度到有剩余 VF 的节点。
步骤 4(推荐):用 Network Operator 一键管理
手工维护 VF 创建、device plugin、CNI、OFED 驱动很繁琐。NVIDIA Network Operator 用 CRD(NicClusterPolicy)把这些组件全部托管——只需声明 ofedDriver、sriovDevicePlugin、rdmaSharedDevicePlugin 等组件的期望版本,Operator 自动完成 VF/驱动/CNI 配置。它与第四部分学过的 GPU Operator 是"姊妹组件",生产环境通常两者一起部署。
20.4 K8s 分布式训练任务完整示例
以 Volcano 的 vcjob(也可用 Kubeflow PyTorchJob,思想相同)为例:2 节点 × 8 卡 PyTorch DDP 训练,走 Multus RoCE 网络。
yaml
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: ddp-train
spec:
minAvailable: 16 # Gang Scheduling:16 个 Pod 齐了才启动
schedulerName: volcano
tasks:
- name: worker
replicas: 16
template:
metadata:
annotations:
k8s.v1.cni.cncf.io/networks: roce-net # 挂 RDMA 网卡
spec:
# hostNetwork: true # 方案B:直接用宿主机网络,性能最简,见下文讨论
restartPolicy: Never
containers:
- name: pytorch
image: nvcr.io/nvidia/pytorch:24.05-py3
command: ["bash", "-c", "torchrun --nnodes=16 --nproc_per_node=8 train.py"]
env:
- { name: NCCL_DEBUG, value: "INFO" }
- { name: NCCL_IB_HCA, value: "mlx5_0,mlx5_1" } # 指定 RDMA 网卡
- { name: NCCL_SOCKET_IFNAME, value: "eth0" } # bootstrap 走默认 CNI
- { name: NCCL_IB_GID_INDEX, value: "3" } # RoCE v2(show_gids 确认)
securityContext:
capabilities:
add: ["IPC_LOCK"]
resources:
limits:
nvidia.com/gpu: 8
volumeMounts:
- { name: infiniband, mountPath: /dev/infiniband }
- { name: shm, mountPath: /dev/shm }
volumes:
- name: infiniband
hostPath: { path: /dev/infiniband }
- name: shm
emptyDir: { medium: Memory, sizeLimit: 16Gi } # PyTorch dataloader 需要大 shmhostNetwork 讨论:hostNetwork: true 时 Pod 直接共享宿主机网络栈,无需 Multus 就能用全部网卡,配置最简、性能无损;代价是端口可能冲突、租户隔离弱。经验法则:独占节点的训练任务用 hostNetwork;多租户或需要灵活组网用 Multus。
20.5 网络与算力的匹配
运维经常要回答:"这个集群配 100G 够不够?要不要上 400G?"估算思路如下。
梯度同步数据量:数据并行中,每个 step 每张卡 AllReduce 的梯度量 ≈ 模型参数量 × 每参数字节数(混合精度梯度通常 FP32 4 字节)。
例:7B 参数模型,FP32 梯度 → 每卡每 step 通信量 ≈ 7×10^9 × 4 B = 28 GB
(AllReduce 实际链路流量约 2×28 GB)带宽够不够,看"通信时间 vs 计算时间":
单步通信时间 = 通信量 ÷ 有效网络带宽 单步计算时间 = 单步 FLOPs ÷ GPU 有效算力
健康目标:通信时间 ≤ 计算时间的 20~30%(否则 GPU 在等网络)经验直觉(数据并行 + 混合精度,无通信压缩):
| 模型规模 | 每卡梯度/step | 建议每 GPU 机间带宽 |
|---|---|---|
| ≤ 1B | ~4 GB | 25~50 Gbps 可接受 |
| 7B~13B | 28~52 GB | 100~200 Gbps 起步 |
| 70B+(通常配 ZeRO/3D 并行) | 更复杂,通信模式混合 | 每 GPU 200~400 Gbps,IB 优先 |
注意现代训练用 梯度分桶 + 通信计算重叠,实际压力比理论值小;但带宽永远"越大越省心"。这也是高端训练集群坚持 8 卡配 8×400G(NDR)的原因——每 GPU 一张满速网卡。
本章小结
- 容器用 RDMA 两条路:共享模式(Macvlan/hostNetwork,简单)与独占模式(SR-IOV,隔离强、可调度)。
- Multus 通过 NetworkAttachmentDefinition + Pod annotation 给 Pod 挂第二张高速网卡。
- SR-IOV 流程:网卡开 VF → device plugin 注册资源 → NAD + Pod 申请;生产推荐 Network Operator 托管。
- 训练任务 YAML 关键点:Gang Scheduling、
/dev/infiniband挂载、IPC_LOCK、NCCL 环境变量、大 shm;独占场景可用 hostNetwork。 - 带宽估算:算每卡每 step 通信量,保证通信时间 ≤ 计算时间的 20~30%。
动手实验
- 在测试集群部署 Multus,创建 macvlan 类型的 NAD,启动一个带 networks 注解的 Pod,用
ip addr确认出现 net1。 - 在 RDMA 测试 Pod 内执行
ibstat验证容器内 RDMA 可用;删除/dev/infiniband挂载重建 Pod,对比报错,理解设备挂载的必要性。 - 计算题:13B 模型、FP32 梯度、每 GPU 100Gbps(≈12.5GB/s)有效带宽,单步通信量 52GB,问单步通信时间约多少秒?如果单步计算是 2 秒,通信占比是否健康?
第 21 章 高性能存储与数据供给
本章目标
- 理解训练负载的两大 IO 特征
- 掌握 NFS/Lustre/Ceph/JuiceFS/Alluxio 等方案的选型
- 了解 GPUDirect Storage
- 掌握存储监控、checkpoint 风暴防护、数据预热等运维要点
- 学会识别和治理"GPU 空转等数据"
21.1 训练 IO 特征
GPU 是"吃饭极快"的算力怪兽,存储供给跟不上,再贵的 GPU 也在空转。训练对存储有两类截然不同的压力:
1)海量小文件读取(数据集加载):图像/语音数据集动辄数百万个小文件,训练时每 step 随机读取一批,形成 高并发随机读、高元数据压力(每秒数万次 open/stat)。瓶颈通常在 IOPS 和元数据服务,而不是带宽。
2)Checkpoint 大文件突发写入:每隔 N 个 step 保存一次模型状态(几十 GB ~ 数 TB),且成百上千个 rank 几乎同时写入,形成 瞬时带宽洪峰。瓶颈是 聚合写带宽;写不完会拖住训练(同步 checkpoint 时训练暂停)。
举例:一个 70B 模型任务的 checkpoint(模型 + 优化器状态)≈ 70B × (2 + 8) B ≈ 700 GB。若要求 10 分钟内写完,需要聚合写带宽 ≥ 1.2 GB/s;若所有 rank 同时写共享存储,峰值需求可能再 ×10 以上。
21.2 常见存储方案对比
| 方案 | 类型 | 优点 | 局限 | 典型场景 |
|---|---|---|---|---|
| NFS | 通用网络文件系统 | 部署最简单、K8s 支持好(nfs-csi) | 单点带宽/IOPS 有限,元数据弱,海量小文件撑不住 | 代码、配置、小规模数据集、小集群 |
| Lustre | 并行文件系统 | 超高聚合带宽(TB/s 级),HPC 老牌方案 | 部署运维复杂,元数据节点(MDS)是瓶颈点 | 超算、大规模训练集群 |
| Ceph(CephFS/RBD) | 分布式统一存储 | 块/对象/文件一体,云原生生态好(Rook),弹性扩展 | 同规模下性能低于 Lustre,调优门槛高 | 通用云平台、训练+推理混合 |
| JuiceFS | 云原生文件系统 | 元数据引擎(Redis 等)+ 对象存储,弹性便宜,POSIX 兼容 | 依赖对象存储底座,极致性能不如专用并行 FS | 云上训练、数据湖场景 |
| Alluxio | 数据编排/缓存层 | 在 GPU 节点建分布式缓存,热数据本地读,屏蔽后端慢存储 | 本身不是持久存储,冷数据首次读仍慢 | 远端对象存储/OSS 加速、跨云数据供给 |
| 云厂商文件存储 | CPFS、EFS、文件存储 Turbo 等 | 免运维、与云 GPU 实例配套 | 贵、绑定厂商 | 云上按量训练 |
选型直觉:小集群/起步用 NFS + 节点本地 NVMe 缓存;自建大集群用 Lustre 或 CephFS 配 Alluxio/本地缓存加速;云上用云厂商并行文件系统(CPFS 类)或 JuiceFS。
K8s 接入:各方案均有 CSI 驱动(nfs-csi、ceph-csi、juicefs-csi 等),以 PV/PVC 方式挂进训练 Pod,与第四部分学过的存储体系一致。
21.3 GPUDirect Storage 简介
传统路径:存储 → 系统内存(bounce buffer,CPU 参与拷贝)→ GPU 显存。
GPUDirect Storage(GDS):NVMe/存储通过 DMA 直接把数据写入 GPU 显存,跳过 CPU 和内存拷贝——与 GPUDirect RDMA 思想一致,只是方向从"网卡↔显存"变成"存储↔显存"。
- 收益:降低数据加载延迟、释放 CPU、提升 IO 带宽利用率
- 现状:需要
nvidia-fs内核模块 + 支持的文件系统(本地 NVMe、部分并行文件系统)+ cuFile API;PyTorch 生态多通过 DALI 等库间接使用 - 运维认知:GDS 是"锦上添花"的高级优化,先把基础 IO 路径(缓存、并发度)调好,再考虑 GDS
bash
lsmod | grep nvidia_fs # 检查 GDS 模块;CUDA 自带 gdsio 工具可做读写压测21.4 运维要点
1)监控存储带宽与 IOPS
bash
iostat -x 2 # 节点视角:看 %util、rkB/s、await
ceph df; ceph osd perf # 分布式存储视角(以 Ceph 为例)
# Prometheus:对接 node_exporter / Ceph mgr exporter / 存储厂商 exporter,
# 做"训练任务带宽 vs 存储水位"大盘关键告警:聚合读带宽接近上限、元数据 ops 暴涨、单 OSD/存储节点热点。
2)Checkpoint 写入风暴防护
- 错开写入:调度上让不同任务/不同 rank 错峰保存(部分框架支持异步 checkpoint、分层 checkpoint)
- 先写本地再上传:checkpoint 先写节点本地 NVMe,后台异步同步到共享存储(torch distributed checkpoint、各大厂训练平台均支持)
- 限流:存储侧 QoS 限制单任务写带宽,避免一个任务的 checkpoint 打挂全集群存储
- 保留策略:自动清理旧 checkpoint,防止撑爆容量(运维最常见的事故之一!)
3)数据集预热与本地缓存
bash
rsync -av /mnt/nfs/dataset/ /local/nvme/dataset/ # 训练前把数据集同步到节点本地 NVMe(预热)
# 或在 Pod 中用 initContainer 预热,训练容器读本地路径稳定的大数据集直接下发到各节点 NVMe 一劳永逸;动态/超大数据集用 Alluxio/JuiceFS 客户端缓存(首次读拉取、后续读命中本地)。注意数据集版本管理与一致性——缓存了旧数据是隐蔽的训练事故。
21.5 "GPU 空转等数据"的识别
现象:nvidia-smi 里 GPU 利用率在 0~100% 之间周期性剧烈抖动,呈"锯齿状"——算一阵(有数据)→ 停一阵(等数据)。
确认方法:
bash
# 1. DCGM 看秒级细粒度利用率(第六部分深入)
dcgmi dmon -e 1002,1003,1004
# 2. nsys 剖析:看 GPU kernel 之间是否有大段空隙(空隙 = 等数据/等通信)
nsys profile -o profile python train.py
nsys stats profile.nsys-rep | grep -A20 "GPU Kernel"
# 3. 旁证:dataloader 进程 CPU 打满、存储 IO 打满,而 GPU 闲着调优思路(按优先级):
- 调大 dataloader 并发:
DataLoader(num_workers=N, pin_memory=True, prefetch_factor=4, persistent_workers=True);num_workers 经验值每 GPU 4~8 个,但别超过 CPU 核数 - 确认 shm 足够:K8s 默认
/dev/shm只有 64MB,dataloader 多进程会崩或退化,必须挂emptyDir: {medium: Memory}(见 20.4) - 数据前移:预热到本地 NVMe / 开缓存(见 21.4)
- 优化数据本身:小文件打包成 TFRecord/WebDataset/LMDB 等大顺序文件,元数据压力骤降
- CPU 预处理卸载:解码、增强用 DALI 搬到 GPU 上,或检查单条数据预处理是否过重
排查口诀:GPU 利用率抖动 → 看 dataloader workers 是否打满 CPU → 看存储 IO 是否打满 → 看 shm 配置 → 依次用"加 worker、上缓存、打包数据"解决。
本章小结
- 训练 IO 两副面孔:海量小文件随机读(拼 IOPS/元数据)、checkpoint 大文件突发写(拼聚合带宽)。
- NFS 简单但弱,Lustre/Ceph 撑起大规模,JuiceFS/云文件存储适合云上,Alluxio 做缓存加速;都通过 CSI 接入 K8s。
- GPUDirect Storage 让数据直达显存,属于高级优化。
- 运维三件事:监控带宽/IOPS 水位、给 checkpoint 错峰限流防风暴、数据集预热到本地缓存。
- "GPU 空转等数据"的识别:利用率锯齿抖动 + nsys 时间线空隙;治理顺序:worker 并发 → shm → 缓存 → 数据打包。
动手实验
- 写一个简单 PyTorch 训练脚本,
num_workers=0跑 1 分钟,用nvidia-smi dmon记录利用率;再把 num_workers 调到 4 并加persistent_workers=True,对比利用率曲线。 - 用
fio对共享存储挂载点分别测试 4K 随机读 IOPS 和 1M 顺序写带宽,判断它更适合放数据集还是 checkpoint。 - 思考题:为什么"把数百万张小图片打包成 WebDataset tar 文件"能同时缓解元数据压力和提升读取吞吐?
本部分小结
- 第 18 章:RDMA 用内核旁路和零拷贝解决 TCP/IP 的带宽与延迟瓶颈;IB 省心但贵,RoCE 便宜但要管好 PFC/ECN;
ibstat/perfquery/ib_write_bw是运维三板斧。 - 第 19 章:NCCL 自动组合 NVLink + GPUDirect RDMA 完成集合通信;
NCCL_DEBUG=INFO是排障起点,nccl-tests 的 busbw 是网络验收标尺。 - 第 20 章:K8s 中用 Multus 挂 RDMA 网卡、SR-IOV 做独占分配,配合 Gang Scheduling 拉起多机训练;建立了"通信时间 vs 计算时间"的带宽估算直觉。
- 第 21 章:存储要同时扛住"小文件随机读"和"checkpoint 突发写";识别"GPU 等数据"看利用率抖动,治理靠缓存、预热和 dataloader 调优。
至此,你已经掌握了从单机 GPU 到多机高速互联集群的完整链路。下一部分将进入 GPU 监控与可观测性,学习用 DCGM + Prometheus + Grafana 把这个复杂系统"看得清清楚楚"。