主题
第七部分 GPU 故障诊断与运维实战
前面六部分我们学会了搭环境、管集群、做调度、搞监控。从本部分开始,我们进入 GPU 运维工程师最核心、也最值钱的能力:出事了怎么办。
GPU 服务器单价动辄几十万到几百万,一个 8 卡节点宕机一天,损失的不只是硬件折旧,更是排队等待的训练任务和算法团队的进度。本部分用"方法论 + 大量真实风格案例"的方式,带你从零建立故障诊断的肌肉记忆。
学习建议:每读完一个案例,合上文档,自己复述一遍"现象 → 排查 → 根因 → 解决 → 预防"五步法。运维能力的差距 = 见过的故障数量 × 复盘的深度。
第 25 章 故障诊断方法论
本章目标
- 建立 GPU 故障的分层模型,做到"看到现象就知道先怀疑哪一层"
- 掌握通用排查流程:dmesg/XID → nvidia-smi → dcgmi → 隔离验证 → 换机定位
- 熟记常见 XID 错误码的含义与处理建议;掌握标准处理动作(drain → 维修 → burn-in → 归队)
25.1 GPU 故障的分层模型
新手排障最常见的问题是"东一榔头西一棒子":看到训练 hang 了就去重启机器,看到 nvidia-smi 报错就去重装驱动,结果越搞越乱。正确的做法是自底向上逐层排查,先排除底层,再怀疑上层。
L6 应用层 训练脚本、NCCL、PyTorch ← 先怀疑代码?不,最后怀疑
L5 调度层 K8s device-plugin、配额、拓扑
L4 CUDA/容器层 CUDA Runtime、nvidia-container
L3 驱动层 NVIDIA 内核驱动、NVML
L2 固件层 GPU VBIOS、BMC、NVSwitch 固件
L1 硬件层 GPU、显存、NVLink、电源、散热 ← 永远从这里开始各层典型故障速查表:
| 层级 | 典型现象 | 首选排查命令 |
|---|---|---|
| L1 硬件 | 掉卡、XID 报错、过热、机器重启 | dmesg -T、nvidia-smi -q、IPMI |
| L2 固件 | 固件版本不一致、Fabric Manager 报错 | `nvidia-smi -q |
| L3 驱动 | NVML 初始化失败、驱动加载失败 | nvidia-smi、`lsmod |
| L4 CUDA/容器 | 容器内看不到卡、CUDA 版本报错 | nvidia-smi(容器内)、nvidia-container-cli |
| L5 调度 | Pod pending、allocatable 为 0 | kubectl describe node |
| L6 应用 | NCCL 超时、OOM、loss 异常 | 训练日志、nccl-tests |
逐层排查的核心思想:
- 下层故障会伪装成上层症状。 一张卡掉卡(L1),表现出来的可能是训练 NCCL 超时(L6 的症状)——只在应用层打转,永远找不到根因。
- 每次只验证一层,验证通过再往上走。 用
nvidia-smi确认 8 卡都在、无 XID,才把嫌疑移到驱动/CUDA。 - 能用最小复现就不要用大复现。 怀疑 NCCL 有问题,先跑
all_reduce_perf,而不是直接重跑整个训练任务。
25.2 通用排查流程
任何 GPU 节点故障,都按下面这条流水线走,形成条件反射:
第 1 步:看内核日志里的 XID 错误(GPU 驱动上报的错误码,排障的第一手信息)
bash
dmesg -T | grep -i xid
# [Wed Jun 12 03:14:22 2026] NVRM: Xid (PCI:0000:31:00.0): 79, GPU has fallen off the bus.第 2 步:看 nvidia-smi 整体状态
bash
nvidia-smi # 卡数对不对?温度、功耗、显存有没有异常?
nvidia-smi -q -d ECC,POWER,TEMPERATURE,CLOCK # 详细查询:ECC、降频原因等第 3 步:跑 DCGM 健康检查与诊断
bash
dcgmi health -g 0 -c # 快速健康检查
dcgmi diag -r 3 # 深度诊断(会压测占用 GPU,不要在生产业务卡上跑)第 4 步:隔离验证——把节点从集群里摘出来(drain),排除业务干扰后单独压测可疑的卡:
bash
CUDA_VISIBLE_DEVICES=3 ./gpu_burn 300第 5 步:换机定位(区分卡坏 vs 主板/电源问题)
这是硬件排障最关键的一步,业内叫"交叉验证":
| 现象 | 结论 |
|---|---|
| 可疑卡换到好机器,故障跟着卡走 | 卡坏了,走 RMA 报修 |
| 可疑卡换机正常,好卡插到故障机也报错 | 主板/PCIe 槽位/电源问题,报修整机 |
| 所有卡在该机器上随机出问题 | 优先怀疑电源功率不足、散热、主板 |
经验法则:没有交叉验证就报修 GPU,有 30% 概率被原厂退回说"卡没问题"。交叉验证一次只要半小时,能省下一周的扯皮。
25.3 XID 错误完全指南
XID 是什么? NVIDIA 内核驱动(NVRM)检测到 GPU 相关错误时,会打印一条带编号的内核日志,格式为 NVRM: Xid (PCI:...): <编号>, ...。它是 GPU 故障的"黑匣子录音",排障时第一个要看的就是它。
bash
dmesg -T | grep -i xid # 或 journalctl -k | grep -i xid
# 部署了 dcgm-exporter 时,XID 以指标 DCGM_FI_DEV_XID_ERRORS 暴露常见 XID 对照表(含义与处理建议整理自 NVIDIA 官方 XID 文档,实际处理请以 NVIDIA 官方 XID Errors 文档 为准):
| XID | 含义 | 严重度 | 处理建议 |
|---|---|---|---|
| 13 | 显存非法访问(Graphics Engine Exception) | 应用级 | 多为应用 bug(越界访问),收集应用日志,通常无需换卡 |
| 31 | FIFO/应用级错误(非法指令、mmu fault) | 应用级 | 多为应用 bug;若同一卡反复出现且换应用也复现,跑 diag |
| 43 | GPU stopped processing(掉卡前兆之一) | 高 | 某卡停止响应,drain 节点,跑 dcgmi diag,常需换卡 |
| 45 | 可恢复的 ECC/通道错误(Robust Channel) | 中 | 单次可观察;短期内反复出现需隔离验证 |
| 48 | 双比特 ECC 错误(DBE,不可纠正) | 高 | 检查 nvidia-smi -q -d ECC,DBE 累计达到阈值即换卡 |
| 61/62 | 内部微控制器错误/告警 | 中 | 观察;反复出现伴随掉卡则报修 |
| 63/64 | ECC 页面退役/行重映射事件(legacy/Ampere+) | 中 | 记录 remapped rows 数量,接近上限安排换卡 |
| 69 | 显存行重映射失败或类驱动错误 | 中 | 结合 remapped rows 判断,失败需换卡 |
| 74 | NVLink 错误 | 高 | 检查 nvidia-smi nvlink -e,错误持续增长需检查 NVSwitch/链路 |
| 79 | GPU has fallen off the bus(掉卡) | 极高 | 经典掉卡。查电源/散热/PCIe,交叉验证定位卡 or 主板 |
| 92 | 高速单比特 ECC 错误率过高 | 中 | 持续出现需关注,结合 diag 判断 |
| 94 | 受限/包含的 ECC 错误(Contained ECC) | 中 | 应用受影响的上下文被终止,重置后可恢复,记录频率 |
| 95 | 非受限/未包含的 ECC 错误(Uncontained) | 高 | 可能影响其他上下文,建议重置该卡并密切观察 |
| 119 | GSP RPC 超时 | 高 | GSP 固件与驱动通信异常,升级驱动/GSP 固件,伴随掉卡则报修 |
| 120 | GSP 错误 | 高 | 同上,常见于驱动/GSP 固件版本不匹配 |
看 XID 的三个关键动作:
- 看时间:
dmesg -T带时间戳,和训练任务失败时间对齐,确认因果关系。 - 看 PCI 地址:
PCI:0000:31:00.0对应具体哪张卡,用nvidia-smi -q | grep "Bus Id"映射到 GPU 序号。 - 看频率:同一 XID 偶尔一次可观察,一周内在同一张卡上反复出现 = 安排下线。
25.4 故障处理标准动作
确认节点有硬件级故障后,按标准流程处理,避免"带病上岗":
bash
# 1. drain 节点:驱逐业务 Pod,标记不可调度
kubectl drain gpu-node-17 --ignore-daemonsets --delete-emptydir-data
# 2. 在资产/工单系统标记节点为"维修中",记录序列号;报修时附上 dmesg、
# nvidia-smi -q、dcgmi diag 输出和交叉验证结论
nvidia-smi -q | grep -E "Serial Number|Bus Id"
# 3. 维修/换卡回来后 burn-in 验证(关键!不要让未验证的机器归队):
# 至少 2~4 小时满负载烧机 + 一轮 nccl-tests
./gpu_burn 7200
./all_reduce_perf -b 8 -e 8G -f 2 -g 8
# 4. 确认无 XID、无 ECC 新增、性能达标后归队
dmesg -T | grep -i xid # 应为空
kubectl uncordon gpu-node-17为什么 burn-in 不能省? 换过卡/重插过的机器,接触不良、固件不匹配等问题开机自检未必暴露,跑几小时满负载才会现形。省掉烧机直接归队再炸一次,伤害远大于烧机的几小时。
本章小结
- 排障先分层:硬件 → 固件 → 驱动 → CUDA/容器 → 调度 → 应用,自底向上。
- 通用流程:dmesg/XID → nvidia-smi → dcgmi → 隔离验证 → 交叉验证换机定位。
- XID 是 GPU 故障的黑匣子,重点记住 13/31/48/74/79/119/120;标准动作 drain → 报修 → burn-in → uncordon 缺一不可。
动手实验
- 在测试 GPU 机器上执行
dmesg -T | grep -i xid、nvidia-smi -q -d ECC,熟悉输出格式,并写出你环境的"XID → GPU 序号"映射命令(对比 dmesg 的 PCI 地址和nvidia-smi -q的 Bus Id)。 - 模拟演练:假设 node-3 报 XID 79,写出从 drain 到 uncordon 的完整命令序列。
第 26 章 典型硬件故障案例
本章目标
- 掌握五大类硬件故障(掉卡、ECC、过热、NVLink、供电)的完整排查链路,每个案例按"现象 → 排查 → 根因 → 解决 → 预防"五步法学习
26.1 掉卡:8 卡机器只剩 7 张
【现象】 用户报障:训练任务在 node-17 上起不来,报 CUDA error: no CUDA-capable device is detected。登录节点执行 nvidia-smi,只显示 7 张卡(应为 8 卡);极端情况下直接报 No devices were found。
【排查过程】
bash
# 第 1 步:看 XID —— 掉卡的直接证据
dmesg -T | grep -i xid
# [Wed Jun 12 03:14:22 2026] NVRM: Xid (PCI:0000:31:00.0): 79, GPU has fallen off the bus.
# 第 2 步:确认 PCIe 层面还能否看到设备
lspci | grep -i nvidia | wc -l # 输出 7 = 卡已从 PCIe 总线消失,偏硬件
lspci -s 31:00.0 -vv | grep -i lnk # 查看该槽位链路速率和宽度
# LnkSta: Speed 8GT/s (degraded), Width x8 (degraded) ← 链路降级!正常应为 16GT/s x16
# 第 3 步:检查电源与温度历史
ipmitool sel list | tail -20 # 看 BMC 事件日志有没有过温/掉电记录【根因分析】 XID 79(fallen off the bus)的常见原因,按概率排序:
| 可能原因 | 判别方法 |
|---|---|
| 接触不良(金手指氧化、没插紧)/ 卡本身损坏 | 交叉验证:换槽位后故障跟卡走还是跟槽位走 |
| PCIe 链路质量差(retimer/线缆/槽位) | lspci -vv 看 LnkSta 是否 degraded |
| 电源功率不足或供电线松动 | 高负载时掉卡、低负载正常;BMC 有电压告警 |
| 过热保护性掉卡 | XID 79 之前有温度飙升记录 |
【解决】 本案例交叉验证结论:故障跟着卡走,且金手指有氧化痕迹。清洁金手指、换槽位重插后稳定运行一周仍复发一次,最终走 RMA 换卡,换卡后 burn-in 4 小时无 XID、链路恢复 Speed 16GT/s, Width x16,归队。
【预防】
- 监控
DCGM_FI_DEV_XID_ERRORS和lspci链路状态,链路降级即告警;电源选型留 20% 以上余量(见 26.5)。 - 机房搬运/震动后例行检查:插拔类故障常在物理移动后暴露。
26.2 ECC 错误:显存的"体检报告"
背景知识:HBM 显存带 ECC(错误校验纠正)。SBE(单比特错误)可自动纠正,记录计数即可;DBE/UBE(双比特及以上错误)不可纠正,会破坏数据并触发 XID 48,必须高度重视。NVIDIA 还有行重映射(row remapping)机制,用备用行替换坏行。
【现象】 监控告警:DCGM_FI_DEV_ECC_DBE_VOL_TOTAL 在 node-22 的 GPU 5 上从 0 涨到 2。
【排查过程】
bash
# 查看 ECC 错误计数(区分可纠正/不可纠正、易失/累计)
nvidia-smi -q -d ECC -i 5
# Ecc Mode
# Current : Enabled
# ECC Errors
# Volatile
# Single Bit : 3 ← 可纠正,观察
# Double Bit : 2 ← 不可纠正,危险!
# Aggregate
# Double Bit : 2
# 查看行重映射(Ampere 及以后架构)
nvidia-smi -q -d ROW_REMAPPER -i 5
# Remapped Rows
# Correctable Error : 1
# Uncorrectable Error : 0
# Remapping Failure Occurred : No ← 若是 Yes,立即换卡
# 确认 XID
dmesg -T | grep -i xid
# Xid (PCI:0000:8A:00.0): 48, ... DBE【根因】 显存颗粒出现物理损坏,先以可纠正错误表现,逐步恶化为 DBE。
【换卡标准】(业界通行阈值,供参考):
| 指标 | 观察 | 安排换卡 |
|---|---|---|
| Volatile SBE | 零星增长 | 每天持续新增 |
| DBE | 1 次且 diag 通过 | 复现或 diag 失败即换 |
| Remapped Rows | 少量可纠正重映射 | 接近上限 / Remapping Failure = Yes |
| XID 48/95 频率 | 单次 | 一周内同卡 ≥ 2 次 |
【解决】 drain 节点 → dcgmi diag -r 3 -i 5 确认 Memory 测试失败 → 交叉验证确认跟卡走 → RMA 换卡 → burn-in 归队。
【预防】 ECC 指标进监控大盘,DBE > 0 自动建工单;每季度例行 dcgmi diag -r 2 做显存体检。
26.3 过热降频:任务没挂但变慢了
【现象】 算法团队反馈:下午 2~4 点训练速度比凌晨慢 15%,任务不报错。
【排查过程】
bash
# 第 1 步:看当前温度和频率
nvidia-smi --query-gpu=temperature.gpu,temperature.memory,clocks.sm --format=csv
# 92C, 94C, 1410MHz ← 温度很高,SM 频率明显低于标称 1980MHz
# 第 2 步:看降频原因位(关键命令)
nvidia-smi -q -d CLOCK
# Clocks Event Reasons
# SW Thermal Slowdown : Active ← 软件热降频生效!
# SW Power Cap : Active
# 第 3 步:查机房侧
ipmitool sensor list | grep -i inlet # 进风口温度 35C(正常应 < 27C)【根因】 机房冷通道的地板出风口被新到的包装箱堵了一半,进风温度飙升,GPU 触发 SW Thermal Slowdown 自动降频保护。温度阈值参考:A100/H100 核心温度到 ~85~95°C 开始降频,显存(HBM)超过 ~95°C 同样降频。
【解决】 清理冷通道障碍物,进风温度回落至 24°C,频率恢复。
【预防】
- 监控
DCGM_FI_DEV_GPU_TEMP、DCGM_FI_DEV_MEMORY_TEMP和 clocks_event_reasons,降频即告警——降频告警比温度告警更早发现问题。 - 另一个案例:某节点单卡温度异常高、其他卡正常,
ipmitool sensor | grep -i fan发现对应风扇转速为 0,换风扇模块解决。风扇转速必须进监控(N+1 冗余但坏一个就会形成局部热点)。
26.4 NVLink/NVSwitch 故障
【现象】 单机 8 卡训练 all-reduce 性能掉了一半,all_reduce_perf 的 busbw 从 380 GB/s 跌到 180 GB/s。
【排查过程】
bash
# 第 1 步:看 NVLink 链路状态,有链路不活跃!
nvidia-smi nvlink -s -i 0
# Link 5: <inactive>
# 第 2 步:看 NVLink 错误计数,重传错误持续累计 = 链路质量差
nvidia-smi nvlink -e -i 0
# Link 3: Replay Errors: 15234
# 第 3 步:NVSwitch 机型检查 Fabric Manager(另见 27.5);dmesg 应有 Xid 74
journalctl -u nvidia-fabricmanager | tail -20
dmesg -T | grep -i xid # Xid 74: NVLink error【根因】 该卡一条 NVLink 链路物理层不稳定(重传错误暴涨),NVSwitch 固件将整条链路降级,all-reduce 只能绕路,带宽腰斩。
【解决】 drain 后重置节点无效,交叉验证定位为 GPU 基板 NVLink 接口问题,报修换板卡后 busbw 恢复。
【预防】 把 NVLink 错误计数(DCGM_FI_DEV_NVLINK_*)和 nccl-tests busbw 基线纳入节点入列/周检项;性能基线漂移是 NVLink 故障最早的信号。
26.5 电源与供电问题
【现象】 一批新上架的 8 卡节点,满负载训练时随机整机重启,低负载正常,dmesg 无 XID,直接断在崩溃点。
【排查过程】
bash
# 第 1 步:BMC 事件日志(整机重启的第一现场)
ipmitool sel list
# Power Unit #0x41 | Power Supply AC lost | Asserted ← 电源掉电!
# 第 2 步:看功耗与 power cap
nvidia-smi -q -d POWER
# Current Power Limit : 400.00 W ← cap 异常:8卡机型单卡应为 700W【根因分析】 两类典型问题:
- 供电不足:整机电源 4×3000W 但市电为 110V/机房 PDU 限额,8×700W GPU + CPU + 风扇峰值超过供电能力,触发 PSU 保护性断电。特征:满负载才炸、无 XID、BMC 有 AC lost 记录。
- power cap 异常:某节点被遗留脚本
nvidia-smi -pl 400限了功耗,训练吞吐下降 30%。nvidia-smi -q -d POWER对比同机型即可发现。
【解决】 调整 PDU 供电分配/改接双路市电;清除遗留 power cap(nvidia-smi -pl 700 恢复额定功耗)。
【预防】 上架验收标准:满负载 gpu-burn 30 分钟 + 记录整机功耗 vs PSU 额定值(要求 ≤ 80%);把 power limit 值做成配置基线巡检项。
本章小结
- 掉卡看 XID 79 +
lspci链路状态 + 交叉验证;ECC 看 SBE/DBE 计数和 remapped rows,DBE 复现即换卡。 - 过热降频用
clocks_event_reasons定位,原因常在机房侧(冷通道、风扇)。 - NVLink 故障表现为性能腰斩,看
nvidia-smi nvlink -s/-e和 XID 74;随机重启无 XID,先查 BMC 电源事件和 power cap。
动手实验
- 在测试机上对比
nvidia-smi -q -d ECC,CLOCK,POWER的完整输出,找到降频原因位说明;并用nvidia-smi nvlink -s画出本机 NVLink 拓扑,与nvidia-smi topo -m对照。 - 写一条 PromQL(或监控规则描述):任一 GPU 出现 DBE 或 SW Thermal Slowdown 时告警。
第 27 章 软件与环境故障案例
本章目标
- 掌握驱动、CUDA 版本、容器 GPU 可见性、显存残留、fabricmanager、device-plugin 六类高频软件故障的处理流程
27.1 驱动异常:NVML 初始化失败
【现象】 节点重启(或内核自动升级)后,nvidia-smi 报:
Failed to initialize NVML: Driver/library version mismatch【根因】 系统内核升级后,旧版本 NVIDIA 内核模块还加载在内存里,而磁盘上的用户态库(libnvidia-ml.so)已更新,两者版本不匹配。这是 GPU 节点最高频的软件故障,没有之一。
【排查与处理】
bash
# 确认:内核模块版本(旧)vs 磁盘上用户态库版本(新)
cat /proc/driver/nvidia/version
dpkg -l | grep nvidia-driver
# 处理流程:drain → 确认无进程占用 → 卸载旧模块 → 加载新模块
kubectl drain gpu-node-05 --ignore-daemonsets --delete-emptydir-data
fuser -v /dev/nvidia* # 有进程占用则模块卸不掉
sudo rmmod nvidia_uvm nvidia_drm nvidia_modeset nvidia
sudo modprobe nvidia
nvidia-smi # 恢复正常
# 若 rmmod 报 "Module nvidia is in use",见 27.4 找残留进程【预防】 生产环境禁用内核自动升级(unattended-upgrades 排除 kernel),内核升级纳入变更窗口,用 DKMS 确保驱动随内核重编译;治本方案:节点重启后自动跑"nvidia-smi 自检脚本",失败则自动隔离并告警。
27.2 CUDA 版本不兼容
【现象】 用户在容器里跑训练,报 CUDA driver version is insufficient for CUDA runtime version,或 cudaGetDeviceCount() 返回 0,但宿主机 nvidia-smi 正常。
【根因】 容器镜像内置 CUDA 12.4,而宿主机驱动是 535.x(只支持到 CUDA 12.2)。CUDA 遵循驱动向后兼容 runtime 原则:旧驱动跑不了新 runtime。
【排查】
bash
# 宿主机驱动支持的最高 CUDA 版本(看 nvidia-smi 右上角)
nvidia-smi # Driver Version: 535.161 CUDA Version: 12.2
# 容器内 runtime 版本
kubectl exec -it train-pod -- nvcc --version【解决】 三选一:
| 方案 | 适用 |
|---|---|
| 升级宿主机驱动到 ≥ 550.x | 根治,推荐,按 25.4 标准流程操作 |
| 换用 CUDA ≤ 12.2 的镜像 | 临时方案,快 |
| 使用 CUDA Forward Compatibility 包 | 特定场景(如数据中心驱动+新 toolkit),需装 cuda-compat 包 |
【预防】 建立"集群驱动版本 ↔ 镜像 CUDA 版本"兼容矩阵文档;镜像 CI 里加一条检查:镜像 CUDA 版本是否超过集群最低驱动支持版本。
27.3 容器内看不到 GPU
【现象】 Pod 启动了,容器内 nvidia-smi 报 command not found 或看不到设备。
【排查清单】(按顺序逐项过):
bash
# ① Pod 是否真的申请了 GPU 资源?没有这行 limits,调度器不会给它挂设备
kubectl get pod train-pod -o yaml | grep -A3 resources
# nvidia.com/gpu: 1
# ② 节点上有没有可分配的 GPU?allocatable 为 0 → 见 27.6
kubectl describe node gpu-node-05 | grep nvidia.com/gpu
# ③ device-plugin Pod 是否正常?
kubectl -n kube-system get pod -l name=nvidia-device-plugin-ds -o wide
kubectl -n kube-system logs <device-plugin-pod>
# ④ 容器运行时是否配置了 nvidia runtime?检查 config.toml 中 runtimes.nvidia 及 default_runtime_name
grep -A3 nvidia /etc/containerd/config.toml
# ⑤ 环境变量是否被错误覆盖?void = 故意不挂卡
kubectl exec train-pod -- env | grep NVIDIA_VISIBLE_DEVICES
# NVIDIA_VISIBLE_DEVICES=void【常见根因与解决】
- containerd 升级后
config.toml被重置,nvidia runtime 配置丢失 → 用配置管理固化该文件。 - 用户 YAML 里写了
NVIDIA_VISIBLE_DEVICES: "void"(复制来的)→ 删掉;device-plugin 因节点 XID 异常退出 → 按 25 章流程处理硬件。
27.4 显存泄漏与僵尸进程
【现象】 nvidia-smi 显示某卡占用 60GB 显存,但 Processes 一栏为空,新任务报 OOM 起不来。
【根因排查】 "有显存无进程"通常三种情况:
bash
# ① 僵尸进程:进程已死但驱动层上下文未释放(常因 kill -9)
fuser -v /dev/nvidia* # 列出所有打开 GPU 设备文件的进程
# 若 fuser 能看到 PID 但 ps 看不到 → 是其他容器/namespace 里的进程,用宿主机视角查:
sudo nsenter -t <pid> -p hostname # 确认是哪个容器
# ② MPS 残留:MPS control daemon 活着会占显存
ps aux | grep -i mps
echo quit | nvidia-cuda-mps-control # 关闭 MPS
# ③ containerd shim 残留:Pod 已删除但 shim 进程还持有 GPU
ps aux | grep containerd-shim | grep -v grep【清理流程】
bash
# 1. 优先优雅处理:找到归属 Pod,让 K8s 删
kubectl get pod -A -o wide --field-selector spec.nodeName=gpu-node-05
# 2. 残留 shim/进程:确认无业务后 kill(先 TERM 后 KILL)
sudo kill <pid>; sleep 5; sudo kill -9 <pid> 2>/dev/null
# 3. 实在清不掉(驱动层上下文卡死):drain 后重置 GPU(需该卡无任何进程占用)
sudo nvidia-smi --gpu-reset -i 2
# 仍失败 → drain 节点重启,并按 25.4 观察是否伴随 XID(可能是硬件前兆)【预防】 训练框架统一用 torchrun 优雅退出;监控"显存占用 > 0 且无进程"超 10 分钟即告警;禁止业务容器里 kill -9 python 主进程(用 SIGTERM)。
27.5 fabricmanager 与驱动版本不一致
【现象】 NVSwitch 机型(如 DGX/HGX A100、H100)重启后,nvidia-smi topo -m 显示 GPU 之间全是 PIX/PHB 而非 NV#,多卡训练性能暴跌或直接 NCCL 初始化失败。
【排查】
bash
systemctl status nvidia-fabricmanager # failed
journalctl -u nvidia-fabricmanager | grep -i error
# [ERROR] The NVLink fabric manager version 550.54 does not match
# the driver version 535.161 ← 版本不匹配!
nvidia-smi nvlink -s | head -5 # 大量链路 inactive【根因】 nvidia-fabricmanager 的版本号必须与驱动完全一致(主版本.次版本都一致)。apt 升级驱动时 fabricmanager 包没跟着升(或未装),NVSwitch 无法初始化,GPU 间只能走 PCIe。
【解决】
bash
sudo apt install -y nvidia-fabricmanager-535=535.161.08-1 # 严格对齐驱动版本
sudo systemctl enable --now nvidia-fabricmanager
nvidia-smi topo -m # 确认恢复 NV# 互联【预防】 驱动升级 checklist 里固定包含"同步升级 fabricmanager 并验证 topo";把 nvidia-smi topo -m 的输出指纹做成节点巡检项,NVSwitch 节点出现 PIX 跨卡连接即告警。
27.6 kubelet 重启后 device-plugin 报卡数为 0
【现象】 节点重启或 kubelet 重启后,kubectl describe node 显示 nvidia.com/gpu: 0,GPU Pod 全部 pending。
【排查】
bash
# ① device-plugin Pod 状态
kubectl -n kube-system get pod -o wide -l name=nvidia-device-plugin-ds
# nvidia-device-plugin-xxxxx 0/1 CrashLoopBackOff
# ② 看日志(两种典型报错)
kubectl -n kube-system logs nvidia-device-plugin-xxxxx
# 报错A: "Failed to initialize NVML: Driver/library version mismatch" → 驱动问题,回 27.1
# 报错B: "error getting devices: ..." 且节点刚重启 → device-plugin 启动早于驱动就绪
# ③ 节点上驱动真的好了吗?
ssh gpu-node-05 nvidia-smi【根因与解决】
- 启动时序问题:节点重启后 device-plugin 先于驱动/nvidia-container-toolkit 就绪,CrashLoop 后 backoff 越等越久。解决:重启 device-plugin Pod 即可;根治是给 DS 加 initContainer 等待
nvidia-smi成功。 - 驱动真的坏了(27.1 场景):修驱动后重启 device-plugin。
- allocatable 残留:plugin 恢复后 allocatable 会自动刷新;不刷新则重启 kubelet。
bash
kubectl -n kube-system delete pod -l name=nvidia-device-plugin-ds \
--field-selector spec.nodeName=gpu-node-05
kubectl describe node gpu-node-05 | grep nvidia.com/gpu # 应恢复为 8【预防】 device-plugin DS 配置 initContainer:until nvidia-smi; do sleep 5; done;节点重启后自动巡检"allocatable GPU == 物理卡数"。
本章小结
- "version mismatch" = 内核升级后驱动未重载,drain → rmmod → modprobe;容器 CUDA 版本不能超过驱动支持上限,建兼容矩阵。
- 容器看不到卡按五连查:limits → allocatable → device-plugin → runtime 配置 → 环境变量。
- 有显存无进程用
fuser -v /dev/nvidia*定位,三类元凶:僵尸进程、MPS、shim 残留。 - NVSwitch 机型:fabricmanager 版本必须 == 驱动版本;device-plugin 卡数为 0 先查 NVML,再查启动时序。
动手实验
- 在测试环境复现 version mismatch 并修复;用
docker run --rm -e NVIDIA_VISIBLE_DEVICES=void复现容器内无卡,再用正确配置恢复。 - 写一个 30 行的节点自检脚本:检查 nvidia-smi、allocatable、fabricmanager(如适用)、XID,输出 PASS/FAIL。
第 28 章 训练任务侧故障案例
本章目标
- 掌握 NCCL 超时、OOM、训练变慢、存储抖动四类任务侧故障的排查套路
- 通过一个完整综合案例串起本部分所有方法论;学会建立团队故障知识库
28.1 NCCL 超时:分布式训练的第一大报障
【现象】 多机训练运行一段时间后挂掉,日志:
[Rank 17] Watchdog caught collective operation timeout:
WorkNCCL(SeqNum=4231, OpType=ALLREDUCE, Timeout(ms)=600000)
RuntimeError: NCCL communicator watchdog timed out【先理解机制】 NCCL watchdog 默认 600 秒内某个集合通信没完成就杀掉任务。超时只是"果",某个 rank 掉队(straggler)才是"因"——所有 rank 都在等最慢的那个。
【排名前列的原因与排查步骤】
第 1 步:定位是谁掉队。超时日志往前翻,找"最后完成 SeqNum 较小的 rank"——通常 63 个 rank 卡在 SeqNum=4231、1 个 rank 还在 4000,那它就是 straggler:
bash
grep -E "Rank.*SeqNum" train.log | sort | uniq -c | sort -rn | head第 2 步:straggler 落在哪个节点?对照任务日志的 rank→node 映射,然后查三大嫌疑:
| 嫌疑 | 排查方法 | 典型案例 |
|---|---|---|
| 同机另一任务打满资源 | nvidia-smi、看 CPU/内存/PCIe 争用 | 共享节点上别人的 debug 任务抢走了数据加载线程 |
| IB/RoCE 链路异常 | ibstat、perfquery 看误码、nccl-tests 多机带宽 | 光模块衰减导致某对节点带宽从 400G 掉到 100G |
| 节点本身慢(掉卡/降频/ECC) | 本部分 26 章全套流程 | GPU 降频导致该节点每 step 慢 20% |
bash
# 第 3 步:用 nccl-tests 二分定位链路问题;全量 busbw 明显低 → 逐对节点两两跑
mpirun -np 64 --hostfile hosts ./all_reduce_perf -b 8 -e 1G -f 2 -g 8【解决与预防】 隔离慢节点/修复 IB 链路;训练任务开启 straggler 检测(监控各 rank step 时间方差)。不要把"调大 NCCL_TIMEOUT"当解决方案——它只是推迟报警,掩盖真问题。
28.2 OOM:两种 OOM 别搞混
【现象 A:训练 OOM(显存不足)】
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 3.12 GiB (GPU 0;
79.15 GiB total capacity; 74.20 GiB allocated; 72.50 GiB reserved by PyTorch)关键看最后一行:"reserved by PyTorch" 高但 allocated 没那么高 = 显存碎片。PyTorch 缓存分配器反复分配释放不同大小的块,留下一堆用不上的碎片。
bash
# 解决碎片:开启可扩展段(PyTorch 2.1+)
export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
# 其他手段:梯度累积、gradient checkpointing、换 ZeRO/FSDP、调小 batch【现象 B:系统 OOMKilled(内存不足,不是显存)】
bash
kubectl get pod train-pod -o yaml | grep -A3 lastState
# reason: OOMKilled exitCode: 137 ← 容器内存超限被 cgroup 杀根因多为:dataloader workers 过多、pin_memory + 大数据集缓存、共享内存(/dev/shm)不足(此时报错常是 bus error 而非 OOMKilled,记得调大 shm;也可 dmesg -T | grep -i "out of memory" 佐证)。
【现象 C:共享卡被别人挤占】 虚拟化/共享场景(本教材第三部分),别人突发占满显存导致你的任务 OOM。排查:对比任务 OOM 时间点与其他任务的显存曲线;解决:用显存配额硬性隔离,或迁移到独占卡。
【预防】 任务模板默认开启 expandable_segments;平台侧对共享卡实施显存配额;监控 OOMKilled 事件自动告警。
28.3 训练变慢的排查套路
按"利用率形态"分三种情况,对号入座:
情况 1:GPU 利用率低(util < 50% 且有大量空闲间隙)→ 喂不饱
bash
nvidia-smi dmon -s u # util 忽高忽低,周期性掉到 0 → 数据加载瓶颈
mpstat -P ALL 1 # 看 CPU 是否被同机其他任务打满
iostat -x 1 # 看存储延迟排查 dataloader:num_workers 是不是太少/为 0?数据集是不是在小文件很多的 NFS 上(随机读小文件是噩梦)?
情况 2:util 高但吞吐低 → 算力没用对地方
常见原因:没开混合精度(AMP)、没开 TF32、cudnn benchmark 未开、算子没走融合 kernel、batch size 过小。排查:和历史基线/同任务在其他集群的吞吐对比;用 nsys/torch.profiler 看 kernel 时间分布。
情况 3:单机正常、多机慢 → 通信问题
bash
# ① 先看 NCCL 实际走了什么链路,确认走 IB 而非 Socket(TCP)
NCCL_DEBUG=INFO torchrun ... 2>&1 | grep -E "NCCL INFO NET|via"
# NCCL INFO NET/IB : using [0]mlx5_0:1/RoCE
# ② 量化带宽基线,对比历史 busbw
./all_reduce_perf -b 1G -e 8G -f 2 -g 8③ straggler 定位(通用套路):给训练加每 rank step 耗时日志(或用 profiling job),统计各节点 step 时间均值,明显离群的就是慢节点;慢节点再按 26 章硬件流程 + 28.1 流程查。
28.4 存储侧故障:checkpoint 写爆与 NFS 抖动
案例 1:checkpoint 写爆存储
【现象】 夜间大量任务同时失败,报 No space left on device;共享存储集群告警容量 100%。
【根因】 多个大模型任务同时在 NFS/Lustre 上保存 checkpoint(每个数百 GB),且 save_every_n_steps 配得太密、旧 checkpoint 无清理策略,48 小时写入 200TB。
【解决与预防】 应急用 du -sh /ckpt/* | sort -rh | head 定位大户,联系属主清理/转冷存。预防(平台侧):checkpoint 目录按项目配 quota;训练框架统一封装 save 逻辑,默认 keep_last_k=3 滚动删除;大模型用异步/分片 checkpoint(如 torch.distributed.checkpoint)削写入峰值。
案例 2:NFS 抖动导致训练 hang
【现象】 训练不报错、不超时(NCCL 还没触发),但 step 时间从 2s 变成 300s,GPU util 为 0,看起来"挂住了"。
【排查】
bash
# ① 看进程状态:D 状态(不可中断睡眠)= 卡在 IO
ps aux | grep python | awk '$8 ~ /D/'
# ② 确认卡在哪个挂载点
cat /proc/<pid>/stack 2>/dev/null | head
iostat -x 1 # nfs01: r_await 4500ms ← NFS 响应时间爆炸【根因】 存储集群某节点故障触发重构,NFS 服务端响应变慢,dataloader 读数据卡住。预防:训练数据放本地 NVMe 缓存或并行文件系统;监控 D 状态进程数和挂载点延迟。
28.5 综合案例复盘:每晚 11 点准时 hang 的训练
这是一个把前面所有套路串起来的完整故事,请体会排查思路的演进。
【现象】 某 64 卡 LLM 预训练任务,连续一周每晚 23:00~23:40 之间 hang 住,NCCL watchdog 超时杀任务。白天重跑一切正常。用户怀疑"集群有鬼"。
【第一轮排查:怀疑硬件(按 25/26 章)】
bash
# 对齐 hang 时间点查所有 8 个节点的 XID、温度、ECC
for n in node-{31..38}; do ssh $n "dmesg -T | grep -i xid | tail -3"; done
# 结果:全部为空,温度正常,无降频 → 排除硬件层
# (但学到了:故障有固定时间规律,这本身就是最强线索)【第二轮排查:怀疑 NCCL/网络(按 28.1)】
分析 watchdog 日志:63 个 rank 等 1 个 rank,straggler 是 rank 42,落在 node-36。但每天 hang 时 straggler 落在不同节点——不是固定节点坏。白天对全部节点两两跑 all_reduce_perf,busbw 全部达标。
【第三轮排查:利用"固定时间"这个线索】
固定时间出问题 → 一定有个定时事件。列出集群里 23:00 前后的定时任务:
bash
kubectl get cronjob -A | grep -i backup
# storage-team/ceph-snapshot-backup 0 23 * * * ← 每天 23:00!【根因】 存储团队每天 23:00 对训练数据所在的 Ceph 集群做快照备份,备份期间 OSD 带宽被打满,训练任务的 dataloader 读数据延迟从 5ms 飙到 3s+。数据加载最慢的 rank 每天不同(取决于数据 shard 落在哪个 OSD),所以 straggler 节点随机。40 分钟后备份高峰过去,但 NCCL 600 秒超时早已触发。
这正是 28.4 NFS 抖动案例的分布式版本:存储抖动 → 数据加载慢 → 某个 rank 掉队 → NCCL 超时。表面是 NCCL 故障,根因在存储侧。
【解决】 ① 备份窗口调整到凌晨 4 点(训练低峰)并限流;② 训练侧开启 dataloader 预取 + 本地缓存,降低对共享存储的实时依赖;③ step 耗时和 dataloader 等待时间进监控,超阈值告警。
【预防(流程改进)】 建立跨团队变更日历:存储/网络的计划性操作同步到训练平台侧,训练任务调度避开高风险窗口。
28.6 建立故障知识库
每个故障按统一模板归档,团队共享。坚持半年,新人成长速度和 MTTR 会有质变。故障记录模板:
markdown
## 故障编号:GPU-2026-0142 日期:2026-06-12
- **现象**:node-17 训练任务 NCCL 超时,nvidia-smi 仅 7 卡
- **影响范围**:1 个 64 卡任务中断 4 小时
- **排查过程**:dmesg 见 XID 79 → lspci 链路 degraded → 交叉验证跟卡走
- **根因**:GPU 金手指氧化导致接触不良,震动后掉卡
- **处理**:清洁重插无效 → RMA 换卡 → burn-in 4h → 归队
- **预防措施**:链路状态监控告警;搬运后例行插拔检查
- **相关命令/日志**:(附原始输出)团队协作机制建议:
| 机制 | 做法 |
|---|---|
| 故障复盘会 | 每个 P1/P2 故障 48 小时内复盘,只问"系统哪里可以改进",不追责个人 |
| 值班手册 | 把高频故障(26/27/28 章)做成 if-then 流程卡,值班人照做即可 |
| 知识库检索 | 按 XID 码/报错关键字打标签,新故障先搜历史 |
| 演练 | 每季度在测试环境人为注入故障(拔卡、限速、杀进程),保持手感 |
本章小结
- NCCL 超时 = 找 straggler:看日志定位 rank → 落到节点 → 查资源争用/IB 链路/硬件。
- OOM 分三种:显存 OOM(注意碎片,
expandable_segments)、系统 OOMKilled、共享卡挤占。 - 变慢三形态:util 低查数据加载;util 高吞吐低查算子/精度;多机慢查 NCCL 链路和 straggler。
- 存储抖动会伪装成 NCCL 故障;固定时间出现的故障,先找定时任务;故障知识库是团队最宝贵的资产。
动手实验
- 用两卡跑一个故意让 rank1 sleep 的脚本,复现 NCCL watchdog 超时,练习从日志定位 straggler。
- 在测试任务上开/关
expandable_segments,对比长时间训练的显存 reserved 曲线。 - 按 28.6 模板把一个故障写成知识库条目;再针对"任务每天凌晨 hang"列出你的完整排查步骤,与 28.5 对照。
本部分总结:故障诊断 = 分层模型 × 标准流程 × 案例经验。硬件故障靠 XID 和交叉验证,软件故障靠版本矩阵和排查清单,任务侧故障靠 straggler 定位和资源视角。把每个故障沉淀进知识库,你就从"救火队员"成长为"防火工程师"。