第10章:常见故障排查与SRE实践
2025.11.29在第九章中,我们为智算中心安装了强大的“天眼”系统——一个覆盖全链路的可观测性平台。我们现在能够看到硬件的每一次心跳,追踪业务的每一次请求。然而,监控本身并不能解决问题,它只是吹响了战斗的号角。当告警声响起,真正的考验才刚刚开始。
一个规模数亿元的千卡集群,每宕机一分钟,都可能意味着数千乃至数万元的算力成本被白白烧掉(量级估算,取决于集群成本与利用率)。作为智算中心的守护者,我们的核心价值不仅在于保障系统“不出事”,更在于出事后能以最快的速度恢复服务(MTTR - Mean Time To Recovery)。这要求我们不仅要熟悉工具,更要具备像急诊医生一样的临床诊断思维:快速分类、定位病灶、对症下药、事后复盘。
本章,我们将直面智算中心运营中最具挑战性、也最惊心动魄的一面——故障处理。我们将从最底层的硬件故障(如GPU掉卡、ECC错误)开始,向上延伸到分布式训练中令人头疼的网络通信问题(如NCCL Timeout),最后再深入到与算法紧密相关的训练过程本身(如Loss NaN、梯度爆炸)。我们将为你提供的,不是一份零散的“偏方”列表,而是一套结构化的、可遵循的SRE排查决策树,帮助你在混乱的故障现场,保持清醒的头脑,步步为营,直击根源。
10.1 硬件级故障:ECC Error、XID Error、掉卡、光模块故障
硬件是所有计算的物理基石,也是故障最频繁发生的层面。硬件故障通常表现为突发性、不可预测、且后果严重。一个成熟的AI Infra团队,必须对硬件故障有高度的敏感性和标准化的处理预案(SOP - Standard Operating Procedure)。
10.1.1 ECC Error:显存的“健康警报”
什么是ECC Error?
ECC(Error-Correcting Code)是一种内存容错技术。数据中心级的GPU(如A100, H800, 910B)使用的HBM显存都支持ECC。它能在数据读写过程中,检测并纠正小范围的比特翻转(bit flip)错误。
比特翻转可能由宇宙射线、电源噪声、芯片老化等多种原因引起。
ECC错误分为两类:
- 可纠正错误 (Correctable Errors): 单个比特的错误。ECC电路可以自动发现并修正它,对上层应用完全透明,通常不会导致程序崩溃。
- 不可纠正错误 (Uncorrectable Errors): 两个或更多比特的错误,超出了ECC的纠正能力。这会导致数据损坏,并通常会触发GPU驱动的严重错误,导致应用崩溃或GPU被重置。不可纠正错误分为SBE(Single-Bit Error,在某些特殊位置)和DBE(Double-Bit Error)。
如何发现?
- 监控告警(最佳实践): 在上一章我们已经设置了告警规则。
increase(DCGM_FI_DEV_UNCORRECTED_SBE_ERRORS[1h]) > 0或increase(DCGM_FI_DEV_UNCORRECTED_DBE_ERRORS[1h]) > 0-> 高优先级告警!increase(DCGM_FI_DEV_CORRECTED_SBE_ERRORS[1h]) > 100-> 中等优先级告警。
- 手动检查:
- NVIDIA:
nvidia-smi -q -d ECC - 华为昇腾:
npu-smi info -t ecc
- NVIDIA:
排查与处理决策树:
if (出现不可纠正错误):
这是一个明确的硬件故障信号! 1.立即隔离节点: 立即将该GPU所在的节点设置为不可调度(
kubectl cordon <node-name>),并驱逐(drain)上面的所有Pod,防止新的任务被调度到有问题的硬件上。 2.记录信息: 记录下故障的GPU卡UUID、服务器序列号、故障时间和错误日志(dmesg | grep -i nve或类似的NPU日志)。 3.创建报修工单: 提交工单给数据中心现场团队或硬件供应商,要求更换GPU卡。不要尝试通过重启来“修复”它,因为这是物理层面的损坏,问题会再次出现。 4.事后分析: 跟踪该批次GPU卡的故障率,如果普遍偏高,可能需要与供应商进行批次质量问题的沟通。
else if (出现少量、偶发的可纠正错误):
通常无需立即行动,但需密切观察。 1.继续监控: 观察该卡的Correctable Error增长速率。如果是几个小时甚至几天才出现零星几个,可以暂时忽略。 2.检查环境因素: 检查机房温度、湿度是否在正常范围。过高的温度会增加比特翻转的概率。
else if (可纠正错误持续、高速增长):
这是硬件即将“寿终正寝”的前兆。 1.评估风险: 虽然应用暂时没崩溃,但高频的ECC纠正会带来微小的性能开销,并且这是发展成不可纠正错误的强烈信号。 2.计划性维护: 同样需要隔离节点,并安排在业务低峰期进行硬件更换。这属于“预防性维护”,避免它在未来的某个关键时刻突然“暴毙”。
10.1.2 XID Error:GPU驱动的“求救信号”
什么是XID Error?
- XID 是NVIDIA驱动内部定义的一套错误代码(历史上源自X服务器的错误编号体系,现已与Xorg无直接关系)。当GPU遇到驱动无法自行处理的严重内部错误时,它会在内核日志(
dmesg)中打印一条NVRM: Xid (PCI: ...): ...的错误信息。 - 每一个XID代码都对应一种特定的内部故障,是排查GPU疑难杂症的“藏宝图”。
如何发现?
- 监控告警:
rate(DCGM_FI_DEV_XID_ERRORS[5m]) > 0-> 高优先级告警! - 手动检查:
dmesg | grep -i xid
常见XID代码与排查思路:
XID 13/XID 31: GPU Page Fault。- 含义: GPU试图访问一个无效的、不存在的或无权限的显存地址。
- 可能原因:
- CUDA程序Bug(最常见): 你的AI应用代码中存在内存访问越界、使用了野指针等问题。这需要算法工程师介入调试代码。
- 驱动Bug: 在极少数情况下,可能是NVIDIA驱动本身的Bug。尝试升级/降级驱动版本。
XID 43/XID 45: GPU已从总线上脱落 (Fallen off the bus)。- 含义: GPU与主板的PCIe连接中断。
- 可能原因:
- 硬件接触不良: GPU卡没有插紧、PCIe插槽金手指氧化。
- 供电问题: GPU的外部供电线缆松动或供电不足。
- 主板或GPU硬件故障。
- 处理: 这是严重的硬件问题。需要现场工程师对服务器进行断电、重新插拔GPU卡和供电线。如果问题复现,需要替换GPU卡或主板。
XID 62/XID 79: 非法指令或内存操作。- 含义: GPU的计算单元在执行时遇到了无法识别的指令或非法的操作。
- 可能原因:
- 硬件故障: GPU内部的某个计算单元或调度器损坏。
- 过热: 温度过高导致GPU工作不稳定。
- 处理: 首先检查GPU温度。如果温度正常,这通常也指向需要更换的硬件故障。
XID排查通用流程:
- 记录XID代码和错误信息。
- Google/NVIDIA开发者论坛查询: 将完整的XID错误信息输入搜索引擎,通常能找到NVIDIA官方或其他开发者对该错误的解释和常见原因。
- 关联分析: 查看故障发生时,该GPU上正在运行什么任务?是某个特定的训练脚本吗?如果是,让算法工程师检查代码。
- 硬件排除法: 如果多个不同的、成熟的应用在同一张卡上都触发了XID错误,那么大概率是硬件问题。反之,如果某个XID只在运行某个新开发的程序时出现,那很可能是软件Bug。
10.1.3 GPU/NPU掉卡:最直观的“失联”
现象
nvidia-smi或npu-smi列出的设备数量少于物理安装的数量。- 训练任务报
CUDA_ERROR_NO_DEVICE或类似的NPU设备未找到错误。 dmesg中可能出现GPU has fallen off the bus等日志。
排查决策树:
Step 1: 软件层面复位。- NVIDIA: 尝试执行
sudo nvidia-smi --gpu-reset -i <gpu_id>。 - 华为昇腾: 尝试使用
npu-smi reset相关命令。 if (复位成功,卡恢复可见):这可能是由某些软件状态异常或驱动的临时性问题导致。可以尝试将该卡重新投入使用,但需要重点监控,如果短时间内再次掉卡,则问题很可能在硬件层。
else (复位失败):进入下一步。
- NVIDIA: 尝试执行
Step 2: 操作系统层面检查。- 执行
lspci | grep -i 'NVIDIA\|Huawei'。 if (在lspci中看不到该设备):这意味着GPU在PCIe总线层面就已经“消失”了。这是明确的硬件或固件层问题。直接跳到Step 4。
else (能看到设备,但驱动无法加载):问题可能出在驱动和内核模块。尝试卸载并重新安装驱动 (
sudo apt-get purge nvidia-*然后重装)。如果问题依旧,进入下一步。
- 执行
Step 3: 服务器冷重启。- 对故障服务器进行一次完整的冷启动(关机再开机,而不是
reboot)。冷启动会重新初始化所有硬件。 if (重启后卡恢复):问题可能较为复杂,可能是主板、BIOS/UEFI与GPU的兼容性或固件问题。需要记录下服务器型号、BIOS版本、GPU型号,并观察是否在同类机型上复现。考虑升级服务器固件。
else (重启后依然掉卡):若错误在同一物理节点上跨任务、跨容器稳定复现,应优先怀疑该节点的硬件、固件或链路;不能在没有样本基线和排除步骤时给出“99%”的故障概率。
- 对故障服务器进行一次完整的冷启动(关机再开机,而不是
Step 4: 现场硬件排查 (On-site Intervention)。- 隔离节点,提报工单。
- 现场工程师进行标准操作: a. 断电,重新插拔GPU卡。 b. 检查并重新插拔GPU的外部供电线。 c. 将故障卡与同一台服务器上的正常卡交换PCIe插槽。如果问题跟随卡走(即换到新插槽后还是这张卡坏),说明是GPU卡故障。如果问题留着插槽上(即新换来的好卡在这个槽位也坏了),说明是主板PCIe插槽故障。 d. 更换硬件:根据上一步的判断,更换GPU卡或主板。
10.1.4 光模块与线缆故障
对于依赖RDMA网络的大规模分布式训练,物理网络的任何一点瑕疵都会被急剧放大。光模块和线缆是看似不起眼、却极其常见的故障点。
现象:
- 某个节点的训练速度突然变得极慢,拖慢整个集群。
- 训练日志中出现大量
NCCL Timeout或类似的通信超时错误,且错误总是指向同一个节点。 - 交换机日志中显示某个端口有大量的CRC Error、丢包或频繁UP/DOWN。
排查思路:
定位问题端口:
- 根据NCCL日志中报告的节点IP,登录到该节点。
- 执行
ibstat(InfiniBand) 或ethtool <interface_name>(RoCE) 查看网卡端口状态。State应该是Active,Physical state应该是LinkUp。 - 登录到该端口所连接的交换机,查看对应端口的状态和错误计数器。
show interface <interface_id> counters errors。
使用硬件诊断工具:
- Mellanox工具集 (IB/RoCE):
ibdiagnet: 一个强大的IB网络诊断工具,能扫描整个网络的拓扑、链路质量和配置,并生成详细的报告。ibclearerrors: 清除所有端口的错误计数器。ibqueryerrors -c: 在一段时间后,再次查询错误计数器,看哪些端口有新的错误增长。
- 交换机诊断命令: 现代交换机通常支持通过
show interface transceiver detail等命令,读取光模块的数字诊断监控(DDM/DOM)信息。这包括:- 光模块的收/发光功率 (Rx/Tx Power)。如果光功率过低或过高,都意味着光模块或光纤有问题。
- 温度和电压。
- Mellanox工具集 (IB/RoCE):
物理替换法:
- 换线: 将故障端口的光纤跳线更换一根。
- 换光模块: 如果换线无效,更换端口上的光模块。
- 换端口: 将线缆插到交换机或网卡的另一个空闲端口上。
- 通过这些交叉验证,可以快速定位故障点到底是线、光模块、网卡端口还是交换机端口。
SRE实践总结:
对于硬件故障,关键在于标准化、流程化。你需要建立一套覆盖“监控告警 -> 自动隔离 -> 工单系统 -> 现场操作 -> 故障复盘”的完整闭环。同时,备件管理至关重要,数据中心必须常备一定比例的GPU、网卡、光模块、线缆等备件,以缩短维修等待时间。
10.2 网络级故障:NCCL Timeout、通信死锁(Deadlock)排查
网络是分布式训练的生命线。网络故障通常比硬件故障更隐蔽、更难复现,是AI Infra工程师的“噩梦”之一。
10.2.1 NCCL Timeout:最常见的“网络感冒”
现象:
训练日志在长时间的沉默后,突然打印出大量类似以下的错误信息:
NCCL WARN Cuda Error in ... : 700 (an illegal memory access was encountered)
NCCL WARN unhandled cuda error ... ret=700
torch.distributed.DistBackendError: NCCL error in ...: 7 (Internal check failed)
核心词是 Timeout, unhandled cuda error, Connect recv failed。
根本原因:
NCCL Timeout的本质是:一个或多个GPU在参与All-Reduce等集合通信时,在规定的时间内,没有等到其他GPU的数据或信令。 这就像一个接力赛跑,有一个选手迟迟没有把棒递过来,导致整个队伍都卡住了。
排查决策树:
Level 1: 是否是“假”网络问题?
很多时候,网络是“背锅侠”,根本原因在别处。 1.检查是否有GPU卡死: 这是最常见的原因。某个GPU因为硬件故障(如XID错误)、过热或程序Bug而卡死,无法参与通信。 - 排查方法: 在所有节点上同时执行
nvidia-smi或dcgm-exporter的指标。如果发现某个GPU的利用率/功耗/温度异常(例如,利用率为0但显存占用很高,或者温度持续在95度以上),或者dmesg中有XID日志,那么问题就在这张卡上。处理方法见10.1节。 2.检查是否有CPU瓶颈或OOM Killer: - 排查方法: 在训练过程中,在所有节点上使用top或htop监控CPU使用率。如果数据预处理逻辑过于复杂,某个节点的CPU被打满100%,导致它无法及时地向GPU提交计算或通信任务,也会引发其他节点的NCCL Timeout。 - 使用dmesg | grep -i 'out of memory'检查是否有进程被系统的OOM Killer杀掉。如果数据加载进程被杀,GPU同样会因为“断粮”而卡住。
Level 2: 确认是网络问题,开始排查。
如果所有GPU和CPU状态看起来都正常,那么问题大概率在网络层。 1.开启NCCL调试日志: 这是排查NCCL问题的“神器”。在启动训练前,设置环境变量:
export NCCL_DEBUG=INFOexport NCCL_DEBUG_SUBSYS=ALL重新运行任务,NCCL会打印出极其详细的日志,包括: - 它检测到的网络拓扑结构(Ring/Tree)。 - 它选择了哪种通信协议(LL, LL128, Simple)。 - 每个GPU之间建立连接的过程。 - 数据传输的详细过程。 仔细阅读这些日志,通常能发现是哪两个节点之间的连接建立失败,或者在哪个环节卡住了。 2.基础网络连通性测试: - 在报告超时的两个节点之间,进行基础的ping和traceroute,确保网络是通的。 - RDMA连通性测试: - IB网络:ibping- RoCE网络:rping- 如果RDMA ping不通,说明RDMA的配置(如RoCE的PFC/ECN)或物理链路出了问题。 3.带宽和延迟测试: - 使用ib_write_bw,ib_read_bw(IB) 或qperf(RoCE) 等工具,在故障节点对之间,进行点对点的带宽和延迟压力测试。 -if (带宽远低于理论值或延迟极高): ### > 问题就在这两个节点之间的物理链路上。按照10.1.4节的方法,检查光模块、线缆和交换机端口。 4.检查无损网络配置 (RoCE特有): - RoCE网络最怕丢包。丢包会导致RDMA性能急剧下降,引发超时。 - 登录到路径上的所有交换机,检查PFC和ECN相关的计数器。 -show priority-flow-control counters interface ...: 查看PFCPAUSE帧的收发统计。如果某个端口的Tx Pause帧数量巨大,说明下游拥塞严重。 -show queue-counters interface ...: 查看队列的丢包计数。任何非零的丢包都意味着无损网络配置失败。 - 无损网络的调试非常复杂,需要网络专家介入,检查端到端的DSCP优先级标记、交换机的队列映射、WRED/ECN阈值等配置。
10.2.2 通信死锁(Deadlock)
现象:
训练任务看起来在运行,所有GPU的利用率可能都很高(甚至100%),但训练的step或loss长时间不更新。任务“活着”,但“死了”。
原因:
死锁通常发生在更复杂的并行策略(如张量并行+流水线并行)或自定义的通信逻辑中。其本质是形成了一个循环等待的依赖关系。
- 例子:
- GPU 0在等待GPU 1的数据。
- GPU 1在等待GPU 2的数据。
- ...
- GPU N在等待GPU 0的数据。
- 这个环路导致没有任何一个GPU可以继续前进。
排查思路:
- GDB/pdb附加进程: 这是最直接、但也最硬核的方法。登录到其中一个卡住的节点,找到训练的Python进程,使用
gdb -p <pid>或py-spy等工具附加进去,查看所有线程的堆栈。py-spy top --pid <pid>: 可以实时看到每个函数花费的时间。如果所有时间都消耗在某个torch.distributed.recv或hccl.recv之类的通信调用上,就证实了死锁。gdb中,可以使用bt命令打印堆栈。如果堆栈的顶端是某个阻塞的通信操作,也能说明问题。
- 简化并行策略:
- 如果正在使用复杂的3D并行,尝试降维。例如,先去掉流水线并行,只用TP+DP跑一下,看是否还死锁。再去掉张量并行,只用DP跑。通过这种方式,定位是哪种并行策略的实现或交互导致了问题。
- 代码审查:
- 死锁的根源最终都在代码逻辑里。需要算法工程师和框架工程师一起,仔细审查模型的
forward函数中的通信顺序。 - 确保没有循环依赖。
- 确保通信操作(如
send/recv)的顺序在所有Rank上是匹配的。 - 检查是否有不同并行组(Process Group)之间的交叉通信,这尤其容易导致死锁。
- 死锁的根源最终都在代码逻辑里。需要算法工程师和框架工程师一起,仔细审查模型的
10.3 训练级故障:Loss NaN、梯度爆炸、训练卡死的运维排查树
这类故障发生在训练过程的算法层面,但其根源可能来自数据、代码、超参数,甚至是硬件。AI Infra工程师虽然不直接负责修改算法,但需要提供必要的工具和排查思路,帮助算法工程师快速定位问题。
10.3.1 Loss NaN(Not a Number):训练“脱轨”
现象: 训练日志中的
loss值突然变成了NaN。一旦出现,通常是不可逆的,训练宣告失败。根本原因: 在计算过程中,出现了数学上未定义的操作,如
0/0、sqrt(-1)、log(0)等。排查决策树(运维视角):
Step 1: 检查数据。
“Garbage in, garbage out.” 脏数据是导致NaN的常见元凶。 1.数据加载脚本: 检查数据预处理和加载的代码,是否有逻辑错误导致读入了空文件、损坏的图像、或者生成了内容为空的Token序列。 2.数值稳定性: 检查输入数据是否经过了归一化(Normalization)。如果输入的数值范围过大或过小,在计算中很容易溢出。
Step 2: 检查模型与超参数。
1.学习率 (Learning Rate) 过高: 这是最最常见的原因!过高的学习率会导致参数更新步子迈得太大,直接“跨”到了一个产生NaN的区域。 - 运维建议: 建议算法工程师降低学习率(例如,减小一个数量级),然后重新从上一个好的Checkpoint开始训练。 2.模型实现中的数值不稳定操作: - 例如,在计算交叉熵损失时,如果对logits先做
exp()再做log(),当logits很大时,exp()可能会溢出为inf,log(inf)还是inf,但中间的除法可能产生inf/inf,结果就是NaN。PyTorch的torch.nn.CrossEntropyLoss内部已经处理了这些数值稳定性问题。 - 运维建议: 建议算法工程师检查是否使用了不安全的自定义数学运算,尽量使用框架提供的、经过数值稳定性优化的模块。 3.混合精度训练问题: - 在使用FP16半精度训练时,其数值范围(约6e-5到65504)远小于FP32。如果梯度过小,可能会下溢(underflow)变成0;如果数值过大,则会上溢(overflow)变成inf。 - Loss Scaling (损失缩放): PyTorch的AMP(torch.cuda.amp.GradScaler)等工具会自动进行损失缩放来缓解这个问题。它在计算loss后,先将其乘以一个巨大的缩放因子(如65536),使得梯度相应地放大,避免下溢。在更新参数前,再将梯度除以该缩放因子。 - 运维建议: 确保算法工程师正确地开启了混合精度训练和Loss Scaling。如果开启了仍然NaN,可以尝试调整GradScaler的init_scale等参数。
Step 3: 使用调试工具。
PyTorch Anomaly Detection: 在训练脚本的开头加上这两行代码:
python import torch torch.autograd.set_detect_anomaly(True)当出现NaN时,PyTorch会打印出导致该NaN的完整反向传播堆栈,直接告诉你是哪一个操作产生了坏的梯度。这是定位问题的终极武器。但注意,它会降低训练速度,只在调试时开启。
10.3.2 梯度爆炸/消失 (Gradient Exploding/Vanishing)
- 现象:
- 梯度爆炸:
loss值突然急剧增大,变成一个巨大的数字甚至inf。 - 梯度消失:
loss长时间不再下降,或者下降极其缓慢。模型学不到任何东西。
- 梯度爆炸:
- 排查思路:
- 梯度裁剪 (Gradient Clipping): 这是应对梯度爆炸的标准方法。它在优化器更新参数前,检查梯度的范数(norm),如果超过一个阈值,就将其“拉回”到阈值内。
- 运维建议: 与算法工程师确认,是否在训练代码中加入了
torch.nn.utils.clip_grad_norm_。
- 运维建议: 与算法工程师确认,是否在训练代码中加入了
- 权重初始化: 不恰当的权重初始化方法是梯度消失/爆炸的根源之一。
- 检查模型架构: 使用了不恰当的激活函数(如Sigmoid在深层网络中容易导致梯度消失)、或者没有使用Batch Normalization/Layer Normalization等。
- 梯度裁剪 (Gradient Clipping): 这是应对梯度爆炸的标准方法。它在优化器更新参数前,检查梯度的范数(norm),如果超过一个阈值,就将其“拉回”到阈值内。
10.3.3 训练卡死 (Stuck)
这是10.2.2节“通信死锁”在应用层面的表现,但原因可能更广泛。
排查思路:
- 首先排除网络死锁: 按照10.2.2节的方法,检查通信堆栈。
- 检查数据加载: 数据加载进程(
DataLoader的worker)是否卡住了?- 原因: 可能是读取磁盘I/O慢,或者某个worker因为Bug而崩溃。
- 排查: 登录节点,
ps aux | grep python,查看是否有僵尸(zombie)进程。监控磁盘I/O (iostat)。
- 死循环Bug: 训练代码本身是否存在死循环?
- 排查: 使用
py-spy附加到进程,查看哪个函数在无休止地运行。
- 排查: 使用
- 资源竞争: 是否有其他高优先级的进程(甚至是运维自己部署的监控Agent)在抢占CPU或内存资源,导致训练进程“饿死”?
SRE实践总结:
对于训练级故障,AI Infra团队的角色是“赋能者”和“工具提供者”。你需要:
- 提供调试环境: 能够让算法工程师轻松地进入一个出问题的Pod进行交互式调试。
- 提供调试工具: 在基础镜像中预装
py-spy,gdb等调试工具。 - 提供监控数据: 将梯度范数、学习率、Loss曲线等算法层面的指标也纳入监控,与硬件指标关联展示。
- 建立知识库(Knowledge Base): 将每一次故障的原因、排查过程、解决方案都记录下来,形成团队的“错题本”。
通过本章的学习,你不仅掌握了如何处理从硬件到软件的各类典型故障,更重要的是,建立了一套结构化的SRE问题排查思维。这种思维能力,远比记住某一个具体的命令要宝贵,它将是你作为一名顶级AI Infra工程师,在面对未来层出不穷的新问题时,最可靠的依仗。