ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

两台机器跑 vLLM 该不该上 Ray?显存、带宽与部署形态权衡

两台机器跑 vLLM 该不该上 Ray?显存、带宽与部署形态权衡 1. 先别急着开干把两台机器翻译成显存、带宽和模型体积1.1 标题里的两台机器实际上问的是三个硬件参数很多人一看到两台机器跑 vLLM就直接跳到要不要上 Ray这个技术选型上我反而觉得第一步应该先把场景拆干净。两台机器是一句非常模糊的话落到真实部署里它至少包含三层信息每台机器上有几张卡、每张卡多大显存以及节点之间走的是什么网络。这三个参数不同Ray 的价值完全不同。拿圈子里最常见的配置来说一台 8 卡 A100/H800/H20 的节点每卡 80GB单机总共 640GB 显存。7B 模型 FP16 权重大约 14GB32B 大约 64GB72B 大约 144GB这些在一台机器内完全放得下。可一旦把目标换成 DeepSeek 这种 671B 的 MoE 模型FP8 权重也要 700GB 往上一台 640GB 的机器连权重都塞不全更不用说还要给 KV cache、给调度留余量。这个时候两台机器合起来的 1.28TB才有真正的意义——它是一个显存扩容的手牌而不是一句抽象的部署口号。第二个被严重低估的参数是节点间带宽。张量并行跨节点跑的时候每个 Transformer 层都要做一次 all-reduce两张卡之间高频交换激活值。这个通信对延迟非常敏感和训练时的长时间大流量还不是一回事。NVLink 在单机内部带宽可以到 900GB/s 级别跨节点的 InfiniBand 200G 也只有它的四分之一不到如果机器之间只是万兆以太网那跨节点张量并行的每一层都会被网络拖成瓶颈。我见过不少团队在 10G 网卡上强行 TP16最后吞吐比两台机器各自独立推理还惨问题就出在没把带宽当回事。所以我的第一个建议很直白先量好你的模型体积、单机显存总量、节点间带宽这三组数字再谈 Ray。这三组数字会直接决定 Ray 对你到底是必需品、加分项还是纯负担。1.2 单机方案的天花板在哪里是装不下还是扛不动判断要不要跨节点的核心不是我有两台机器而是单机方案到底在哪里卡住了。我通常把卡住的情况分成两类。第一类是真装不下。模型权重加最大并发时的 KV cache 总显存需求已经超过单机全部 GPU 总显存这种情况下你没有任何选择跨节点并行是唯一可行的路Ray 也因此是必需品。第二类是装得下但扛不动。比如 32B 模型在 4 张 80GB 上完全放得下但你的线上请求全是长上下文prefill 阶段把算力全部吃满单机实例的吞吐和 TTFT 都到不了 SLO。这类问题很容易被误判成显存不够、需要更多卡于是很多人直接上 TP16把两台机器绑定成一个超大实例。这个方向往往是错的并发扛不动的时候更合理的方案是加副本、做路由而不是扩大单个实例的张量并行度。道理不复杂。两台机器各自跑一个 vLLM 实例QPS 几乎线性翻倍而且任一台挂掉另一台还能继续服务两台机器绑成 TP16通信开销加进来吞吐大概率不升反降故障域也从一台机器变成了整个集群。只有在模型权重确实需要跨节点分布的硬约束下TP16 才是合理的。因此两台机器跑 vLLM什么时候才值得引入 Ray这个问题本质上是问两件事你的模型是否必须跨节点并行以及你的节点间网络撑不撑得起这个并行。这两个答案都是否的话Ray 只会给你添乱。2. vLLM 的多机并行靠什么组织Ray 在里面实际干了哪些活2.1 TP、PP、DP、EP 与 Ray 的真实关系vLLM 自己就有完整的并行框架这一点经常被低估。--tensor-parallel-size负责张量并行把每一层的矩阵和注意力头切到多张卡上--pipeline-parallel-size负责流水线并行按网络层做切分。在单机场景里这些并行可以由 vLLM 自带的 multiprocessing 执行器完成并不强制依赖 Ray。但一旦计算要跨节点问题就变了。vLLM 需要有人帮它去各台机器上拉起 worker 进程、确认每台机器还剩多少 GPU、把一组要求的资源锁定成一个稳定的集合并且让所有 worker 在网络层互相找到对方。这个活目前就是 Ray 在干。Ray 在 vLLM 里的身份是分布式执行后端distributed executor backend官方参数也叫--distributed-executor-backend ray。Ray 具体做三件事第一管理分布于多台机器的 Ray Actor每个 Actor 对应 vLLM 的一个 worker第二用 Placement Group 声明一组 bundle比如 16 个 GPU bundle保证同一个模型的所有 worker 能被放置到同一批节点上第三在 worker 之间建立地址互认并为后续 NCCL 通信初始化环境。真正的模型切分、KV cache 分配、调度决策都发生在 vLLM 内部Ray 只是那个把散落在两台机器上的 GPU 聚起来的调度者。不同并行方式和 Ray 的关系可以用一张表说清楚并行方式是否可能需要跨节点跨节点时是否依赖 Ray典型适用场景DP数据并行副本否不需要两台机器各跑一个实例前置负载均衡TP张量并行可以跨节点时必须671B 这类超重模型显存必须多机拼盘PP流水线并行可以通常也走 Ray节点间带宽有限时的折中切分EP专家并行MoE 专用可以跨节点时必须DeepSeek/V3 类 MoE 模型的多机推理注意最上面那行如果你只是两台机器各跑一个独立实例这本质是数据并行根本不需要 Ray市面上大量的Ray 多机教程在这种情况下是彻头彻尾的过度设计。2.2 最容易卡住你的其实是 Placement Group而不是模型加载多机跑 vLLM 时真正决定能不能顺利跑起来的核心是 Ray 的 Placement Group。你给 vLLM 传--tensor-parallel-size 16Ray 后端就会去集群上申请 16 个 GPU 资源然后按照 bundle 的粒度把 worker 分布到节点上。这里有一个很隐蔽的行为如果某个 bundle 因为资源不匹配申请不到vLLM 会一直等表现就是日志里反复出现 Waiting for pending 或者 Placement group ... is not ready 之类的信息。很多人以为是镜像下载慢、模型加载慢、甚至网络断了实际上只是资源撞车。我自己遇到的一个典型场景是头节点上还跑着一个旧 vLLM 实例占掉了 4 张卡然后新任务申请 TP16Ray 发现头节点只剩 4 张 GPUworker 节点有 8 张还差 4 张于是整个任务挂着不动。日志没有任何红字报错只是永远在 wait。这种问题在单机环境很少见因为资源都在眼皮底下到了多机资源分布不透明排查成本直线上升。排的时候也别只盯着 vLLM 的日志。Ray 侧的执行器日志、dashboard 里的 Placement Group 状态、ray status的资源汇总这些才是第一手信息。想明白了这一点你就能理解为什么我反复强调先确认单机方案已经到顶了再上 Ray——因为多机引入的复杂度不是 vLLM 给的是资源编排给的。2.3 破除Ray 等于自动加速的误解这是我最想写清楚的一节。很多同学引入 Ray 时心里默认分布式 加速这个预期在 vLLM 场景下是错的。Ray 是调度器不是加速器它不会减少跨节点通信的物理代价。跨节点张量并行的每一步 all-reduce 都是真实发生在网线上的NCCL 该等多久还是等多久Ray 改变不了这个事实。单机内部的 NCCL 走 NVLink跨节点走 TCP 或 RoCE。物理规律摆在那里10G 网卡跨节点 TP 就是灾难25G 网卡也只能说勉强能用真正配得上跨节点 TP 的至少是 40G 起步、最好是 InfiniBand。在带宽不够的前提下Ray 把 16 张卡聚成一个逻辑实例带来的只是更大的调度便利而不是更快的推理速度。所以我在评估一个项目要不要引入 Ray 时通常先问网络再问模型最后才聊调度。顺序反了后面全是被动。3. 两台机器三种部署形态我把判断标准量化给你3.1 形态 A两台机器各自 vLLM 实例前置一层路由——大多数项目的最优解这是我最推荐、也是线上我最常采用的形态。前提很简单模型在单台机器上放得下并且单实例的并发能力能满足大部分流量。具体做法是在两台机器上各起一个vllm serve然后在前面加一个 L7 负载均衡比如 OpenResty、Nginx或者网关组件把请求按亲和性或轮询转发到两个实例。这个方案的优势很直观。第一吞吐几乎线性翻倍因为两个实例互不共享任何状态各自做 continuous batching完全并行第二故障域是一台机器而不是整个集群其中一台挂了另一台还能扛下一半流量第三完全摆脱了 Ray 的部署复杂度也不用操心 Placement Group 和 NCCL 跨节点通信。对大多数 7B、32B、72B 级别的线上服务来说两台机器跑两个副本性价比远高于绑定成一个 16 卡大实例。我接触过不少团队明明 70B 模型一张 80GB 都放得下却为了充分利用两台机器硬要上 TP16最后吞吐反而下降还得天天处理 out-of-memory 级别以外的分布式疑难杂症。这是典型的为方案找需求。判断标准很简单你的延迟和 QPS 指标在单机双实例下能不能达标能达标就别碰 Ray。3.2 形态 B一份模型跨两台机器张量并行——Ray 的必须主场形态 B 才轮到 Ray 出场的核心场景模型权重单个节点放不下必须让 16 张卡共同承载同一份模型。最典型的例子就是 671B 的 DeepSeek 系列或大规模 MoE 模型。这时候 TP 跨节点不是值不值得的问题是不做就不行的硬约束。实际操作上你需要先在一台机器上启动 Ray head# 头节点 ray start --head --port6379 --dashboard-host0.0.0.0 --num-gpus8再在另一台机器上启动 Ray worker# 工作节点地址换成头节点 IP ray start --address192.168.1.10:6379 --num-gpus8然后回到头节点用同一个vllm/vllm-openai镜像启动服务vllm serve /models/DeepSeek-R1 \ --tensor-parallel-size 16 \ --ray-address http://127.0.0.1:8265 \ --max-model-len 32768 \ --gpu-memory-utilization 0.92vLLM 会通过--ray-address找到 Ray 集群申请 16 个 GPU bundle然后跨节点初始化 NCCL 通信。这里有几个一定要检查的细节所有节点的镜像版本必须一致否则 Ray 版协议不齐可能出奇怪问题--network host基本是必须的容器网络桥接会引入额外的 NAT 层NCCL 在跨节点时容易卡在握手共享内存也要放开--ipchost能防止/dev/shm太小导致 KV cache 分配失败。关于带宽我给个经验阈值跨节点 TP 在 25G 以太网上属于能跑但明显不舒服每次 all-reduce 都会成为微秒级的延迟增量40G 以上才谈得上有实用价值如果节点间是 10G 网卡我强烈建议放弃形态 B改走 PP 或者拆副本别硬撑 TP。3.3 形态 Cprefill/decode 分离或多模型混部——Ray 是加分项但别为它而上第三种形态相对进阶就是利用 Ray 的调度能力去做跨节点的 prefill/decode 分离或者在同一批节点上混合部署多个模型。vLLM 的激进调度本来就希望 prefill 和 decode 能拆开跑prefill 节点吃算力、decode 节点吃显存带宽。用 Ray 的 Placement Group 可以把这两类引擎分别钉在不同的机器上让两台各司其职。这个方案在单集群、多模型的场景下的确有价值。比如两台机器上除了主模型还想常驻一个 embedding 模型qwen3-embedding-0.6b 这类和一个较小的对话模型手动在每台机器上分配 GPU 很麻烦Ray 的资源调度反而能帮你把卡位规划清楚。但我个人对两台机器的规模持保守态度只有两台机器手动划分资源的成本已经很低了Ray 带来的编排收益并不大反而引入了一整套需要维护的集群状态。形态 C 更适合节点数到了 4 台以上、模型矩阵比较复杂的情况。两台机器的话直接把 GPU 人工分成几组、每组起独立 vLLM 容器往往更稳。4. 从 Docker 镜像到跑通多机的实操链路以及镜像带不带模型这类高频问题4.1 vllm/vllm-openai 镜像里有什么、没有什么官方镜像vllm/vllm-openai是社区用得最多的部署载体。镜像内部包含了 vLLM 的安装包、依赖的 CUDA 运行库、需要的编译依赖以及 Ray 的运行时也就是说启动容器之后已经具备跑多机的基本条件不需要你再手动pip install ray。但有一个高频误解必须澄清镜像里不带模型。官方镜像只是推理引擎不捆绑任何权重文件。所以模型需要你自己搞定。常见做法是把权重目录挂载进容器比如-v /data/models:/models然后在启动命令里写/models/DeepSeek-R1或者让 vLLM 启动时从 HuggingFace 或 ModelScope 下载。后者要注意网络和认证线下环境通常走HuggingFace 先下载到本地再挂载的离线方案。如果你用国内源把HOME下的 HuggingFace 缓存目录挂进去或者设置HF_HOME指向已经下载好的模型目录能省很多事。另外像qwen3-embedding-0.6b这类 embedding 模型能否被正确加载取决于你用的 vLLM 版本是否支持该模型架构。embedding 支持和 chat 支持走的是不同的模型 registry老版本镜像可能只支持对话模型对 embedding 端点直接报错。我遇到过不止一次模型在新版 vLLM 能跑、在老镜像里起不来的情况基本都要靠升级镜像版本解决。4.2 两台机器用 Docker 起 Ray 集群的关键细节在容器里起 Ray 集群比裸机多几个坑我把关键步骤和顺序整理一下。头节点容器docker run --gpus all \ --network host \ --ipchost \ -v /data/models:/models \ vllm/vllm-openai:v0.27.1 \ ray start --head --port6379 --dashboard-host0.0.0.0 --num-gpus8工作节点容器docker run --gpus all \ --network host \ --ipchost \ -v /data/models:/models \ vllm/vllm-openai:v0.27.1 \ ray start --address192.168.1.10:6379 --num-gpus8然后进入头节点容器启动 vLLMdocker exec -it head-container \ vllm serve /models/DeepSeek-R1 \ --tensor-parallel-size 16 \ --ray-address http://127.0.0.1:8265容器化部署的几个注意点每一个都是踩坑换来的--network host不是可选项而是强建议项。Ray 的分布式握手、NCCL 的 socket 连接在 bridge 网络下会出现各种超时尤其是跨节点场景经常表现为 TP worker 一直初始化不完。--ipchost解决/dev/shm过小的问题。vLLM 的共享内存使用量不容小觑尤其长上下文场景默认 Docker 的 64MB/dev/shm根本不够。两台机器的镜像版本必须一致性校验。docker image inspect之后对比 vLLM 版本和 Ray 版本版本不齐经常导致头节点看得见、任务调度不下去。--num-gpus参数要和实际可见卡数匹配。多机部署时最容易出现的问题是节点上有别的任务占了卡但你强行声明--num-gpus8Ray 会认为所有 GPU 都空闲最后任务启动时实际拿不到资源报错又隐藏得比较深。部署完成之后验证方式很简单curl 一下http://头节点IP:8000/v1/models能返回模型列表就说明基本链路通了。之后用 Chatbox 这类 OpenAI 兼容客户端填上http://头节点IP:8000/v1就能做对话测试。这一步对非后端同学非常友好不用写代码就能验证服务是否正常。4.3 模型版本、vLLM 版本和镜像版本的三方对齐做模型服务的人很熟悉一句话新模型一定要配新引擎。GLM 系列、DeepSeek 系列、以及各种刚发布的 embedding 模型官方都会在发布说明里标注推荐的 vLLM 版本甚至直接给出请使用某日之后的 nightly 镜像这样的指令。你手里的vllm/vllm-openai版本太旧新模型的权重格式、注意力实现、MoE 路由方式都可能不被支持表现往往是启动时报KeyError或直接提示 unsupported architecture。我个人的习惯是生产环境不追新但也不刻意守旧。模型发布层面的 issue 里如果社区普遍反馈某个版本有问题就尽早锁到下一个稳定版。镜像 tag 建议以官方 release 为准比如你在例子中看到的v0.27.1这类版本号设置时直接按目标模型官方推荐的版本去选而不要随手拉latest。latest在本地测试也许没问题但两台机器同时起容器万一其中一台拉到了新 tag、另一台还停在旧 tag排查成本很高。判断镜像版本是否合适的办法也很简单进入容器执行pip show vllm和pip show ray把两个版本写进部署清单。多机部署不是单组件问题头节点和所有工作节点必须在一份清单里对齐。5. 跑起来之后才发现的那些事scheduler 逻辑、error 1033 排查链路以及什么时候果断放弃5.1 多机下的调度逻辑没有变但它的感知边界变了vLLM 的 scheduler 是经典的连续批处理调度器干的活包括把新请求组织成 sequence group批内每步迭代动态决定哪些序列继续解码、哪些被抢占用 PagedAttention 管理 KV cache 的分页当显存不足时按策略做 preemption——可以是丢弃重算也可以是 swap 到 CPU 内存。这些调度逻辑在多机环境下并不会因为 Ray 的引入而改变但有一个边界值得注意全局调度决策依然由 driver worker 完成而 KV cache 和各层计算则分布在多个节点上。也就是说调度器需要知道全局序列状态同时每个 worker 只处理自己那份张量切片的数据。跨节点时这种一个大脑、多套手脚的模式比单机更容易出现等待——比如某个 worker 因为网络抖动慢了整步解码就会整体慢下来表现是吞吐骤降但不是报错。读日志时如果发现单机跑得好好的同一个模型到多机出现周期性的慢优先怀疑 NCCL 通信稳定性而不是 scheduler 配置问题。scheduler 参数比如--max-num-seqs、--max-model-len在多机下的调节逻辑和单机完全一致不需要为 Ray 单独调一套。5.2 用 error 1033 当入口走一遍多机排障链路跑过 Ray 多机的同学大概率见过这种日志error 1033 ray id: a41b20262cb6b9aa 2026-09-27 14:16:20 UTC Traceback (most recent call last): ... RuntimeError: NCCL error ...这里的1033是 Ray 任务失败时给事件分配的错误码ray id是这个事件的全局唯一标识。看到这行日志时很多人第一反应去搜 vLLM 的报错其实更应该先顺着 Ray 的逻辑查。我把排查链路整理成固定的四步专治这类问题。第一步在头节点执行ray status看集群里到底有多少节点、每个节点上报了几张 GPU、还有多少 pending 资源。多机部署里最大的坑是某个节点掉出了集群或者某个节点上报资源少于实际前者通常是网络连通性问题后者通常是--num-gpus写错了。第二步去看ray dashboard的 Placement Group 面板确认目标 worker 的 bundle 是否全部被放置成功。pending 状态一律先查资源和版本不要去翻模型目录。第三步查 worker 日志。ray logs可以拉取指定 actor 的日志NCCL 初始化失败的信息基本都能在里面找到比如网卡名不对、找不到可用的 GID、或者握手超时。第四步检查跨节点 NCCL 网络。用 nccl-tests 编一个 all-reduce 测试跨两台机器跑如果带宽明显低于预期问题在物理链路如果测试正常但 vLLM 跑不起来问题大概率在环境变量。多机 NCCL 最常见的环境变量坑是NCCL_SOCKET_IFNAME没有指定。机器上有多块网卡时NCCL 可能选错接口导致跨节点握手上失败。两节点都设成一致的 RDMA 或高速以太网接口名通常立刻解决。没有 InfiniBand 的环境主动设置NCCL_IB_DISABLE1也能避免 NCCL 反复尝试 RDMA 而超时。提示error 1033 本身没有通用的标准答案它只是 Ray 的事件码。真正有价值的永远是对应的 traceback 文本以及ray status里资源上报是否和你预期一致。先看这两处再谈别的。5.3 什么时候从 Ray 方案里退回来我的取舍经验讲了这么多最后说点实际取舍。我在生产里经历过一次非常典型的Ray 上马又下马两台 8 卡 H20模型是 72B FP8节点间是 25G 以太网。上线初期大家觉得不用 Ray 可惜了于是按形态 B 绑成 TP16。结果实测单请求 TTFT 反而比单机 8 卡版本慢了近 3 倍QPS 也没有明显优势因为权重虽然在单台 8 卡上放得下但跨节点的通信延迟把所有收益都吃掉了。后来我果断退回形态 A两台机器各跑一个实例前面用 Nginx 做轮询QPS 直接翻倍部署复杂度也降了一大截。那之后我的判断标准变得很量化先跑一轮基准。用 vLLM 自带的 benchmark 脚本分别测单机单实例双机双实例双机 TP16三种方案的 QPS、TTFT、吞吐。如果双机双实例已经能满足目标流量Ray 方案就被否掉只有当双机双实例因为模型显存硬约束而无法成立时才去看 Ray 方案并且同步检查网络是否达标。另外还有一种情况值得用 Ray你面对的是多个模型混跑且资源动态变化需要 Ray 帮你管理集群级资源分配。但那是节点规模扩大到一定程度之后的故事。两台机器时手动把 GPU 切分成组、每组起独立容器比背着整个 Ray 集群做事放心得多。我个人现在的态度是Ray 是工具不是目标。当单机装不下模型它是唯一解当单机装得下它往往是可选项当单机装得下且流量可控它就是负资产。这篇文章的标题问什么时候才值得引入 Ray我的答案是先让模型体积和网络带宽做决定别让分布式三个字做决定。
返回列表