跳到主要内容
推理优化

6.5 多节点部署与 Ray:集群编排与并行度映射

Ray 集群的搭建、vLLM 的分布式执行后端(ray/mp)与自动选择逻辑、多节点启动流程、TP/PP/DP 到进程与节点的映射、常见故障排查

Ray多节点部署集群编排分布式执行器故障排查

前几节讲的都是”并行策略怎么选”,这一节落地”多卡多机怎么跑起来”。vLLM 的分布式编排依赖 Ray——它负责把 TP/PP/DP 的并行拓扑翻译成”哪台机器、哪张卡、跑哪个进程”。理解这一层,你才能读懂启动日志、排查卡死的通信、以及规划生产集群。

📑 目录


1. 为什么是 Ray:编排层解决什么问题

分布式推理的”最后一公里”是把并行配置变成实际运行的进程

  1. 每个进程跑在哪台机器、哪张卡上?
  2. 进程间怎么发现彼此(谁是谁的 TP 组、PP 组)?
  3. 谁负责拉起、重启、监控这些进程?

自己写这套要处理节点发现、故障恢复、资源调度……所以 vLLM 用现成的 Ray。Ray 提供两个关键能力:

  • 集群抽象:多台机器组成一个 Ray Cluster,统一暴露”我有多少张 GPU”
  • Placement Group:把”TP=8 + PP=2 的 16 个进程”打包成一个资源组,Ray 保证它们按指定拓扑(同节点/跨节点)分配

💡 提示:Ray 在 vLLM 里只负责”编排”——进程拉起、资源分配、拓扑放置。真正的张量通信(AllReduce/A2A)走 NCCL,不走 Ray。理解这个分工,排错时就能快速定位:“进程起不来”找 Ray,“起了但通信卡死”找 NCCL


2. vLLM 的执行后端:ray / mp / uni / external

vLLM 的 --distributed-executor-backend 支持四种后端(v0.26):

后端含义适用
rayRay 集群编排多节点部署(默认)
mpPython 原生多进程单节点多卡
uni统一执行器(UNI)v1 引擎内部统一抽象
external_launcher外部启动器第三方编排(K8s 等)自己拉起进程

自动选择逻辑(不显式指定时):

  • 世界大小 = 1(TP×PP=1)→ 单进程,无分布式
  • 世界大小 > 1 且单节点mp(原生多进程,零依赖)
  • 世界大小 > 1 且多节点ray(必须)

📌 关键点:vLLM 文档明确——多节点部署必须用 Ray(除非自己实现 external_launcher)。单节点多卡默认走 mp,不需要装 Ray。所以”装了 vLLM 却起不来多节点”的常见原因之一就是 Ray 没装或没启动。


3. 单节点 vs 多节点的后端选择

3.1 单节点多卡:mp 就够了

# 4 卡 TP,单节点——vLLM 自动用 mp,无需 Ray
vllm serve <model> --tensor-parallel-size 4

mp 的进程管理简单、故障模型直接(进程死 = 服务死),日志都在同一个终端——单节点场景的默认正确选择。

3.2 多节点:必须 Ray

# 2 节点,TP=8 + PP=2——显式指定 ray 后端
vllm serve <model> \
    --tensor-parallel-size 8 \
    --pipeline-parallel-size 2 \
    --nnodes 2 \
    --distributed-executor-backend ray

Ray 的额外价值:节点故障时自动重调度(placement group 重建)、GPU 资源按需求分配、日志聚合。

💡 提示:vLLM v0.26 的一个细节——nnodes > 1 且使用 DP(data-parallel-backend=mp)时有限制:nnodes > 1 is only supported with data_parallel_backend=mp(源码断言)。多节点 + DP 场景需要 --data-parallel-backend ray 或外部 LB 模式。这类”参数组合限制”在生产配置时最容易踩,先查版本文档再配参数。


4. 并行度到进程与节点的映射

这是分布式部署最需要建立的心智模型——并行参数最终变成什么

4.1 进程数 = world size

进程数=world size=TP×PP\text{进程数} = \text{world size} = \text{TP} \times \text{PP}

(DP 不增加单副本进程数,6.1 已讲)

4.2 映射规则(vLLM 自动执行)

2 节点 × 8 卡、TP=8、PP=2 为例:

节点 A(8 卡)               节点 B(8 卡)
┌────────────────────┐      ┌────────────────────┐
│ rank 0-7(TP 组 0)  │      │ rank 8-15(TP 组 1)│
│ = PP stage 0        │  ←→  │ = PP stage 1       │
│ 层 0-39             │ IB   │ 层 40-79           │
└────────────────────┘      └────────────────────┘
  • TP 组:rank 0-7 在同一节点(NVLink 内)——TP 维度最内层
  • PP 组:rank 0 与 rank 8 配对(跨节点)——PP 维度外层
  • rank 编号规则:rank = pp_rank × tp_size + tp_rank

4.3 反过来的排错价值

看到启动日志的 rank 分配,就能验证映射是否符合预期。排查通信卡死时先画这张图:报错的 rank 属于哪个组?它和谁通信?数据该走 NVLink 还是 IB?——这一步能过滤掉一半的误诊。


5. 多节点部署流程:从零启动集群

5.1 节点准备

  • 所有节点:相同 GPU 型号(强烈建议)、相同 CUDA 驱动、vLLM 同版本
  • 节点间网络:IB 或 RoCE 优先,至少万兆以太网(TP 跨节点会很难受,PP 可以)
  • NCCL 环境变量(跨节点通信的关键,网络较差时):
export NCCL_SOCKET_IFNAME=ib0        # 指定通信网卡
export NCCL_IB_DISABLE=0             # 启用 InfiniBand
export NCCL_IB_GID_INDEX=3           # RoCE 场景常见配置

📌 关键点NCCL 网络配置是多节点部署的第一大故障源NCCL_SOCKET_IFNAME 不指定时,NCCL 可能选了错误的网卡(比如管理网口),表现为”通信极慢”或”超时卡死”。容器化部署里还要注意 NCCL_IB_DISABLENCCL_IB_GID_INDEX 等与宿主机网络的配合。

5.2 启动 Ray 集群

# 头节点(head)
ray start --head --port 6379 --num-gpus 8

# 工作节点(每个节点执行,指向头节点)
ray start --address <HEAD_IP>:6379 --num-gpus 8

# 验证
ray status
# 预期看到:节点数、每节点 GPU 数与状态

5.3 启动 vLLM

vllm serve <model> \
    --tensor-parallel-size 8 \
    --pipeline-parallel-size 2 \
    --nnodes 2 \
    --node-rank 0 \        # 每节点上需要分别指定(或用 --nnodes 自动推导)
    --distributed-executor-backend ray \
    --max-model-len 32768

💡 提示:vLLM 会自动发现 Ray 集群里的全部 GPU 并分配 placement group——不需要在每台机器上分别启动 vLLM,只要 Ray 集群是通的,一个 vllm serve 就能拉起跨节点的进程。这是 Ray 编排的核心价值。

5.4 容器化注意(K8s 场景)

  • 每个 Pod 内要能访问 Ray head(--address 指向 service)
  • 共享卷:模型权重放共享存储(NFS/对象存储),各 rank 从同一路径加载
  • 工作目录一致:ray 的 runtime_env 或镜像保持一致,避免”rank 0 能跑、rank 3 缺文件”
  • GPU 分配:K8s 的 device plugin 分配后,Ray 通过 --num-gpus 感知

6. 验证与监控:集群是否健康

6.1 启动期验证

检查项命令预期
Ray 集群状态ray status节点数、GPU 数与预期一致
分布式启动日志vLLM 启动日志出现 Distributed init 成功、world_size=16
KV cache 分配启动日志GPU KV cache size: xxx tokens(各 rank 报告)
NCCL 初始化启动日志NCCL timeout / connection error

6.2 运行期监控

指标工具关注点
每卡利用率nvidia-smi / DCGMTP 组内卡利用率应相近;单卡 100% 其他 30% → 负载不均
网络吞吐nvidia-smi(NVLink 域)、ibstat通信是否走了预期网卡
端到端延迟/吞吐vLLM metrics(Prometheus)与单卡基线对比,验证扩展收益
Ray 日志ray logs / dashboard异常重启、OOM

📌 关键点:扩展性验证是部署的收尾动作——TP 从 1 到 4 应该看到延迟接近 3x 提升,加节点后吞吐应接近线性。如果实测远低于预期,按”算力 → 通信 → 负载均衡”三层次排查(6.7 实战会走一遍完整流程)。


7. 常见故障排查

症状根因方向排查动作
Connection to ray head timed outRay 未启动 / 端口不通ray status;检查 6379 端口;节点间防火墙
启动卡在 distributed initNCCL 网络配置错误检查 NCCL_SOCKET_IFNAME;ping 测试 IB 网卡;nccl-tests 验证
训练能跑、vLLM 起不来Ray 版本不兼容vLLM 对 Ray 版本有要求(见发布说明);pip show ray 对照
某 rank 反复崩溃重启该卡显存不足 / 驱动问题看该 rank 日志;nvidia-smi 检查显存
吞吐不随节点数扩展网络带宽瓶颈 / 负载不均nvidia-smi 看 NVLink 域利用率;ibstat 看 IB 带宽;检查请求均衡
nnodes > 1 报错DP 与多节点组合限制--data-parallel-backend ray 或外部 LB(6.4 第 2 节)

💡 提示:排错的第一原则——先确认拓扑图(4.2 的映射)与日志中的 rank 分配一致,再谈其他。多数”分布式怪问题”最后都归结为:进程没按预期拓扑放置(Ray 侧)或 NCCL 走了错误的网络路径(网络侧)。


📝 总结

  • Ray 负责编排(进程拉起、拓扑放置),NCCL 负责通信——分工清晰,排错按此分头查
  • 四种执行后端:单节点默认 mp,多节点必须 ray,K8s 可用 external_launcher
  • 进程数 = TP×PP,rank = pp_rank × tp_size + tp_rank,TP 锁节点内
  • 部署三步:节点准备(NCCL 网络配置)→ Ray 集群(ray start / ray status)→ vllm serve(并行参数)
  • 验证两看:启动日志(world size、KV cache)+ 运行指标(每卡利用率、吞吐扩展性)
  • 故障三大类:Ray 连接、NCCL 网络、资源(显存/版本)

🎯 自我检验清单

  • Ray 和 NCCL 在 vLLM 分布式部署中各负责什么?
  • 单节点 4 卡 TP 需要装 Ray 吗?为什么?
  • TP=8/PP=2 的 2 节点部署:进程数多少?rank 0 和 rank 8 是什么关系?
  • 说出多节点部署的完整启动流程(节点准备 → Ray → vLLM)
  • NCCL_SOCKET_IFNAME 是干什么的?不配会发生什么?
  • 一个 rank 反复崩溃,你按什么顺序排查?

📚 参考资料