文章

LLM 分布式推理通信故障治理:用 NCCL RAS、Flight Recorder 与拓扑基线定位集体通信卡死

分布式大模型推理发生 NCCL 集体通信卡死时,往往只剩模糊的超时日志。本文介绍如何用 RAS、Flight Recorder、拓扑基线和异常 Rank 定位,建立可观测、可回滚的通信上线门禁。

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_TIMEOUTwatchdog 异常时输出调试信息
DESYNC_DEBUG定位可能首先发生不同步的 Rank
ENABLE_TIMING记录每个 Collective 的准确时序
ENABLE_MONITORINGwatchdog 自身失去心跳时终止长期占用资源的进程

环形缓冲区大小、是否采集 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,BOOTSTRAPRank 初始化与建联
NET网卡、连接和传输错误
GRAPHNVLink、PCIe 和网络设备局部性
COLLCollective 及其操作序号
TUNINGRing、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. 事故处置必须先保全证据

推荐的自动化顺序:

  1. 网关停止向异常模型实例分配新请求
  2. 给现有请求设置有限排空窗口
  3. 查询 NCCL RAS,并保存全局状态
  4. 触发 ProcessGroupNCCL 协调转储
  5. 收集 NCCL、内核、GPU、NIC、容器和调度事件
  6. 终止并重建整个进程组
  7. 对可疑节点执行通信基线复测
  8. 复测不通过则隔离节点,通过后才允许重新加入资源池

不要让取证无限阻塞恢复。排空、转储和日志上传都需要明确超时,超时后优先释放故障实例占用的 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 和软件版本指纹
3nvidia-smi topo 与 P2P 矩阵符合预期
4nccl-tests 覆盖真实消息尺寸、目标 Collective 和多节点组合
5RAS 可从受控诊断路径查询,且结果能关联模型实例与 Rank
6Flight 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 准入任务。

参考资料

  1. NVIDIA NCCL RAS
  2. NVIDIA NCCL GPU Troubleshooting
  3. NVIDIA NCCL Logging
  4. PyTorch ProcessGroupNCCL Environment Variables
  5. NVIDIA nccl-tests

常见问题

NCCL 超时后把 timeout 调大是否就能解决问题?
通常不能。调大超时只会延后失败暴露,必须先区分集体操作不同步、网络传输停滞、GPU 失联和纯粹的慢通信。
Flight Recorder 与 NCCL_DEBUG 有什么区别?
Flight Recorder 记录 ProcessGroupNCCL 集体操作的开始、结束和 Rank 时序;NCCL_DEBUG 更侧重 NCCL 的初始化、网络、拓扑、算法选择和底层调用日志。
发现一个异常 Rank 后能否只重启对应进程?
多数推理进程组不应这样处理。其他 Rank 仍持有旧通信器状态,通常需要排空请求并重建整个进程组,再决定是否隔离故障节点。
如何判断某台节点是否应该被隔离?
节点在相同容器、相同测试参数下持续偏离同型号拓扑或通信基线,或者出现不可恢复的 P2P、NIC、GPU 错误时,应先隔离。重新入池前需完成硬件检查并重新通过 nccl-tests 准入任务。