6.5 多节点部署与 Ray:集群编排与并行度映射
Ray 集群的搭建、vLLM 的分布式执行后端(ray/mp)与自动选择逻辑、多节点启动流程、TP/PP/DP 到进程与节点的映射、常见故障排查
前几节讲的都是”并行策略怎么选”,这一节落地”多卡多机怎么跑起来”。vLLM 的分布式编排依赖 Ray——它负责把 TP/PP/DP 的并行拓扑翻译成”哪台机器、哪张卡、跑哪个进程”。理解这一层,你才能读懂启动日志、排查卡死的通信、以及规划生产集群。
📑 目录
- 1. 为什么是 Ray:编排层解决什么问题
- 2. vLLM 的执行后端:ray / mp / uni / external
- 3. 单节点 vs 多节点的后端选择
- 4. 并行度到进程与节点的映射
- 5. 多节点部署流程:从零启动集群
- 6. 验证与监控:集群是否健康
- 7. 常见故障排查
- 📝 总结
- 🎯 自我检验清单
- 📚 参考资料
1. 为什么是 Ray:编排层解决什么问题
分布式推理的”最后一公里”是把并行配置变成实际运行的进程:
- 每个进程跑在哪台机器、哪张卡上?
- 进程间怎么发现彼此(谁是谁的 TP 组、PP 组)?
- 谁负责拉起、重启、监控这些进程?
自己写这套要处理节点发现、故障恢复、资源调度……所以 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):
| 后端 | 含义 | 适用 |
|---|---|---|
ray | Ray 集群编排 | 多节点部署(默认) |
mp | Python 原生多进程 | 单节点多卡 |
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
(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_DISABLE、NCCL_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 / DCGM | TP 组内卡利用率应相近;单卡 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 out | Ray 未启动 / 端口不通 | ray status;检查 6379 端口;节点间防火墙 |
启动卡在 distributed init | NCCL 网络配置错误 | 检查 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 反复崩溃,你按什么顺序排查?
📚 参考资料
- Ray 官方文档:Ray Cluster 部署(https://docs.ray.io/en/latest/cluster/getting-started.html)
- vLLM 官方文档:Parallelism and Scaling(https://docs.vllm.ai/en/latest/serving/parallelism_scaling/)
- vLLM 官方文档:Engine Arguments(—distributed-executor-backend 等)(https://docs.vllm.ai/en/latest/configuration/engine_args/)
- vLLM 源码:vllm/config/parallel.py(v0.26.0)(https://github.com/vllm-project/vllm/blob/v0.26.0/vllm/config/parallel.py)
- NVIDIA:NCCL 环境变量文档(https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/env.html)