路线图

02 大规模训练稳定性保障

星辉 2026-07-02 阅读 6 min 1,155 字 路线图
02 大规模训练稳定性保障 封面

学习目标:能保障千卡级分布式训练的稳定运行——理解「掉卡即中断」的同步训练特性;掌握「故障检测 → 中断 → checkpoint 恢复 → 重调度」的自动恢复闭环;会用 NCCL_DEBUG 等手段排查训练 hang/超时;能识别并剔除慢节点(straggler);理解训练任务为何必须 Gang Scheduling;建立训练可观测(loss/吞吐/MFU/通信占比)。 重点度:必会(运维核心) 视角声明:本章是 AI Infra 运维视角——怎么让训练在硬件故障下自动恢复、怎么发现慢节点、怎么排查 hang。不涉及训练框架开发、通信库实现、并行策略算法、optimizer/loss 设计。


概述#

大模型训练和推理服务在运维上有本质区别:推理是无状态、可随时扩缩的;训练是有状态、长时间运行、且高度耦合的。 一个千卡训练任务跑几周,中间任何一张卡出问题,整个任务都会中断。

规模带来的残酷数学(业界实测数据):

text
· 单节点 1.5% 的日故障率,扩到 1000 卡 → 系统日故障率高达 84.8%
· 10 万卡集群 → 平均每 18 分钟就有一次故障
· Llama 3 预训练 54 天窗口 → 466 次任务中断(其中 47 次是计划内维护)
· 千卡以上训练:平均每周 3-7 次 GPU 故障,掉卡就中断训练

结论:在千卡规模,故障不是「会不会」而是「一天几次」。 运维不能指望硬件不坏,只能让训练「坏了能自动爬起来」。

核心问题:如何让长时间、跨千卡、强耦合的训练任务,在频繁的硬件故障下,尽量少丢进度、尽量自动恢复、尽量不被慢节点拖垮。

能力矩阵:

text
中断恢复闭环(检测 → 中断 → checkpoint 恢复 → 重调度,尽量自动)
慢节点检测(straggler:一张慢卡拖垮整个 all-reduce)
NCCL 故障排查(hang / 超时 / 走错网卡)
运维调度(排队 / 优先级 / 抢占 / Gang Scheduling)
训练可观测(loss / 吞吐 / MFU / GPU 利用率波动 / 通信占比)

同步训练的木桶效应:为什么一张卡能拖垮全部#

先建立最重要的认知模型。主流大模型训练是同步训练(synchronous):每个 step 结束,所有 GPU 要做一次 all-reduce 同步梯度,必须等最慢的那张卡到齐才能进入下一步

text
step N:  GPU0 ─┐
         GPU1 ─┤
         GPU2 ─┼─▶ all-reduce(同步屏障,等所有卡到齐)─▶ step N+1
         ...   │
         GPU999─┘
        任何一张卡慢/挂/网络卡 → 全部 999 张卡在这里干等

两个直接后果,构成本章两条主线:

  1. 一张卡挂了 → 整个任务卡死或崩溃(fail-stop)→ 需要「中断恢复闭环」。
  2. 一张卡慢了(还活着)→ 整个任务被拖慢(fail-slow / straggler)→ 需要「慢节点检测」。

fail-slow 比 fail-stop 更阴险:卡没报错、没有错误码,只是慢,监控上不告警,但整个千卡任务的吞吐被它一张卡按在地上。实测:生产 LLM 训练集群中 42.5% 的任务受 straggler 影响,浪费约 10.4% 的资源;512-1024 卡的任务里 59% 遇到过 fail-slow,平均拖慢 34.59%。


训练中断与恢复闭环#

运维目标:掉卡不是让人半夜重启,而是系统自动从 checkpoint 爬起来。

text
┌────────┐  ┌────────┐  ┌──────────────┐  ┌────────┐  ┌────────┐
│ 故障检测│─▶│ 任务中断│─▶│ checkpoint 恢复│─▶│ 重调度  │─▶│ 继续训练│
│Xid/hang│  │全局失败 │  │ 回到最近存档   │  │健康卡   │  │        │
└────────┘  └────────┘  └──────────────┘  └────────┘  └────────┘

1. 故障检测#

  • 硬件侧:DCGM health / Xid 错误(掉卡 Xid 79、ECC Xid 48)——见专题 01。
  • 任务侧:NCCL 超时、进程退出、心跳丢失、某 rank 无响应。
  • 训练指标侧:loss 突然 NaN/Inf、吞吐掉零、某 step 卡住不动。

2. checkpoint 是恢复的地基(运维视角)#

运维不写 checkpoint 逻辑,但要保障它存得下、存得快、读得回

text
运维关注点:
· 存储位置:checkpoint 落在哪?(共享存储 NFS/Lustre/对象存储)
· 存储带宽:千卡同时写 checkpoint 会打爆存储带宽 → 分片/异步写
· 存储频率:存太密 → IO 压力大、拖慢训练;存太疏 → 崩了丢进度多
· 恢复可用性:崩溃时最近的 checkpoint 必须完整可读(写坏的半个 checkpoint 是灾难)
· 容量治理:checkpoint 动辄 TB 级,要定期清理旧的,别把存储写满

checkpoint 频率的运维权衡:

text
恢复代价 = checkpoint 间隔时间 × 故障频率
存储代价 = checkpoint 频率 × 单次大小 × 存储带宽占用

千卡任务经验值:每 15-60 分钟一次 full checkpoint;
配合异步/分层 checkpoint(先写本地 NVMe 再后台刷共享存储)降低训练停顿。

运维排查点:训练每次到 checkpoint 时刻就卡顿明显 → 存储带宽瓶颈,查 NFS/Lustre 是否被打满;恢复失败 → 查最近 checkpoint 文件是否完整(大小、校验)。

3. 重调度与自动恢复#

传统做法(人肉):报警 → 人登录 → 找坏节点 → cordon/drain → 手动重提任务。千卡集群一天几次,人扛不住。

自动恢复做法(运维要推动落地的):

  • 弹性训练框架(PyTorch torchrun / TorchElastic):支持 worker 故障后重组、从 checkpoint 重启,无需人工。
  • 训练作业控制器(K8s 上的 training-operator / Kubeflow):检测到 Pod 失败自动重建,配合 Gang Scheduling 整组重拉。
  • 自愈系统联动(NVSentinel 等,见专题 01):坏节点自动 cordon/drain,调度器把任务重调度到健康节点。
bash
# TorchElastic 弹性启动(节点故障后自动重组,从 checkpoint 续训)
torchrun \
  --nnodes=1:128 \                # 允许节点数在区间内弹性变化
  --nproc-per-node=8 \
  --max-restarts=3 \              # 故障最多自动重启 3 次
  --rdzv-backend=c10d \
  --rdzv-endpoint=$MASTER:29400 \
  train.py

自动恢复闭环的运维验收标准:一张卡挂了,无人干预,任务能在 X 分钟内从最近 checkpoint 在健康节点上继续,而不是等到早上有人发现。


慢节点 / Straggler 检测#

fail-slow 是运维最难抓的问题——卡还活着、不报错,就是慢。

慢从哪来:

text
· 慢卡:某张 GPU 降频(thermal throttle)、ECC 频繁纠错、隐性硬件劣化
· 慢网络:某一跳交换机/网卡/光模块质量差,某条 IB 链路误码率高
· 慢存储:某节点本地盘/数据加载慢,喂不上数据
· 数据倾斜:某 rank 分到的数据/序列更长(更偏框架,运维少见)

怎么发现(运维手段):

text
1. per-rank 吞吐对比:正常所有 rank 吞吐接近,某 rank 明显低 → 嫌疑
2. 通信等待时间:某 rank 在 all-reduce 上等待时间异常长(它在等别人 = 别人慢)
   反过来,某 rank 让别人等 = 它自己慢
3. GPU 降频信号:nvidia-smi throttle reason / DCGM SM 时钟
4. 网络侧:交换机端口错包计数、IB 链路 symbol error、PCIe replay counter
bash
# 快速对比全集群各卡的时钟/温度/降频,揪出被 throttle 的慢卡
nvidia-smi --query-gpu=index,clocks.sm,temperature.gpu,clocks_throttle_reasons.active \
  --format=csv

# DCGM 采 per-GPU SM 活跃度和时钟,对比找离群卡
dcgmi dmon -e 100,101,155,203   # sm_clock, mem_clock, power, temp

# 检查 IB 链路误码(straggler 常见根因之一)
ibqueryerrors           # 查全网 IB 端口错误计数
mlnx_perf -i mlx5_0     # 单网卡性能计数

剔除慢节点的运维动作:

text
1. 定位到具体慢卡/慢链路
2. 该节点 cordon + 打标签
3. 下一次 checkpoint 恢复时把任务重调度到健康节点(避开慢节点)
4. 慢卡:dcgmi diag -r 3 压测确认;慢网络:换光模块/网卡/查交换机端口

运维要点:straggler 不告警,所以必须主动建 per-rank 吞吐/通信等待的对比面板,靠「离群检测」而不是靠阈值告警。成熟系统(如各家自研的 fail-slow 检测)会自动标出拖后腿的 rank。


NCCL 卡住 / 超时排查(运维高频)#

NCCL(NVIDIA Collective Communications Library)是多卡通信的底座,训练 hang 有很大比例是 NCCL 层的问题。这是运维最高频的排查场景之一。

头号元凶:走错网卡#

多机训练 NCCL 超时的 #1 原因:NCCL 自动选网卡时选错了——本该走高速 IB/RoCE,结果走了慢的管理网(1G 以太网),或者选了一个 UP 但实际不通的网卡,导致 init 失败或直接 hang。

bash
# 排查第一步:打开 NCCL 详细日志,看它到底选了哪块网卡
export NCCL_DEBUG=INFO
export NCCL_DEBUG_SUBSYS=INIT,NET
# 日志里搜 "NET/IB" 和 "Using" 确认走的是 IB 还是 socket

显式指定网卡(生产必配,别让 NCCL 瞎猜):

bash
# 以太网/RoCE:指定走哪块网卡(前缀匹配,^ 排除)
export NCCL_SOCKET_IFNAME=eth0        # 只用 eth0
export NCCL_SOCKET_IFNAME=^docker,lo  # 排除 docker/lo 这类假网卡

# InfiniBand:指定用哪块 HCA(RDMA 网卡)
export NCCL_IB_HCA=mlx5_0,mlx5_1

# 启用 IB(0=启用,默认值;=1 则彻底禁用 IB 只走 socket——多机会灾难性慢)
export NCCL_IB_DISABLE=0

超时参数#

bash
# 集合通信超时在 PyTorch 层设(NCCL 本身没有 NCCL_TIMEOUT 这个环境变量,设了也不生效)
# init_process_group(backend="nccl", timeout=timedelta(minutes=30))
export TORCH_NCCL_BLOCKING_WAIT=1   # 让上面的 timeout 真正阻塞生效(默认是异步 watchdog)
                                    # 超时太短会误杀慢节点,太长故障发现慢

# InfiniBand Verbs 超时:实际等待 = 4.096µs × 2^NCCL_IB_TIMEOUT × NCCL_IB_RETRY_CNT
export NCCL_IB_TIMEOUT=22  # 大网络适当调大

排查流程#

text
现象:多机训练卡在启动 / 某 step 后 hang 住不动

1. NCCL_DEBUG=INFO 看日志停在哪
   · 卡在 init(bootstrap/ring 建立)→ 网卡选择/连通性问题
   · 卡在某次 all-reduce → 某 rank 挂了或某跳网络断了

2. 确认走对网卡:日志里是不是走了 IB?还是掉回了 socket?
   → 掉回 socket:配 NCCL_IB_HCA / NCCL_SOCKET_IFNAME 强制走 IB

3. 定位是哪个 rank 卡住:
   · 看哪个 rank 的进程还活着但不推进
   · 结合 Xid 日志:是不是那台机器掉卡了(Xid 79)

4. 网络连通性验证:
   · 节点间 IB 连通:ib_write_bw(点对点 IB 带宽测试)
   · 全集群通信基准:nccl-tests 的 all_reduce_perf

5. 分层定位(见实战):GPU 故障?NCCL/网络?慢节点?存储?
bash
# 用 nccl-tests 做集群通信健康基准(排除业务代码干扰)
# 8 卡单机
./build/all_reduce_perf -b 8 -e 256M -f 2 -g 8
# 多机(配合 mpirun/torchrun):对比实测带宽 vs 理论带宽,低很多说明有慢链路

训练任务的运维调度#

千卡训练对调度器有特殊要求,运维要理解这些机制的用途和运维含义(不涉及调度器内部实现)。

为什么必须 Gang Scheduling(成组调度)#

分布式训练要求所有 pod 要么一起起、要么都不起(all-or-nothing):

text
没有 Gang Scheduling:8 卡任务,只调度上 7 个 pod,第 8 个 pending
  → 7 张昂贵的 GPU 空转干等第 8 个,永远等不来(资源被占着又没产出)
  → 极端情况:多个任务互相卡着对方最后一张卡 → 死锁,谁都跑不起来

有 Gang Scheduling:8 个 pod 凑齐才一起调度,凑不齐就都不占资源
  → 避免「占着一半资源干等」的浪费

这就是训练任务必须用 Gang Scheduling 的原因,也是普通 K8s 默认调度器(逐个调度 pod)不适合训练的根本原因。

排队、配额、优先级、抢占#

能力作用运维含义
Gang Scheduling整组一起调度训练任务必备,避免半拉子占用
队列 / 配额各团队按配额排队防止一个团队占满集群
优先级 / 抢占高优任务抢占低优高优训练可整组驱逐低优任务(保持 gang 语义)
拓扑感知尽量把任务的卡排在同交换机/同机架减少跨交换机通信,训练更快

主流调度器(运维选型认知,不写实现):

  • Volcano:CNCF 项目,提供 Gang Scheduling、fair-share、抢占,训练场景最常用之一。
  • Kueue:K8s 原生作业队列,集群级队列、租户配额、cohort 借用、原子准入(atomic admission,天然契合 gang)。
  • KAI Scheduler:NVIDIA 2025 开源(Apache 2.0),额外支持分数 GPU 分配、拓扑感知、分层队列。

抢占 + gang 的效果:高优训练来了,整组驱逐低优任务腾出资源;实测能把集群利用率从 ~60% 提到 ~85%。

运维要点:抢占对被抢的低优训练是「被中断」——所以所有可被抢占的训练都必须支持 checkpoint 恢复,否则抢占 = 白跑。抢占策略和 checkpoint 策略要配套。


训练可观测#

训练是长跑,可观测的目标是尽早发现「跑歪了」或「跑慢了」,而不是等几天后 loss 崩了才发现。

训练指标(运维要盯的几条线)#

指标含义异常信号
loss训练损失NaN/Inf → 立即告警(可能坏卡/数值问题);不降/反弹 → 通知算法
吞吐(tokens/s、samples/s)训练速度突然下降 → 有慢节点或通信问题
MFU(Model FLOPs Utilization)实际算力利用率 vs 理论峰值偏低(如 < 30%)→ 通信/数据/降频瓶颈
每 step 耗时单步时间某步突然变长/卡住 → straggler 或 hang
GPU 利用率波动各卡 util周期性掉零 → 数据加载/通信占比高
通信占比all-reduce/通信时间 / 总时间占比过高 → 网络瓶颈或并行策略问题
text
MFU 是训练效率的「体检总分」:
  MFU = 实测吞吐对应的 FLOPs / GPU 理论峰值 FLOPs
  MFU 低 → 卡没喂饱,钱在烧但没换算出训练进度
  运维排查方向:通信占比高?数据加载慢?某些卡降频?batch/并行配置?

采集与告警#

text
· GPU 侧:DCGM Exporter → Prometheus(SM 活跃度、显存、温度、NVLink、Xid)
· 训练侧:框架指标(loss/吞吐/MFU/step time)→ Prometheus / TensorBoard / W&B
· 通信侧:per-rank 通信等待时间、nccl-tests 基准带宽
· 关键告警:
    - loss = NaN/Inf → critical(可能坏卡)
    - 吞吐较基线跌 > 20% 持续 N 分钟 → 查 straggler
    - 某 step 超时(> 正常 step 时间 × K)→ 疑似 hang
    - GPU util 集体掉零但进程还在 → 疑似通信 hang / checkpoint 卡住

实战要点#

实战 1:千卡训练跑着突然 hang 住,怎么排查#

hang 住时最重要的是分层定位,按「最可能且最好查」的顺序排:

text
┌─ ① GPU 故障(最常见,先查)
│    dmesg -T | grep -i xid   → 有 Xid 79 掉卡 / Xid 48 ECC?
│    nvidia-smi 各节点是不是有的只剩 7 张卡?
│    → 有 → 走专题 01 隔离换卡,任务从 checkpoint 恢复
├─ ② NCCL / 网络
│    NCCL_DEBUG=INFO 日志停在哪个集合操作?
│    是不是掉回 socket 走了管理网?
│    ib_write_bw / nccl-tests 验证节点间 IB 通不通、带宽正不正常
│    → 修网卡配置 / 换光模块 / 查交换机端口
├─ ③ 慢节点(没挂但拖死)
│    per-rank 吞吐/step 时间对比,找离群 rank
│    该 rank 的卡是不是降频了(throttle)?IB 链路是不是误码高?
│    → cordon 慢节点,恢复时避开
└─ ④ 存储
     是不是卡在 checkpoint 读写?NFS/Lustre 带宽被打满?
     数据加载 pipeline 是不是某节点本地盘慢?
     → 查存储带宽、数据加载 worker

排查口诀:先看 Xid(掉卡)→ 再看 NCCL 日志(走对网卡没)→ 再对比 per-rank 吞吐(慢节点)→ 最后查存储。 90% 的 hang 在前两步能定位。

实战 2:一张卡挂了,怎么让训练自动恢复(而不是人肉重启)#

text
目标闭环(无人干预):
1. DCGM/NVSentinel 检测到 Xid 79 掉卡
2. 自愈系统自动 cordon + drain 坏节点
3. TorchElastic / training-operator 检测到 worker 失败,触发重组
4. Gang Scheduler 在健康节点上重新整组拉起(避开坏节点)
5. 任务从最近 checkpoint 加载,续训
6. 全程告警通知运维,但不需要运维手动操作

落地检查清单:
□ 训练用了弹性框架(torchrun --max-restarts / TorchElastic)
□ checkpoint 频率合理(15-60min),且写在共享存储、可靠可读
□ 调度器支持 Gang Scheduling(Volcano/Kueue/KAI)
□ 有自愈系统自动隔离坏节点(NVSentinel 或自研)
□ 集群有 buffer 健康节点可供重调度(不能满负荷零余量)
□ 恢复后有告警 + per-rank 吞吐验证(确认没恢复到另一张慢卡上)

小结#

大规模训练稳定性保障的核心能力:

text
认知基础    同步训练木桶效应:一张卡挂→全崩(fail-stop);一张卡慢→全拖慢(fail-slow)
中断恢复    检测 → 中断 → checkpoint 恢复 → 重调度,追求「无人干预自动爬起来」
慢节点      主动对比 per-rank 吞吐/通信等待,靠离群检测揪出 straggler(不告警)
NCCL 排查   NCCL_DEBUG=INFO 看走对网卡没;走错管理网是 #1 元凶;nccl-tests 做基准
运维调度    Gang Scheduling 是训练必备;抢占的前提是能 checkpoint 恢复
可观测      loss/吞吐/MFU/step 时间/通信占比,尽早发现「跑歪或跑慢」

2026 年的关键认知:

  • 千卡规模故障是日常(一周 3-7 次),运维目标不是「不坏」而是「坏了自动恢复」。
  • fail-slow 比 fail-stop 更难缠:不报错、不告警,靠主动 per-rank 对比才能抓出来。
  • checkpoint 是一切自动恢复的地基:运维保障它存得快、读得回、不写满存储。
  • NCCL 走错网卡是多机训练超时的头号原因:生产必须显式配 NCCL_SOCKET_IFNAME/NCCL_IB_HCA,别让它瞎猜。
  • Gang Scheduling 不是可选项:没有它,训练会「占一半资源干等」甚至死锁。
  • 自动恢复闭环(弹性框架 + Gang 调度 + 自愈系统 + checkpoint)是千卡训练稳定性的完整拼图,缺一块都得靠人肉半夜救火。

至此,专题 01(GPU 集群故障管理)+ 专题 02(训练稳定性)构成了 AI Infra 运维在「训练/集群侧」的稳定性保障全景:01 管好每一张卡的健康与故障闭环,02 管好这些卡组成的千卡训练任务在故障下的自动恢复。