《从裸机到推理服务》· 03 / 12
不用交换机权限,一条命令造出丢包,看 16 张卡怎么从「飞快」变成集体卡死。
预计 1 小时 | 前置:两台各 8 卡、装了 NCCL 与 nccl-tests(第 2 篇那两台);没有 8 卡也能做,降级方案在文内
给 .12 的网卡加 0.1% 丢包,只加 0.1%。
再跑一次集合通信:链路速率满格,GPU 一张没坏,16 张卡卡死了。
参数面怕的不是慢,是丢。这一篇把这件事演出来,再看「无损网络」靠什么把丢包摁住。
这一篇的产出
一条造丢包的命令(和用完删掉的习惯)、一张「丢包率 → busbw」对照表,以及一句能说出口的判断:参数面的事故,先查丢包,再查带宽。
一、崩的那一步,卡在等什么
先把那一秒拆开。三台各放一份完整模型,24 张卡分到的数据各不相同(全局 batch 切成 24 份),各自前向、各自反向,算出 24 份梯度;一步结束时把这 24 份加起来求平均,每台拿同一份平均值,各更新各的参数。这叫数据并行,绝大多数集群的默认做法。
所以三台都在算,没有主从。rank0(全局 0 号进程)只多负责打印日志、存 checkpoint,计算量不比别人多一个 FLOP。
一步的顺序只有四段:读数据 → 前向算 loss、反向算梯度 → 汇总梯度 → 各自更新。只有第三段跨机器,前后三段都在本机内完成。
↳ AllReduce:汇总梯度用的那套操作,把各卡算出的梯度加成一个结果,人人都拿到一份。
跨机器传的为什么只有梯度,两层原因:
- 数据不流动:每台读自己那份,读多少由全局 batch 除以机器数定,跨机交换数据没有意义;
- 参数不必流动:三台本来就一样,需要对齐的是「各台算出来不一致」的梯度。
口语里常说「同步参数」,传的其实是梯度。两者形状相同、含义不同:参数是模型本身,梯度是这一步该往哪改。参数只在训练开始时从 rank0 广播一次,之后靠梯度对齐。
于是三张网的分工就定死了:
| 流的是什么 | 走哪张网 | 频率 |
|---|---|---|
| 梯度(AllReduce) | 参数面 | 每个 step,最频繁 |
| 训练数据、checkpoint 落盘 | 存储面 | 每 step 读、每 N 步写 |
| 日志、SSH、监控 | 管理面 | 随时,量极小 |
↳ checkpoint:训练中途把参数和优化器状态存成文件,崩了能接着跑。动辄几百 GB,走存储面。
回到开头那一秒:四段里唯一跨机的那一段,就是 16 张卡必须同时站住的时刻。前面算得再快,这一段慢,全都白等。
二、为什么 0.1% 就够 16 张卡全体停下
AllReduce 的规矩:一步里所有 rank 到齐,缺一个,其余的干等。Ring 实现里每人只和左右邻居收发,环上一环慢,整条环慢。
第二条是数学:走内核协议栈(TCP)的链路上,吞吐和丢包率大致是 吞吐 ∝ 1/√丢包率。丢包涨十倍,吞吐掉三倍多;从 0 涨到 0.1%,对每秒发几十万个包的集合通信来说,注定有包中招。
第三条最容易被忽略:RDMA 丢包比 TCP 更贵。IB 链路靠 credit 收发,本来就丢不了;RoCE 丢了包,传输层按 Go-Back-N 重传------从丢的那个包起,后面整段全部重发。TCP 至少只补丢的,RDMA 连好的一起重来。
所以三张网里只有参数面必须是「无损网络」:
↳ PFC :交换机对「不许丢」的那一档流量,队列快满时先喊停发送端。不丢包,代价是对端整口停发,调不好会连累别的流量。
↳ ECN :交换机在队列将满时给包打标记,发送端见标记就降速。不喊停,靠提前减速绕开丢包。
↳ 无损网络:靠 PFC、ECN 这些机制,把「不丢包」从运气变成配置保证。参数面的前提。
一个粗短的比喻:PFC 是路口「先停再走」,ECN 是「看见前方排队就松油门」,都是为了让包落不到「丢了再补」那一步。
顺带说清:加机器解决的是时间,不是容量。 单机卡在哪,决定了该不该加机器:
| 单机卡在哪 | 数据并行管用吗 | 该用什么 |
|---|---|---|
| 算力不够,跑得太慢 | 管用 | 数据并行,8→16 卡典型加速 1.7---1.85 倍 |
| 显存装不下模型或优化器状态 | 不管用 | 切模型:张量并行、流水并行,或 ZeRO / FSDP 分片 |
| 装不下大 batch | 不用多机 | 梯度累积,攒几步再更新一次 |
| 一台盘装不下训练数据 | 与并行方式无关 | 共享存储,第 08 篇 |
数据并行的前提是「一台机器装得下整个模型」------它买的是时间,不是容量。装不下时,模型必须被切开,参数就不再每台一份:
↳ ZeRO / FSDP:把参数、梯度、优化器状态按卡数切开,每张卡只存 1/N,需要时向别人取。显存降下来了,代价是通信从「每步一次梯度平均」变成「每层都要取参数」。
那时参数面吃的不只是梯度,还有来回复制的参数分片,流量比这一篇的场景大一个量级。本篇讲的是放得下、要更快的那一类,也是绝大多数集群的日常。
三、把丢包造出来
实验只在 .11、.12 两台上做,16 卡,道理相同、规模减半。
第一步:在真参数面上拿基线
nccl-tests 的 all_reduce_perf 跑一次 Ring AllReduce,.11 和 .12 各 8 卡:
bash
mpirun -np 2 -H hostfile ./all_reduce_perf -b 512M -e 512M -g 8
# size count type redop root time algbw busbw
# (B) (elements) (us) (GB/s) (GB/s)
536870912 134217728 float sum -1 5284.1 96.9 181.6
看 busbw,不看 time:它是链路上实际搬动的速率,181.6 GB/s 对两台各 8×200G 的聚合线速是 90%------和第 2 篇测到的比例对上了。hostfile 两行,.11、.12 各一行。
第二步:把通信指到能被拦住的路上
坏消息先说:tc netem 作用在内核的发送队列上,拦不住 RDMA 流量------参数面的数据从用户态直通网卡,绕过内核。想造丢包,先让 NCCL 走一遍内核协议栈:
bash
export NCCL_IB_DISABLE=1 # 不走 IB
export NCCL_SOCKET_IFNAME=eno2 # 走存储面那张以太网卡
mpirun -np 2 -H hostfile ./all_reduce_perf -b 512M -e 512M -g 8
536870912 134217728 float sum -1 89123.4 6.0 11.3
同一个测试,busbw 从 181.6 掉到 11.3 GB/s------这是普通以太网(100G)跑集合通信的真实水平。拿它当基线,丢包的效果才量得出来。
第三步:加丢包,一次只加一档
↳ tc netem:内核自带的网络模拟工具,不用交换机权限,就能给指定网卡造丢包、延迟、抖动。
bash
# 在 .12 上执行
sudo tc qdisc add dev eno2 root netem loss 0.1%
tc qdisc show dev eno2
qdisc netem 8001: root refcnt 2 limit 1000 loss 0.1%
loss 0.1% 已生效:每 1000 个包丢 1 个。netem 只管这张卡的出方向,双向都丢得在两台各加一条。逐档重跑第一步的命令,每档记一行:
| 丢包率 | algbw (GB/s) | busbw (GB/s) | 现象 |
|---|---|---|---|
| 0(TCP 基线) | 6.0 | 11.3 | 正常 |
| 0.01% | 2.3 | 4.3 | 明显变慢 |
| 0.1% | 0.4 | 0.8 | 慢到没法用 |
| 1% | --- | --- | 600 秒后被 watchdog 收掉 |
丢包率十倍十倍地涨,吞吐三到五倍地掉,对得上 1/√丢包率。0.1% 看着小,一次 AllReduce 要发几十万个包,注定有包中招;一个包中招,整条环陪着重传。
第四步:看它怎么死
丢包加到 1%,带上 NCCL 日志:
bash
NCCL_DEBUG=INFO mpirun -np 2 -H hostfile ./all_reduce_perf -b 512M -e 512M -g 8
node12:3821:4103 [3] NCCL WARN Complete callback timeout for op 7 after 600 seconds
node12:3821:4103 [3] NCCL INFO aborting: watchdog timeout
600 秒是 watchdog 的默认时长。死法值得看清:不是报错退出,是先卡死------16 个 rank 全部挂在同一个集合操作上等重传,GPU 利用率归零、显存不释放、日志一行不发,600 秒后才被看门狗收割。生产环境里半夜告警的「训练 hang 住」,多数长这样。
第五步:删掉它,再确认删干净
bash
sudo tc qdisc del dev eno2 root
tc qdisc show dev eno2
qdisc noqueue 0: root refcnt 2
回到 noqueue 才算恢复。这一步不是仪式:不删,.12 之后所有出方向流量都在静悄悄丢包,包括明天别人的实验。
只有一台机器怎么办
单机跑
all_reduce_perf只走 NVLink,碰不到网卡,整套实验做不了。退一步用回环口:
$ sudo tc qdisc add dev lo root netem loss 0.1%
$ iperf3 -c 127.0.0.1 -t 10
[ ID] Interval Transfer Bitrate Retr
[ 4] 0.00-10.00 sec 1.72 GBytes 1.48 Gbits/sec 2143 sender
回环口本来能跑到 20 Gb/s 量级,0.1% 丢包把它压到 1.5 Gb/s,重传两千多次------TCP 世界里丢包的下场和参数面同源,只是看不到「卡死」:那是 16 张卡互相等的集体行为。用完同样删掉。
四、先看「丢没丢」,再看「跑多快」
真实故障里没人帮着加 netem,丢包是自然发生的:光模块劣化、PFC 配错、交换机缓存溢出。查法和实验相反------先看计数器,再跑带宽:
bash
sudo ethtool -S mlx5_0 | grep -iE "crc|out_of_seq|resend"
rx_crc_errors: 0
packet_seq_resend: 47
out_of_sequence: 12
packet_seq_resend、out_of_sequence 不为零或在涨,说明传输层正在重传、遇到乱序,这就是「卡死」的源头;rx_crc_errors 有数,先换线换模块。和第 2 篇合起来才是完整的链路体检:速率对不对(第 2 篇)、丢没丢(这一篇),两样都干净,才轮到怀疑代码。
RoCE 环境还要多看一眼
PFC 和 ECN 有各自计数器:
ethtool -S eth2 | grep -iE "pfc|cnp"。PFC 帧暴涨是喊停喊得太频繁(配得过猛),CNP(ECN 标记包)暴涨是拥塞真实存在。这两个数字是网络工程师调无损网络的仪表盘。
处置:先分「线坏」还是「拥塞」,再决定找谁
三个计数器就能把方向分开,不必等别人:
rx_crc_errors在涨 → 物理层。换线、换模块、擦端面,这些自己能做;- CRC 不动,
packet_seq_resend、out_of_sequence在涨 → 拥塞或 PFC 反压,交换机侧的事; - 三项都干净,训练还是慢 → 不是丢包,回第 2 篇查速率和 PCIe。
| 现象 | 判断 | 动作 | 归谁 |
|---|---|---|---|
| CRC 涨 | 线或模块在劣化 | 换线换模块,记下更换前后的计数差 | 现场 |
| 重传涨、CRC 不动 | 拥塞、缓存溢出、PFC 反压 | 停掉同链路其他流量复测;查交换机端口利用率 | 网络 |
| 只有个别 rank 慢 | 那一条 rail 或那一张卡 | 换端口、换卡槽,把范围缩到单链路 | 自己 |
| 训练 hang、日志里是 watchdog | 已经到「卡死」这一档 | 先抓 dmesg、计数器、NCCL 日志,再重启 |
自己 |
| RoCE 环境 CNP 暴涨 | 拥塞真实存在 | 调 ECN 门限与 PFC,交换机侧动作 | 网络 |
两条纪律,比上面任何一步都容易踩:
- 重启会洗掉计数器。 抓完再重启。反过来做,第二天手上只剩一句「昨晚它卡了」。
- 调大
NCCL_IB_TIMEOUT不是修复。 它只是让程序多等一会儿,把「卡死」变成「更久的卡死」,现场看起来像是好了。
五、最容易卡住的五处
坑一:netem 加了,数字纹丝不动
参数面流量绕过内核,netem 拦不住,第一步就该想到。要么让 NCCL 走以太网(第二步的两个环境变量),要么在 RoCE 环境从交换机侧造拥塞。实验没效果,先查这一条,不是先怀疑工具。
坑二:NCCL 自己挑了别的网口
不指定 NCCL_SOCKET_IFNAME,NCCL 可能挑到 docker0 或回环口,甚至走本机共享内存------数据根本没出网卡,什么实验都测不出来。固定到实验网卡。另外 eno2 上还跑着 NFS,实验时段和别人的数据读写错开。
坑三:netem 用完不删
第五步的 del 加 show 是一套动作。漏了删,故障被留在了机器上;漏了 show,删没删成不知道。
坑四:把 busbw 当带宽报
busbw 含 Ring 通信的冗余传输,天然比 algbw 大。报数带口径:「algbw X,busbw Y(Ring 16 卡)」,否则和第 2 篇的链路带宽对不上,越对越糊涂。
坑五:不留基线
丢包 0 的那两行数字是整套实验的锚。删环境前把表存下来,和第 2 篇的三行表放在一起。
六、怎么算做完了
验收标准
① 一张四行表:丢包 0 / 0.01% / 0.1% / 1% 各自的 busbw 和现象;
② 一套能复现的命令:加丢包 → 跑测试 → 删丢包 → 确认恢复 noqueue;
③ 一句话结论:这条链路无丢包时 busbw X GB/s;丢包率每升十倍,吞吐掉三到五倍------参数面必须是无损网络,原因就在这张表里。
企业里这活归谁:PFC、ECN 由网络工程师在交换机上配置,交付验收时要看他们的无损属性测试报告。运维的职责是发现:看计数器(第四节)、认日志里的 watchdog(第五节),把「网络是不是有问题」回答成带数字的问题。
下一篇:.11 重启之后卡在自检。不装系统、不进 BIOS 也进得去------从 BMC 开始,把一台开不了机的机器救回来。
命令与判读方法整理自 NVIDIA NCCL 文档、nccl-tests 仓库、Linux tc/netem 手册与 RDMA 运维社区帖。输出示例为通用配置,数字区间为公开资料中的常见范围,实际以现场为准。如有错漏,欢迎指出。