LLM 分布式推理通信故障治理:用 NCCL RAS、Flight Recorder 与拓扑基线定位集体通信卡死
为什么分布式推理的”卡死”特别难查
单 GPU 推理出现异常时,错误通常还能落到某个 CUDA 调用、显存分配或模型算子上。进入多 GPU、尤其多节点推理后,故障形态会明显改变:一个 Rank 没有进入预期的集体操作,其他 Rank 可能全部停在同一个 AllReduce;某块网卡发生短暂异常,应用看到的却只是几十秒甚至数分钟后的 watchdog timeout;GPU 进程已经失去响应,控制面仍然认为 Pod 存活。
问题的关键在于——集体通信是一个全局协议。参与者不仅要调用同一种操作,还要在相同顺序、相同通信器和兼容的数据形状下推进。任何一个 Rank 偏离,都会把局部问题放大为整个模型实例不可用。
生产环境中常见的故障至少分为五类:
| 类别 | 描述 |
|---|---|
| 初始化卡住 | Rank 建联、网络接口选择、通信器初始化或 bootstrap 不一致 |
| Collective Desync | 不同 Rank 执行了不同操作,或操作顺序不一致 |
| 传输停滞 | InfiniBand、RoCE、Socket、GPU Direct RDMA 或 P2P 路径异常 |
| 性能退化 | 通信没有失败,但拓扑识别、算法选择或链路带宽偏离基线 |
| 进程或设备失联 | 某个 Rank、GPU 或节点停止响应,其他参与者持续等待 |
只看最后一条超时日志,通常无法区分这五类问题。
核心方法:把三层证据对齐
有效的 NCCL 故障治理不是”把日志级别开到最大”,而是同时保留三类证据:基础设施基线、NCCL 运行态和应用集体操作时间线。
第一层:拓扑与带宽基线
NCCL 会根据 GPU、NVLink、PCIe、NIC 和 NUMA 拓扑选择通信路径。容器里的 /sys 挂载不完整、PCIe ACS 配置变化、GPU 与 NIC 的局部性变化,都可能使同一套模型在不同节点上表现完全不同。
上线前应为每种节点型号生成不可变的通信拓扑指纹,至少记录:
- GPU UUID、PCI Bus ID 和驱动版本
- CUDA、NCCL、PyTorch 和推理引擎版本
nvidia-smi topo -m输出- GPU 两两 P2P 能力矩阵
- GPU 与 NIC、CPU NUMA 节点的亲和关系
- 代表性消息大小下的 NCCL 正确性、延迟与带宽基线
基础检查示例:
nvidia-smi topo -m
nvidia-smi topo -p2p p
nvidia-smi topo -p2p n
仅确认拓扑还不够。应使用 NVIDIA nccl-tests 对实际节点组合执行代表性集体通信测试:
# 单节点 8 GPU,扫描多个消息尺寸并输出逐迭代统计
./build/all_reduce_perf \
-b 8K -e 256M -f 2 \
-g 8 -w 5 -n 50 \
-I 1 -c 1
消息范围应覆盖真实推理负载,而不是只跑一个大消息峰值带宽。对于多节点环境,还应验证节点间组合,避免”每个节点单独健康,但组合后异常”。
基线的价值不只是验收性能,而是让故障发生后能回答:这台节点今天的通信路径,是否仍与发布时一致?
第二层:NCCL RAS 运行态
NCCL 从 2.24 起提供 RAS(Reliability, Availability and Serviceability)子系统。它在 NCCL 进程之间维护轻量健康网络,可在作业运行时查询通信器和进程状态,帮助识别崩溃、挂起和无响应进程。
启用并查询:
export NCCL_RAS_ENABLE=1
export NCCL_RAS_ADDR=localhost:28028
# 查询当前作业状态
echo "verbose status" | nc localhost 28028
在 Kubernetes 中,不建议把 RAS 端口直接暴露到集群外。更稳妥的方式是由同 Pod 的诊断 Sidecar、受控 exec 或节点故障采集器访问,并把查询结果与模型实例、Rank、节点和部署版本关联。
RAS 的作用是回答:
- 哪些进程仍然响应
- 哪些通信器处于异常状态
- 故障是局部 Rank 失联,还是整个进程组停止推进
- 在应用被强制终止前,NCCL 看到的最后全局状态是什么
因此,先采集 RAS,再杀进程,应成为故障处置流程的一部分。
第三层:ProcessGroupNCCL Flight Recorder
RAS 能看到通信器健康状态,但不一定知道业务代码最后执行了哪个 Collective。PyTorch ProcessGroupNCCL 的 Flight Recorder 使用环形缓冲区记录集体操作事件,可在 watchdog timeout 时输出最近的操作时间线。
一个适合预发布和故障增强模式的示例配置:
export TORCH_NCCL_TRACE_BUFFER_SIZE=65536
export TORCH_NCCL_DUMP_ON_TIMEOUT=1
export TORCH_NCCL_DESYNC_DEBUG=1
export TORCH_NCCL_ENABLE_TIMING=1
export TORCH_NCCL_ENABLE_MONITORING=1
export TORCH_NCCL_TRACE_CPP_STACK=0
各参数说明:
| 参数 | 作用 |
|---|---|
TRACE_BUFFER_SIZE | 必须大于 0,超时转储才有可用事件 |
DUMP_ON_TIMEOUT | watchdog 异常时输出调试信息 |
DESYNC_DEBUG | 定位可能首先发生不同步的 Rank |
ENABLE_TIMING | 记录每个 Collective 的准确时序 |
ENABLE_MONITORING | watchdog 自身失去心跳时终止长期占用资源的进程 |
环形缓冲区大小、是否采集 C++ 栈和心跳超时不能直接复制固定值。应在压测环境中根据 Collective 频率、日志体积、故障取证窗口和额外开销进行标定。
Flight Recorder 主要回答:
- 各 Rank 最后执行的 Collective 是否一致
- 哪个 Rank 最先偏离
- 卡住的是初始化、某次 AllReduce,还是后续同步点
- 慢 Collective 是偶发尾延迟,还是持续退化
NCCL 日志应按”常态”和”事故”分层
把 NCCL_DEBUG=TRACE 永久打开并不是可观测性方案。它会产生大量日志,反而可能掩盖关键信号。
常态配置可保持最低必要日志:
export NCCL_DEBUG=WARN
export NCCL_DEBUG_TIMESTAMP_LEVELS=WARN
进入诊断模式后,再按故障类别启用子系统:
export NCCL_DEBUG=INFO
export NCCL_DEBUG_SUBSYS=INIT,BOOTSTRAP,NET,GRAPH,COLL,RAS
export NCCL_DEBUG_FILE=/var/log/nccl/nccl_%h_%p.log
export NCCL_DEBUG_TIMESTAMP_LEVELS=WARN,INFO
不同子系统对应不同问题:
| 子系统 | 排查场景 |
|---|---|
INIT,BOOTSTRAP | Rank 初始化与建联 |
NET | 网卡、连接和传输错误 |
GRAPH | NVLink、PCIe 和网络设备局部性 |
COLL | Collective 及其操作序号 |
TUNING | Ring、Tree、Simple、LL、LL128 等算法与协议选择 |
CALL | 深度排障时跟踪 NCCL API 调用 |
RAS | 运行态健康信息 |
日志文件必须按主机和 PID 分离,并带统一时间戳,否则多个 Rank 的事件无法排序。
工程落地:从发布门禁到故障恢复
1. 建立通信制品指纹
每个分布式模型实例除了模型权重指纹,还应记录一个通信指纹:
communicationFingerprint:
imageDigest: "sha256:..."
cudaVersion: "..."
ncclVersion: "..."
pytorchVersion: "..."
gpuModel: "..."
gpuCount: 8
topologyHash: "sha256:..."
ncclTestProfile: "tp8-baseline-v3"
networkDeviceSet:
- "..."
只要驱动、容器、NCCL、GPU 拓扑、NIC 绑定或 Kubernetes 设备配置发生变化,就重新生成基线,不能默认沿用旧结果。
2. 把 nccl-tests 变成节点准入任务
新节点、维修节点、驱动升级节点和网络变更节点,不应直接加入推理池。准入任务至少验证:
- P2P 能力矩阵符合节点模板
- AllReduce、AllGather 等目标 Collective 正确性通过
- 代表性消息尺寸的延迟和带宽未明显偏离同型号基线
- 多节点链路可建立且无持续重试
/sys、共享内存和网络设备在容器中可正确访问
阈值应来自相同硬件的历史分布,而不是使用跨机型的统一数值。
3. 事故处置必须先保全证据
推荐的自动化顺序:
- 网关停止向异常模型实例分配新请求
- 给现有请求设置有限排空窗口
- 查询 NCCL RAS,并保存全局状态
- 触发 ProcessGroupNCCL 协调转储
- 收集 NCCL、内核、GPU、NIC、容器和调度事件
- 终止并重建整个进程组
- 对可疑节点执行通信基线复测
- 复测不通过则隔离节点,通过后才允许重新加入资源池
不要让取证无限阻塞恢复。排空、转储和日志上传都需要明确超时,超时后优先释放故障实例占用的 GPU。
4. 恢复边界以”进程组”为单位
当一个 Rank 已经离开 Collective 序列,其他 Rank 持有的通信器通常不能靠业务重试恢复。此时不应只重启单个 Worker 并继续承接旧请求。
更安全的恢复边界:
| 级别 | 动作 |
|---|---|
| 请求级 | 失败请求返回可识别的临时错误,由上层决定是否重试 |
| 实例级 | 排空整个模型实例 |
| 进程组级 | 销毁并重建所有 Rank |
| 节点级 | 通信基线失败时隔离节点 |
| 集群级 | 拓扑或网络配置发生系统性漂移时停止扩大故障面 |
监控指标应围绕”推进能力”设计
GPU 利用率并不能说明 Collective 是否正常推进。建议至少采集:
| 指标类别 | 具体指标 |
|---|---|
| 异常事件 | Collective watchdog timeout 次数、Flight Recorder dump 次数和涉及的首个异常 Rank、RAS 报告的无响应进程数 |
| 性能分布 | 每类 Collective 的调用次数和耗时分布 |
| 基线漂移 | nccl-tests 基线相对历史分布的变化、拓扑指纹/网卡选择/算法选择漂移 |
| 恢复事件 | 进程组重建次数、节点隔离次数和复测结果 |
| 业务影响 | 故障期间受影响请求数及排空耗时 |
告警应能区分”通信变慢”和”通信停止推进”。前者可以先降载,后者通常需要快速中止并重建进程组。
适用场景
这套方法适合以下系统:
- Tensor Parallel 或 Pipeline Parallel 的多 GPU 推理
- 跨节点部署的大参数模型
- 使用 PyTorch ProcessGroupNCCL 的推理引擎
- 对尾延迟和实例恢复时间有明确 SLO 的在线服务
- GPU、NIC、驱动或容器版本经常变更的共享集群
单 GPU 推理或完全不使用 NCCL 的系统不需要引入完整方案,但仍可保留节点拓扑和驱动基线。
常见误区
误区一:把 timeout 调大就算修复。 如果根因是 Collective Desync 或 Rank 失联,调大 timeout 只会延长 GPU 被占用的时间。应先判断系统是否仍在推进,再决定超时窗口。
误区二:长期启用 TRACE 日志。 TRACE 适合短时间深度排障,不适合作为默认生产日志。常态应使用 WARN 和轻量 Flight Recorder,事故时再提升 NCCL 子系统日志。
误区三:单节点带宽正常就代表多节点正常。 单节点主要验证 NVLink、PCIe 和 P2P,多节点还涉及 NIC 选择、RDMA、交换网络和 GPU–NIC 局部性,必须分别建立基线。
误区四:发现异常 Rank 后只重启它。 Collective 是全局序列。单独替换 Rank 往往无法恢复其他 Rank 的旧通信器,应重建完整进程组。
误区五:吞吐下降一定是模型或 Kernel 问题。 容器中的 /sys 拓扑、ACS、网卡选择或算法协议变化,都可能让模型代码不变但通信路径退化。
上线检查清单
| # | 检查项 |
|---|---|
| 1 | 最近 30 天主题去重已完成,本文未重复近期技术族 |
| 2 | 每种节点型号已保存 GPU、NIC、NUMA 和软件版本指纹 |
| 3 | nvidia-smi topo 与 P2P 矩阵符合预期 |
| 4 | nccl-tests 覆盖真实消息尺寸、目标 Collective 和多节点组合 |
| 5 | RAS 可从受控诊断路径查询,且结果能关联模型实例与 Rank |
| 6 | Flight Recorder 在预发布环境完成过超时转储演练 |
| 7 | 常态日志与事故日志配置分离,日志按主机和 PID 落盘 |
| 8 | 故障流程遵循”停止分流、有限排空、保全证据、重建进程组” |
| 9 | 节点重新入池前必须通过通信基线复测 |
| 10 | 超时、日志量和缓冲区大小已经过压测标定 |
FAQ
NCCL 超时后是否应该立即调大 timeout? 不应把它作为第一动作。先检查 RAS 是否存在无响应进程,再用 Flight Recorder 判断各 Rank 的 Collective 序列是否一致。如果系统只是稳定但偏慢,才考虑根据历史分布调整超时;如果已经停止推进,应尽快排空并重建进程组。
RAS、Flight Recorder 和 NCCL_DEBUG 应该如何分工? RAS 给出作业与通信器的全局健康状态;Flight Recorder 给出 PyTorch 集体操作时间线和异常 Rank 线索;NCCL_DEBUG 解释底层网络、拓扑、算法和 API 行为。三者需要使用同一实例 ID、Rank 和时间基准关联。
如何判断某台节点是否应该被隔离? 节点在相同容器、相同测试参数下持续偏离同型号拓扑或通信基线,或者出现不可恢复的 P2P、NIC、GPU 错误时,应先隔离。重新入池前需要完成硬件检查、驱动和网络确认,并重新通过 nccl-tests 准入任务。