1. 智算集群到底在“听”什么:一次真实的“慢训练”排查经历
1.1 一次十分钟的误判
先讲个我亲身经历的事。去年年中,业务方反馈某训练任务“突然变慢”,准确说是吞吐掉了一半。第一反应是查代码、查数据管道、查IO,折腾了半天没头绪。后来习惯性打开Grafana看了一眼GPU利用率面板,发现8卡里有两卡的SM利用率只有30%左右,其他6卡满载。再翻DCGM指标,这两块卡的NVLink吞吐明显异常,持续在低水平抖动。最终定位到是PCIe链路上的板卡接触不良,重插一次就好了。
这个案例本身不复杂,但它点破了一个容易被忽略的事实:在智算集群里,“慢”是最难排查的症状,因为它可能是业务问题、系统问题、硬件问题、网络问题层层叠加的结果。如果没有基线,你都不知道“慢”是相对于谁慢;如果没有监控,你连“哪一层慢”都看不见;如果没有诊断方法,就算看见异常指标你也得靠猜。所以我一直把Benchmark、监控与AI集群故障诊断这三件事称为智算集群的“听诊器”——不是治病的工具,但没它,你连病在哪都不知道。
1.2 性能、效率、稳定:智算集群的三条生命线
智算集群和普通服务器集群最大的区别在于:它的核心负载是高性能计算和深度学习训练,这类负载对算力、显存带宽、通信带宽的敏感度极高。普通Web服务瓶颈大多在CPU、内存、IO,而AI训练任务动辄上千张GPU/NPU协同,任何一张卡的性能劣化都会拖慢整条流水线。
因此集群运维关注的底层诉求可以拆成三条线:
- 性能:硬件算力是否被完整释放,GPU的FLOPS、显存带宽、NVLINK/RDMA通信能力是否达到标称值。
- 效率:集群资源利用率是否健康,有没有大量“死等”的任务占用着算力却不出活。
- 稳定:故障率是否可控,硬件故障、驱动异常、网络抖动对训练任务的影响能否被快速发现和收敛。
这三条线互相纠缠。性能不达标,效率必然难看;监控做不好,稳定性无从谈起。而要把这三条线都管住,Benchmark、监控、故障诊断缺一不可,它们形成了“基线→观察→诊断→复测”的闭环。
1.3 基线、观察、诊断:一套听诊闭环
我经常把集群运维比作体检。Benchmark就是建立健康指标基线——你血常规正常值是多少,总得先有个标准;监控是持续的体征观察,心跳、血压、体温每天都得记;故障诊断则是当指标偏离基线时,判断是哪里出了问题、严重程度如何。
这个闭环里有一个关键细节很多人会忽略:Baseline不是测一次就完事的静态数据,它是会漂移的。驱动升级、固件更新、板卡老化、网络拓扑调整,都会改变集群的性能基线。所以我会建议,每季度至少做一次完整的Benchmark复测,把新基线和旧基线对比,把漂移趋势找出来。这个话题我们放到后面细讲,先看Benchmark本身。
2. 建立基线:Benchmark到底该测什么、怎么测
2.1 从“能跑通”到“跑得快”:为什么非测不可
新集群上线验收的时候,最常见的做法是拿个模型跑几步,看到Loss下降就宣布“能用”。这其实只回答了一个问题:能不能跑通。但“能跑通”和“跑得快”完全是两码事。我曾经见过一个集群,业务方说“训练正常”,结果一测NCCL AllReduce带宽只有标称值的三分之一,整机性能白丢了一大截,没人察觉,因为业务方根本没有对比参照物。
Benchmark的价值就在这里,它回答三个问题:
- 这台设备达到标称性能了吗——比如A100标称FP16算力312 TFLOPS,实际跑到多少?
- 集群扩展是否线性——1卡、8卡、32卡、128卡,吞吐量的扩展效率是多少?
- 性能变化的趋势如何——同样的测试跑三次,间隔三个月,结果是否稳定?
2.2 硬件层四件套:算力、显存带宽、通信带宽、延迟
硬件层的基准测试,核心是四个维度。这里我直接给出我们内部一直沿用的测试工具和关注指标,方便大家直接抄作业。
| 测试维度 | 工具 | 关键指标 | 关注点 |
|---|---|---|---|
| 单卡算力 | nvidia-smi/torch.cuda.get_device_properties+ 实测算子 | FLOPS利用率 | 是否达到标称的90%以上 |
| 显存带宽 | bandwidthTest、cudaMemcpy实测 | 读写带宽 GB/s | 是否接近理论带宽,掉速说明显存或板卡有问题 |
| P2P/NVLINK | p2pBandwidthLatencyTest | 双向带宽、延迟 | 同一节点内GPU间通信能力 |
| RDMA/网络 | nccl-tests、ib_write_bw | AllReduce带宽、消息传输速率 | 跨节点通信是否正常 |
这里要强调一个经验:不要只看单卡算力,显存带宽和P2P带宽对训练任务的影响往往比FLOPS更明显。很多LLM训练任务的瓶颈不在计算,而在显存带宽——模型参数、梯度、优化器状态频繁在SM和HBM之间搬运。所以测单卡时,我会额外跑一个“FFT或矩阵乘的吞吐”脚本,看实测GFLOPs与理论峰值的百分比,低于85%就得警惕驱动、频率或散热问题。
测试时还要注意环境变量和锁定频率。GPU的Boost频率是动态的,温度一高频率就降,测试成绩自然难看。所以我会在测试前设置nvidia-smi -lgc 1410这类指令锁定频率,保证结果可复现。Benchmark最怕的是每次测量条件不一样,那数据就没有可比性了。
2.3 软件层:单卡吞吐与集群扩展效率
硬件层测完,软件层的基准测试同样重要。因为用户真正感觉得到的是“训练一个epoch要多久”,而不是TFLOPS数字。所以我会在集群上放一个标准的训练吞吐测试模型——一般用ResNet-50或GPT风格的Transformer架构,跑固定步数,统计每秒处理的样本数。
比如一个简单的PyTorch基准脚本核心逻辑是这样的:
import torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset model = nn.Linear(4096, 4096).cuda() dataset = TensorDataset(torch.randn(2048, 4096), torch.randn(2048, 4096)) loader = DataLoader(dataset, batch_size=256, num_workers=8) optimizer = torch.optim.SGD(model.parameters(), lr=0.01) criterion = nn.MSELoss() warmup_steps = 50 total_steps = 200 for step, (x, y) in enumerate(loader): if step >= warmup_steps: torch.cuda.synchronize() start = torch.cuda.Event(enable_timing=True) start.record() x, y = x.cuda(), y.cuda() out = model(x) loss = criterion(out, y) optimizer.zero_grad() loss.backward() optimizer.step() if step >= warmup_steps: end = torch.cuda.Event(enable_timing=True) end.record() torch.cuda.synchronize() elapsed = start.elapsed_time(end) / 1000 throughput = 256 / elapsed print(f"step: {step}, elapsed: {elapsed:.4f}s, throughput: {throughput:.2f} samples/s")集群扩展效率的计算公式也不复杂:
扩展效率 = (N卡时单步吞吐 / N) / (1卡时单步吞吐) × 100%比如1卡能跑500 samples/s,8卡理想情况是4000 samples/s,如果实测只有3200,那扩展效率就是80%。低于85%基本可以断定存在通信瓶颈或负载不均,这时候就该看AllReduce带宽了。
2.4 什么时候该重新测一遍
我建议至少在这四个时间点重测Baseline:
- 新集群上线验收:记录原始性能基线,作为后续所有对比的基准。
- 驱动或固件升级后:驱动版本对GPU性能影响极大,升级后必须验证性能没有回退。
- 硬件变更后:更换板卡、添置节点、调整网络拓扑,都要重测。
- 季度巡检:环境老化、板卡积灰、散热恶化是渐进式的,定期复测才能发现性能漂移。
记住一个教训:如果你没有在集群正常时测过基线,那么故障时你连“异常”都证明不了。这不是夸张,很多“慢训练”工单最后因为缺乏基线数据,只能靠猜。
3. 监控体系:采集哪些指标才能“听见”集群的呼吸
3.1 监控架构:采集层、存储层、可视化层、告警层
Benchmark是“体检报告”,但体检不可能天天做,日常的体征监护就得靠监控体系。现在智算集群监控的主流方案,是Prometheus + Grafana + Alertmanager的组合,这在业内几乎是事实标准。为什么选这套?因为它的数据模型天然适合时序指标,采集端生态丰富,而且社区里已经有大量现成的Exporter可以直接用。
我们集群的监控架构分四层:
- 采集层:
node_exporter采集CPU/内存/磁盘,dcgm_exporter采集GPU指标,ibstat/ethtool脚本采集网络指标,任务调度层面用slurm sacct或k8s kube-state-metrics采集作业状态。 - 存储层:Prometheus时序数据库,根据指标数量和保留周期规划存储容量。
- 可视化层:Grafana看板,按“集群总览、单节点详情、作业维度”三个层次组织面板。
- 告警层:Alertmanager负责规则计算、告警分级、通知路由。
如果是混合异构集群,NPU场景也有对应方案——昇腾有npu-exporter,可以通过dcmi接口采集AI Core利用率、HBM温度、功耗等指标,同样可以接入Prometheus+Grafana。这块生态没有NVIDIA的DCGM成熟,但基本能力是够用的。
3.2 必抓指标清单:GPU、网络、存储、CPU
监控系统最忌讳眉毛胡子一把抓。采集指标前先想清楚:哪些指标能直接反映集群健康度?哪些指标在故障时能帮我们定位根因?只采集“有用的”即可。下面这份清单是我们验证过多次的核心指标,基本覆盖了绝大多数故障诊断场景。
| 类别 | 指标 | 获取方式 | 健康阈值参考 |
|---|---|---|---|
| GPU算力 | SM利用率、Tensor Core利用率 | DCGM_FI_DEV_GPU_UTIL | 训练时稳定在80%~100%;长期低于50%要警惕 |
| GPU显存 | 显存利用率、显存温度 | DCGM_FI_DEV_MEM_COPY_UTIL / DCGM_FI_DEV_MEM_TEMP | 显存利用率波动大属正常;温度不超85℃ |
| GPU功耗 | 当前功耗、功耗上限 | DCGM_FI_DEV_POWER_USAGE | 满载时接近TDP;明显偏低说明任务没吃饱 |
| ECC状态 | 可纠正/不可纠正错误计数 | DCGM_FI_DEV_ECC_DBE / SBE | 不可纠正错误必告警 |
| NVLink/P2P | NVLink收发吞吐 | DCGM_FI_DEV_NVLINK_BANDWIDTH | 训练中应持续有流量;降到0可能是链路down |
| RDMA网络 | 端口收发速率、丢包、重传 | ibstat/ethtool -S | 丢包为0;重传率高会影响NCCL性能 |
| CPU/内存 | 使用率、负载 | node_exporter | 训练节点CPU利用率不宜长期100%,可能是数据加载瓶颈 |
| 存储 | IOPS、吞吐、延迟 | node_exporter / 存储系统API | 训练启动和checkpoint阶段IO会突增,持续高延迟要注意 |
3.3 看板设计:一张能“一眼定位”的Grafana面板
看板这东西,做得好的能救命,做得烂的就是装饰品。我调试过太多团队做的GPU看板,打开全是花花绿绿的折线图,但根本没法回答问题:“我的任务现在卡在哪?”
我自己的看板设计原则是:按故障排查路径来组织面板。
第一层是全集群总览。一屏放下GPU总利用率、空闲卡数、故障卡数、作业排队数、RDMA总吞吐。值班同学扫一眼就知道集群健康状况。
第二层是单节点下钻。点开某个节点,能看到8张卡的利用率、温度、功耗、NVLink吞吐、ECC错误计数、InfiniBand端口状态。这些指标并排展示,方便对比“卡与卡之间的差异”——训练任务多卡性能应该基本一致,某张卡明显偏低就是问题信号。
第三层是作业维度。以Slurm作业ID为标签聚合GPU指标和网络指标,看单个任务的生命周期曲线。训练任务跑到一半突然吞吐下滑,看作业维度的曲线能直接判断是通信问题还是单卡降频问题。
这里有个非常实用的小技巧:给Grafana面板加上$job_id和$node_name这类模板变量下拉框,值班时就能快速切换视角——“点一下作业ID,它的所有卡指标全出来”。这个功能很多人没用起来,实际上排查效率的差距就在这里。
3.4 告警规则:怎么定阈值才不会“狼来了”
告警规则是监控体系里最考验经验的部分。阈值太敏感,天天被噪音轰炸,最后没人看告警;阈值太迟钝,真出事时告警变成“事后通知”,意义就没了。
我的经验是三个原则:
- 持续条件优先于瞬时条件。不要用“GPU温度>85℃”这种瞬时条件直接告警,而是配合
for: 5m,让Prometheus在条件持续5分钟后才触发。偶发瞬时尖峰多半是正常波动,持续偏离才是问题。 - 分级处理而非一刀切。Warn级别只通知值班群,Critical级别才发短信/电话。比如“显存利用率下降”是Warn,但“GPU Xid错误出现”就是Critical。
- 告警必须可执行。每一条告警都应该对应一个明确的处理动作,否则就不要发出来。例如“GPU利用率连续15分钟低于30%”的告警发生后,值班同学的下一步动作是查该节点是否有任务在跑、任务是否处于等待状态、NCCL是否阻塞。
之前见过一个团队给每块GPU设置了“利用率低于10%就告警”的规则,结果半夜频繁误报——因为夜间低峰期本来就没任务。后来加了一个条件“仅当该节点有运行中的训练任务时检测”,噪音立刻就消失了。
4. 故障诊断:监控数据如何变成根因结论
4.1 诊断方法论:从表象指标、状态指标到日志指标
有了监控数据,不等于会诊断。我把故障诊断的思路分成三个递进层次:
- 表象指标:用户可见的异常,比如训练中断、吞吐下降、Loss出现NaN。这一层只能告诉我们“出事了”。
- 状态指标:监控系统里的各项指标快照,比如GPU利用率、温度、功耗、网络重传率。这一层能告诉我们“系统哪里不对劲”。
- 日志指标:驱动日志、系统日志、应用日志里吐出的具体错误信息,比如Xid错误码、NCCL超时报错、dmesg里的PCIe报错。这一层才是根因证据。
诊断的过程,就是从表象出发,通过状态指标缩小范围,最终在日志里找到决定性证据。整个过程像剥洋葱,切忌跳层直接下结论。
4.2 案例一:GPU掉卡与ECC错误——先从Xid和dmesg入手
现象:训练任务随机中断,有时报“CUDA error: an illegal memory access was encountered”,有时直接整卡从nvidia-smi消失。
排查路径:
# 1. 查看GPU状态和Xid错误 nvidia-smi nvidia-smi -q -d ECC # 2. 查看显存ECC错误计数,确认是否有不可纠正错误 nvidia-smi -q -d ECC | grep -A 5 "Volatile" # 3. 查内核日志和GPU错误事件 dmesg | grep -i xid dmesg | grep -i nvidiaXid是NVIDIA驱动上报的错误码,常见的有79(GPU fell off the bus)、13(图形引擎异常)、43(设备被驱动移除)等。如果dmesg里刷出大量Xid 79并伴随“link is down”字样,基本可以判断是板卡物理链路问题,优先检查PCIe插槽、供电线缆、板卡金手指。
ECC错误分两种:Single-bit(可纠正)和Double-bit(不可纠正)。单比特错误偶发无碍,但如果错误计数持续增长,说明显存颗粒正在退化,这块卡迟早要换。查ECC时一定看Volatile和Aggregate两个值的变化趋势,单次快照意义不大。
4.3 案例二:NCCL通信超时——先怀疑网络,再怀疑配置
现象:多机训练时任务频繁卡死,日志里报NCCL ERROR: Timeout,重试偶尔能过,但越来越频繁。
排查思路:
# 1. 检查RDMA端口状态和物理层信息 ibstat ibstatus # 2. 查看交换机端口计数,关注丢包和重传 ibdiagnet -c # 3. 查看网卡错误计数 ethtool -S ens6f0 | grep -i err # 4. 用nccl-tests实测跨节点通信带宽,对比基线 ./build/all_reduce_perf -b 128M -e 8G -f 2 -g 32NCCL超时的根因非常多。链路不稳定的表象是丢包和重传。之前遇到过一个案例:某节点InfiniBand链路物理层状态正常,但ibstat显示Rate: 40(应为100或200),说明链路协商降速了。后续查到是光模块老化,更换后NCCL测试带宽立刻恢复。还有一次是Mellanox网卡固件版本和交换机不兼容,导致大规模AllReduce时频繁超时。所以我的建议是:所有NCCL超时问题,第一步不是改NCCL参数,而是检查物理层链路质量和交换机配置。
4.4 案例三:性能劣化与功耗墙——降频问题最容易“装傻”
现象:训练任务刚开始正常,跑了几个小时后整体变慢,看GPU利用率仍接近100%,但单步耗时拉长了一倍。
这类问题最容易迷惑人,因为你想当然地以为“利用率这么高,GPU在满负荷工作”。但真实原因是:GPU达到功耗墙或温度墙后自动降频,你看利用率没变,但它跑的实际频率从1.4GHz掉到了0.7GHz,算力直接腰斩。
排查路径:
# 1. 看当前频率 nvidia-smi -q -d CLOCK # 2. 看降频原因,重点关注Power和Thermal nvidia-smi -q -d PERFORMANCE # 3. 看温度曲线,确认是否触及温度上限 curl -s http://localhost:9400/metrics | grep DCGM_FI_DEV_GPU_TEMP # 4. 确认是否撞功耗墙 nvidia-smi -q -d POWER这个案例的处理方式是优化机房散热、清理GPU散热器积灰、检查风扇转速策略。硬件老化、工作环境变化导致的降频,性能衰减是渐进式的,只能靠定期Benchmark复测来发现。
4.5 诊断工具清单:别等故障时才开始学
诊断工具不用太多,顺手就行。下面是我最常用的组合:
| 工具 | 用途 | 使用频率 |
|---|---|---|
nvidia-smi | GPU状态快照(利用率、温度、功耗、频率) | 每天 |
nvidia-smi dmon | GPU指标连续采样,观察实时波动 | 故障时 |
dcgmi diag | GPU硬件深度自检(显存、PCIe、NVLink等) | 故障时/季度巡检 |
ibstat/ibdiagnet | InfiniBand链路状态与端口错误 | 网络故障时 |
ethtool -S | 以太网/RoCE网卡错误计数 | 网络故障时 |
dmesg | 内核和驱动错误日志 | 故障时必查 |
npu-smi info | 昇腾NPU状态查看 | NPU集群日常 |
| Prometheus + Grafana | 长期指标趋势分析 | 日常 |
这里特别提一下dcgmi diag。它的运行级别值得大家记住:-r 1是快速检查,-r 2包含显存压力测试,-r 3是更深度测试但耗时很长,-r 4会直接修改GPU状态需要谨慎使用。处理在线故障时先用-r 1快速判断,确认疑似硬件问题后再用-r 2跑一遍。
5. 落地心得:从“能监控”到“用好监控”
5.1 指标不是越多越好,采集密度更要克制
刚接触Prometheus的人容易犯一个幼稚的错误:看见什么Exporter都要接,指标拉了几百上千条,最后看板密密麻麻全是线,真到故障时反而找不到重点。
我的原则是:每个故障场景只需要5~8个核心指标就能锁定范围,指标数量再多就是噪音。而且采集密度也需要克制,GPU指标5秒一次足够了,训练任务又不需要毫秒级采样。采样太密会导致存储占用飙升,查询变慢,后端撑不住。
以300节点、2400卡规模的集群为例,如果按5秒采样、15天保留期规划,Prometheus的数据量大约需要1.5~2TB存储。如果采样密度翻倍,存储成本也跟着翻倍,但排查问题的效率并不会因此提高——时序曲线趋势都一样,采样稀疏一些反而更平滑直观。
5.2 告警要能“自愈”,别让值班同学变成复读机
监控体系建好之后,下一个要解决的问题是告警疲劳。一个成熟的集群,告警应该分级处理,能自动恢复的就别打扰人。
我在前面提到的几个案例做过自动化处理:
- GPU进程异常退出,由调度器自动重启作业,最多重试两次。
- NCCL超时偶发一次不影响任务,只记录不告警;连续三次才对业务方告警并通知值班。
- 检测到节点温度偏高,先自动加快风扇转速(部分支持IPMI的硬件),持续30分钟仍未下降才告警。
- 单卡ECC软错误计数缓慢增长,不直接告警,而是在季度巡检时标记“建议更换”。
做过自动化后,集群告警量至少降了一半,值班同学终于有时间去处理真正需要人介入的问题。告警不是目的,减少告警才是目的。
5.3 故障复盘模板:把每一次事故变成“知识资产”
智算集群的故障排查,最大的浪费是“同样的坑踩了两次”。所以我会强烈建议团队维护一份故障复盘文档,格式固定,写清楚五件事:
- 时间线:从故障发生到恢复的完整时间线,精确到分钟。
- 影响范围:哪些作业受影响、损失多少GPU时(GPU Hours)。
- 表象与定位:用户看到了什么,监控查到了什么,最终根因是什么。
- 处置动作:做了什么操作让系统恢复,哪些措施无效。
- 预防改进:为阻止复发,要补哪些监控项、改哪些配置流程。
这份文档的价值,不是事后追责,而是把个人经验变成团队资产。新同学接手集群运维时,把历史故障复盘翻一遍,比自己摸爬滚打快得多。我们内部已经积累了两百多篇这样的复盘,现在的故障平均修复时间比头一年压缩了接近三分之二。
5.4 最后再聊几句心里话
做智算集群运维这几年,我最大的感受是:工具永远只是辅助,真正救命的是一套清晰的流程和对集群脾气的理解。Benchmark、监控、故障诊断这三件事,本质上都是在回答同一个问题——你的集群现在到底处于什么状态。把基线建好,把看板调好,把复盘做好,集群的稳定性自然会上一个台阶。
最后再分享一个私有经验:每次大版本驱动升级前,一定先在一个非业务节点上跑一遍完整的Benchmark和dcgmi diag,确认没有问题再批量执行。别问我是怎么知道这个教训的。