vLLM与Ray怎么选?大模型分布式部署的架构分析与实战指南 前阵子有个朋友跑来找我说手上有两台八卡 A800想用 vLLM 部署一个 70B 的模型问我是不是得上 Ray。这个问题我最近被问了很多次所以干脆把思路完整写出来。先说结论Ray 和 vLLM 根本不是一个层面的东西。vLLM 是推理引擎负责把显存里的权重高效地变成 tokenRay 是分布式框架负责在多台机器之间调度资源、管理进程、做弹性伸缩。两者可以配合但“两台机器跑 vLLM”并不天然等于“需要 Ray”。实际上我见过的很多场景里上 Ray 不仅没有解决问题反而因为多了一个控制平面把问题变复杂了。什么时候真的值得引入 Ray关键要看模型能否被单机容纳、流量是否波动、是否需要多个模型共享同一批 GPU这三个问题想清楚答案基本就出来了。这篇文章我会把 vLLM 的分布式范式、Ray 的实际价值、两条技术路线的取舍、以及我自己在真实部署中踩过的坑都聊一遍最后给出一个可以直接照着判断的决策清单。1. vLLM 和 Ray先把“并行”这件事拆开讲很多人把“多机部署”和“用 Ray”混为一谈这其实是因为没有搞清楚 vLLM 的并行机制和 Ray 的调度机制分别解决什么问题。要做出合理的架构选型第一步就是把这两个概念拆开。1.1 vLLM 自带的分布式TP、PP、DP 到底在切什么vLLM 的分布式推理有三种基本模式它们切分模型的方式完全不同对网络的要求也完全不同。**张量并行TP**是把一个权重矩阵切开比如把 8192×8192 的矩阵切成两块 8192×4096分别放在两张卡上。forward 过程中每一层做矩阵乘法之后都需要一次 all-reduce把分散在每张卡上的分片结果合并回完整张量。这种并行方式通信量最大、延迟最敏感但好处是能跑单卡放不下的超大模型。TP 主要靠节点内的高速互联NVLink、NVSwitch、PCIe Switch来支撑跨节点做 TP 会非常考验网卡带宽。**流水线并行PP**是按层切分。比如 60 层 transformer1-20 层放卡 A21-40 层放卡 B41-60 层放卡 C。数据像流水线一样从第一组层流到最后一组层通信量远小于 TP但会有流水线气泡问题——前一卡在算的时候后一卡可能在等待。PP 对网络要求低一些但 vLLM 的推理场景下用得不如 TP 高频因为单个请求的延迟会被流水线步数放大。**数据并行DP**最简单它不切模型而是复制模型。两台机器各放一份完整的模型权重各跑各的请求互不干扰。吞吐可以随副本数量几乎线性扩展通信只发生在最外层的负载均衡器上后端实例之间基本不需要通信。DP 是“两台机器”这个规模下被低估得最严重的一种方案。并行方式切分对象通信频率对网络要求典型延迟影响张量并行 TP权重矩阵每层一次 all-reduce极高依赖 NVLink影响单请求延迟流水线并行 PPTransformer 层每层组一次传输中等流水线气泡数据并行 DP不切模型复制权重几乎无后端通信极低不影响单请求延迟这里有一个关键认知vLLM 本身就能做跨节点的 TP不是必须依赖 Ray。vLLM 提供了--tensor-parallel-size和--pipeline-parallel-size参数也提供了--distributed-executor-backend参数这个参数可以选mp多进程或rayRay 集群。也就是说Ray 在这里更像是一个“工人管理器”它帮你管理那些分布在不同机器上的 worker 进程但真正传输梯度/激活值的还是 NCCL。1.2 Ray 不是推理引擎是“GPU 资源的操作系统”Ray 是一个通用的分布式计算框架它解决的问题是你有若干台机器、若干块 GPU怎么让不同任务公平、高效地使用这些资源。你可以把 Ray 理解成 GPU 集群的操作系统——它负责进程调度、资源记账、故障恢复、对象存储甚至通过 Ray Serve 做模型服务化。vLLM 和 Ray 的关系可以类比成“发动机”和“整车控制系统”。发动机负责把燃料转化为动力但要不要换挡、要不要自动巡航、多个司机怎么协调这是整车控制系统的事。在部署推理服务的语境下Ray 的价值主要体现在几个方向把多台机器的 GPU 抽象成一个资源池任务申请几块卡就分配几块卡。通过 Ray Serve 把模型封装成 deployment支持按请求量自动扩缩容。某个 worker 崩溃后自动重启减少单点故障带来的业务影响。和其他 Ray 生态组件Ray Data、Ray Tune无缝配合。但这也意味着如果你压根不需要“资源池”这个抽象不需要自动扩缩容也不在乎故障自愈那 Ray 带来的价值就很有限反而会引入额外的复杂度。1.3 “两台机器”这个规模下的真实约束“两台机器”是一个很微妙的规模。它比单机多了一个网络跳点但又不至于像几十台机器那样必须依赖统一调度系统。在这个规模下拓扑结构往往比框架选型更重要。首先要清醒认识到两台机器之间的物理链路带宽。如果是 25GbE理论带宽也就 3GB/s 左右如果是 400G 的 RoCE 或 InfiniBand可以到 50GB/s。但即便是 50GB/s和单机 NVLink 动辄 600GB/s 以上的聚合带宽相比仍然差一个数量级。所以跨机器的通信一定要尽量少或者干脆避免。其次是资源数量。两台机器就算都是八卡也就 16 块 GPU。这么小的资源池手工排布完全可以搞定比如“这台机器跑模型 A那台机器跑模型 B”。在这个规模下Ray 的调度优势并不明显除非你打算频繁动态地调整任务分布。我个人的判断是两台机器的首要任务是“把网络拓扑和模型尺寸这件事算清楚”而不是“选一个分布式框架”。框架是为拓扑服务的。2. 两台机器常见的三种部署路线先不要急着讨论 Ray把两台机器能走的路线都摆出来。其实绕来绕去就是三种组合方式。2.1 路线 A单机 TP 双副本 DP最常见也最稳这个路线是每台机器内部用 NVLink 做张量并行把模型放进一台机器的显存里然后两台机器各跑一个独立的 vLLM 实例也就是跑两个模型副本在最前面用一个负载均衡器把请求分到两个实例上。举个例子每台机器 8 张 A800 80G要部署一个 70B INT8 模型。70B 参数用 INT8 量化后大约 70GB 权重如果用单机 8 卡每张卡只需要放大概 9GB 权重还有大量 KV cache 空间。这种情况下单机 TP8 完全没问题。两台机器各跑一个副本QPS 直接双倍。这个方案之所以好是因为它把“跨机通信”彻底从推理路径上拿掉了。vLLM 的两个实例之间零通信前端负载均衡器只需要按轮询或连接数分发请求。负载均衡这一层用 Nginx 就能解决问题upstream vllm_backend { least_conn; server 192.168.1.10:8000 max_fails2 fail_timeout5s; server 192.168.1.11:8000 max_fails2 fail_timeout5s; } server { listen 8001; location / { proxy_pass http://vllm_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_http_version 1.1; proxy_set_header Connection ; proxy_read_timeout 300s; } }注意proxy_read_timeout要设置得足够大因为长文本生成往往超过几十秒默认 60 秒很容易断流。路线 A 的适用前提非常明确模型必须能放进单台机器的显存。如果不能那就只能考虑路线 B 或者降级量化。2.2 路线 B跨机器 TP 跑一个巨型模型当模型大到一台机器放不下时就必须要跨机器切权重了。比如一个 671B 的 MoE 模型即便做了 INT8 量化权重也有几百 GB单机 8 卡放不下这时候就只能把 TP 扩展到跨机。先说结论跨机器 TP 在 vLLM 里能跑vLLM 的--tensor-parallel-size16会把 16 张卡组织成一个逻辑实例但性能高度依赖节点间网络。如果两台机器之间只有 25GbE那每次前向传播里的 all-reduce 都会成为瓶颈。我们可以简单估算一下通信量。假设模型 hidden size 是 8192一次 decode 的 batch 是 64 个 token那么每层需要做 all-reduce 的张量大小大致是 64 × 8192 × 4 字节 × 2双向≈ 4MB。一个 60 层的模型就是 240MB。在 25GbE 网卡上NCCL 实际有效带宽可能只有 2GB/s 左右光这 240MB 的 all-reduce 就要 120ms 以上。而如果是在单机 NVLink 环境同样 240MB 只需要不到 1ms。差距就是这么悬殊。batch 越小跨机通信的影响越小batch 越大跨机 TP 就越不划算。所以跨机器 TP 更适合低并发、单请求延迟要求不那么极端的场景。如果目标是把吞吐拉满那路线 A 的双副本 DP 方案在绝大多数情况下都比跨机 TP 强。另外要说明的是vLLM 跨机 TP 既可以用mp后端也可以用 Ray 后端。用 Ray 后端的好处是你不用手动在两台机器上各起进程、手动设置 NCCL 的通信地址Ray 会帮你把 worker 调度好。所以“跨机 TP”和“用 Ray”经常被绑定在一起但逻辑上它们是两回事。2.3 路线 CRay Serve 做多模型弹性平台还有一条路线不是为某一个模型服务而是为了“多模型 弹性”服务。假设你这两台机器不只是跑一个 70B还要跑一个 8B 的小模型、一个 embedding 模型而且流量波动很大那手工管理就会变得很烦。这时候 Ray Serve 就值得认真考虑。用 Ray Serve 部署 vLLM可以写成这样from ray import serve from vllm import LLM, SamplingParams serve.deployment( num_replicasauto, max_ongoing_requests20, ray_actor_options{num_gpus: 4}, ) class VLLMDeployment: def __init__(self): self.llm LLM(model/models/qwen-70b, tensor_parallel_size4) async def __call__(self, req): prompt req[prompt] result self.llm.generate( [prompt], SamplingParams(max_tokens256) ) return {output: result[0].outputs[0].text} entrypoint VLLMDeployment.bind()这里有个很容易踩的坑ray_actor_options{num_gpus: 4}和tensor_parallel_size4必须匹配。如果你只给了num_gpus1但 vLLM 内部默认占满所有可见 GPURay 的资源记账就完全失真后面再有任务调度时会出现资源冲突。我见过有人在这个问题上卡了一整天。路线 C 的优势是模型可以按需被加载到空闲 GPU请求入口统一副本数可以自动伸缩。劣势也很明显架构复杂调试困难且部署成本远高于前两条路线。3. 六个“真需要 Ray”的判断信号讲了这么多到底什么时候才值得引入 Ray我把自己的判断标准总结成六个信号命中一个以上才值得认真考虑它。3.1 模型超出单机显存而且不是“一次定型”如果模型固定、不会经常换那即便超出单机显存你也可以手工配置跨机 TP甚至写一个脚本在启动时自动设置好网络参数。真正需要 Ray 的场景是模型不固定今天跑 A明天换 B后天又调一个 C 的量化版本。这种“频繁试错”的场景里Ray 的资源调度能让你不用每换一个模型就重新手动安排进程。不过要泼一盆冷水如果你只是偶尔换模型手工改一下 vLLM 启动参数也就几分钟的事。这个信号单独成立时优先级并不高。3.2 多个模型要共享同一个 GPU 池两台机器 16 块 GPU模型 A 要 6 块模型 B 要 2 块模型 C 要 4 块。手工安排也能做但一旦你有“动态调整某个模型的 GPU 数量”的诉求Ray 就有用了。Ray 的资源约束机制能确保一个 deployment 申请的 GPU 数量不会和另一个 deployment 冲突。我在实践中的体会是多模型共存的真正痛点不是“放不放得下”而是“谁来分配”。如果没有 Ray你可能得自己写一个资源分配表然后手动启停进程。这个表一旦超过 3 行就会开始出错。3.3 流量波动大需要自动扩缩容Ray Serve 支持num_replicasauto也就是根据队列长度、请求延迟等指标自动增减副本。如果你的服务有明显的早晚高峰比如白天 QPS 500凌晨 QPS 10那自动扩缩容确实能帮你省下不少 GPU 占用的成本。但要注意vLLM 实例启动需要加载权重一个 70B 模型的加载时间可能要一两分钟。Ray 的自动扩缩容面对这种“冷启动”场景伸缩速度并没有想象中那么快。最好配合最小副本数一起使用别把自动扩缩容当成魔法。3.4 需要故障恢复与自愈单点故障是很多团队忽略的问题。如果你的 vLLM 进程崩了Nginx 负载均衡会把请求转到另一个副本影响还算可控但如果你只有一个大模型实例在跨机跑进程一崩整个服务就断了。Ray 可以在 worker 崩溃时重新拉起 actor理论上能做到一定程度的自愈。不过vLLM 本身也会做超时重试、健康检查。Ray 的故障恢复能力属于“锦上添花”只有在你对服务可用性有明确 SLO 时才算刚需。3.5 已经有 Ray 集群在跑其他任务这是个很现实的信号。如果你们公司已经在用 Ray 跑数据处理、强化学习、离线批处理那让 vLLM 也接入同一个 Ray 集群资源池就能复用运维体系也能统一。这种情况下为 vLLM 单独引入 Ray 的成本已经很低了价值却是联合的。反过来如果只是为了 vLLM 而专门搭一套 Ray就要重新评估上面几条是否值得。3.6 需要跨机多卡统一调度但不想手动管理 NCCLvLLM 的跨机 TP用--distributed-executor-backendray确实能省掉不少手动配置的麻烦。比如分布式 worker 的启动顺序、NCCL 通信组的初始化、失败重试Ray 都替你处理了。如果你打算把--tensor-parallel-size扩展到跨机用 Ray 后端是一个比较稳妥的选择。不过这里有个细节很多人没注意即便用 Ray 后端跨机通信的物理瓶颈依然在网卡上Ray 只解决“进程管理”不解决“带宽不够”。别指望加个 Ray 就能让 25GbE 的跨机 TP 变快。判断信号强烈程度说明模型多变频繁换模型中手工配置也能应付但 Ray 更省心多模型共享 GPU 池高资源分配是真正的调度问题流量波动大中高冷启动时间长需要配合最小副本故障自愈需求中有 SLO 要求才算刚需已有 Ray 生态高复用资源池边际成本低不想手管跨机 NCCL中依赖网络Ray 只是简化管理4. 五个“别急着上 Ray”的预警信号说完什么时候该用也得说说什么时候千万别用。以下五个情况我每次都会先劝对方冷静。4.1 只有一两台固定机器、模型固定架构越简单越容易排障。一台机器能放下模型就一台机器跑两台能放副本就两台一起跑。固定模型 固定机器这种场景Ray 的动态调度根本没有用武之地反而让问题排查多了一层。我见过一个团队为了部署一个 13B 模型就把 Ray 集群搭上了结果模型才占 2 张卡集群每天要维护 head 节点日志系统也变得复杂。这个投入完全没必要。4.2 流量平稳峰值可控自动扩缩容的本质是应对波动。如果你的流量曲线像一条直线峰值也在设计范围内那就直接把实例数量固定用 vLLM 原生参数跑就行。多出来的自动伸缩逻辑只会增加不确定性和故障面。4.3 Nginx 或 Gateway 就能解决流量分发前面已经写过 Nginx 的配置。如果你需要的是“把请求分发到多个 vLLM 实例”那用 Nginx、Envoy、LiteLLM 或者 vLLM 自带的 OpenAI 兼容入口就足够了。这类工具轻量、稳定、任何人都看得懂。只有当请求路由逻辑复杂到“某个模型按某个规则打到某组 GPU”时才轮到 Ray Serve 出场。4.4 模型能通过量化塞进单卡这个永远是最优先考虑的优化方向。一个 70B 模型FP8 量化后大约 70GB勉强塞进 80GB 单卡INT4 量化后大约 35GB单卡绰绰有余。如果量化能把模型从“两台机器才能跑”变成“一台机器跑两个副本”那性能反而可能更好。因为避免了跨机通信单卡吞吐未必比双卡 TP 差。当然量化会带来精度损失这一点需要结合业务评估。但从工程角度讲先量化后并行是性价比最高的路径。4.5 团队没有 Ray 的运维经验这可能是最现实的一条。Ray 集群本身需要维护head 节点挂了怎么办worker 之间网络隔离怎么做Ray Dashboard 怎么看这些都需要经验。如果一个团队的运维能力只停留在“会跑 docker、会拉日志”的层面那引入 Ray 之后排障成本会成倍增加。我在项目里遇到过多次vLLM 报错后第一反应是查 Ray 的日志但日志分散在不同的 worker 上根本没有头绪。相比之下直接用 vLLM 原生模式所有日志都在当前终端或系统 journal 里排障效率完全不是一个级别。5. 实操对比同一个 70B 模型三种方案的实测差异为了把上面的抽象讨论落到地上我用一个具体的 70B 模型案例给出三种方案的实操配置和预期差异。5.1 硬件与模型前提机器2 台 8 卡 A800 80G网络25GbE注意这里不是 NVLink over network模型DeepSeek-R1-Distill-Qwen-70BINT8 量化后约 70GB单机显存8 × 80G 640G完全能容纳5.2 方案 A无 Ray双副本 负载均衡启动命令非常朴素# 机器 1 vllm serve /models/qwen-70b-int8 \ --tensor-parallel-size 8 \ --port 8000 # 机器 2 vllm serve /models/qwen-70b-int8 \ --tensor-parallel-size 8 \ --port 8000然后前面挂 Nginx 或 Envoy。这个方案里每个请求只会经过一台机器不存在跨机通信延迟完全由单机 8 卡 NVLink 决定吞吐量则翻倍。实测下来单实例 QPS 大约 20-40双实例就是 40-80取决于 prompt 长度和生成长度。优点是部署简单任何一个能看懂命令的人都能复现。缺点是如果 70B 模型放不进单机这个方案就完全失效。5.3 方案 B无 Ray跨机 TP16 单实例这是勉强为之的方案。命令看起来也不复杂vllm serve /models/qwen-70b-int8 \ --tensor-parallel-size 16 \ --distributed-executor-backend ray前提是你要先启动 Ray 集群并把两台机器都加入。启动之后 vLLM 会把 16 张卡组成一个 TP 组。问题在于每一层 forward 结束时都要做一次跨机 all-reduce通信量前面已经算过。对于 batch1 的单用户请求影响可能还能接受一旦 batch 拉高到 32 或 64吞吐瓶颈会非常明显。我的实测感受是这个方案在 25GbE 网络环境下的峰值吞吐甚至不如方案 A 的单机实例。跨机 TP 适合的是“模型大到单机装不下”且“并发不需要太高”的场景在 70B 这个量级它往往不是最优解。5.4 方案 CRay 集群 Ray Serve把两台机器的 16 卡交给 Ray然后用 Ray Serve 部署一个 deployment每个副本占用 8 卡跑一个完整的 70B 副本。这样 Ray 可以做故障恢复也能控制副本数量必要时缩到 1 个、扩到 2 个。一条比较合理的设计是两个最大副本数都设为 2最小副本数设为 1这样流量低时只占 8 卡流量高时自动扩到 2 个副本占满 16 卡。但要注意 vLLM 冷启动加载权重的速度如果你设置的最小副本数为 0那流量突然飙升时新副本可能要等一两分钟才能接流量客户端很可能已经超时了。对比项方案 A双副本 Nginx方案 B跨机 TP16方案 CRay Serve部署复杂度低中高单请求延迟低单机 NVLink高跨机 all-reduce低单机 NVLink峰值吞吐高双实例并行中低受网络限制高动态伸缩故障恢复依赖 Nginx 健康检查单点风险Ray 自动拉起动态扩缩容不支持不支持支持适合场景模型能装进单机、流量稳定超大规模模型、低并发多模型、流量波动、需要调度5.5 我的选型建议顺序现在遇到类似场景我的思考顺序基本固定了模型能不能通过量化塞进单机能就直接单机跑两台机各跑一个副本Nginx 分发。模型必须跨机 TP先确认两台机器之间的网卡类型和带宽。如果只有 25GbE慎重如果有 400G 高速网络可以试。是不是有频繁换模型、动态调资源需求有再上 Ray Serve。这个顺序看起来平淡但真的能避免 90% 的过度设计问题。6. 部署前必须知道的五个坑最后把这些年踩过的坑集中写一下这些内容翻官方文档不一定能找到但在实际部署中几乎都会遇到。6.1 网络接口要提前锁死跨机 TP 时NCCL 经常找不到正确的通信网卡。如果你的机器有多个网卡比如一个管理网口、一个业务网口NCCL 默认走的可能是慢的那个。这里建议设置两个环境变量export NCCL_SOCKET_IFNAMEeth1 export NCCL_IB_GID_INDEX3NCCL_SOCKET_IFNAME指定走哪块网卡NCCL_IB_GID_INDEX针对 InfiniBand 场景设置 GID index。不设置的话轻则速度慢重则直接报Connection refused。6.2 Ray 集群和 vLLM 的版本要对齐vLLM 对 Ray 的版本有依赖如果 Ray 版本过新或过老启动时会出现类似ModuleNotFoundError或兼容性报错。我的建议是直接用 vLLM 官方镜像并按照官方 requirements 安装 Ray不要手痒升级到最新版。生产环境里稳定压倒一切。6.3 GPU 记账不一致前面提过ray_actor_options里的num_gpus和 vLLM 的tensor_parallel_size必须匹配。如果 mismatchRay 会认为某块 GPU 空闲但实际上它已经被某个 vLLM actor 占用了后续调度就会产生资源冲突表现通常是任务排队时间很长或一直 pending。6.4 显存预留和 vLLM 的“贪心”vLLM 默认会把它看到的空闲显存几乎全部预留用来做 KV cache这本来是好事。但如果你在同一台机器上还要跑其他进程就会被打个措手不及。建议用--gpu-memory-utilization参数限制一下比如设为 0.85给系统和其他进程留出余量。用 Ray 调度时这个参数更加重要因为 Ray 不会替你做显存超卖保护。6.5 日志要提前收集别等故障再想办法Ray 模式下vLLM 的日志会分散在各 worker。我强烈建议在部署之初就做好日志收集最简单的方式是加一个--log-format参数输出结构化日志并统一收集到日志平台。否则等线上出问题你会发现自己完全不知道哪个节点、哪个 worker 报了错。常见报错可能原因排查方向NCCL connection refused网络接口没指定设置 NCCL_SOCKET_IFNAMERay actor init timeoutGPU 资源不足或版本不匹配检查 ray_actor_options 与 TP sizeOutOfMemoryvLLM 预留显存过大设置 gpu-memory-utilizationWorker failed due to NCCL error跨机网络不稳定检查网线、网卡、交换机配置Model loading timeout冷启动加载时间过长调整部署超时设置或预热模型我个人在实际部署中的体会是引入 Ray 这件事本身不是问题问题在于很多人没有先想清楚“引入 Ray 是为了解决哪个具体的麻烦”。如果你只是需要把两台机器各跑一个 vLLM 副本那你需要的是一个 Nginx 配置文件而不是一个分布式框架。反过来说如果你确实有多模型共存、动态伸缩、故障自愈这些需求那 Ray Serve 确实值得投资但也要做好接受更高运维复杂度的准备。最后再分享一个我自己踩过的小技巧如果你决定用 Ray Serve 部署 vLLM先把 vLLM 的启动参数在单机模式下调通再把 Nginx 的分发逻辑跑通最后再给这个组合套上 Ray Serve。每往上加一层抽象前先确认下一层是稳的。做推理服务这件事简单可靠永远比花哨架构更值钱。