前 13 篇把单机推理这条路走到了头:引擎原理、量化、KV Cache、投机解码、兼容层,全是"一台机器、几张卡"视角下的优化。但总有走到单机边界的一天:模型权重超出了单卡显存、或者业务并发超出了单卡的吞吐上限、或者服务要 SLA 而单机给不了高可用——这时候就从"单机调优"进入"多机组网"的领地。本篇讲两件事:怎么把一个大模型切到多张卡、多台机器上跑(张量并行与流水线并行),以及怎么把推理服务放进 Kubernetes(K8s)获得调度与弹性能力。
这是《本地大模型部署实战》系列第 14 篇,从"单机"跨到"集群"的一篇。先给多机的三条判断信号,再图解 TP 与 PP 两种并行策略,然后用 vLLM + Ray 走一遍从单机多卡到多机的完整命令,接着算清"加卡还是加量化"这笔账,最后落到 K8s:GPU 调度、Deployment 写法、弹性伸缩的真相与多副本负载均衡。本文命令以 2026 年 10 月的 vLLM v0.32.0 与 K8s 1.34 生态为准。
1、多机的三个信号
什么时候该上多机?把三条信号摆出来,逐条对照第 6 篇的单卡容量规划方法即可判断。
第一条:权重装不进单卡。70B 模型 FP16 权重约 140 GB,单张 80 GB 的 A100/H100 装不下;上百 B 的 MoE 模型即使每 token 激活参数不多,总权重照样动辄数百 GB。这条是硬边界,量化(第 9、10 篇)能推迟它,但推不掉百 B 级别。第二条:吞吐超出单卡。权重装得下,但第 6 篇那套"显存预算 ÷ 单请求 KV 占用"的算法告诉你:并发开不满、TTFT 压不到目标,容量先于显存见顶。第三条:要高可用。单机就是单点,进程挂了、卡坏了、要升级 vLLM 版本,服务都得停;多副本跨节点分布才有故障转移可言。
三条信号的紧迫程度不同,对策也不同。
| 信号 | 本质 | 优先对策 |
|---|---|---|
| 权重超单卡 | 显存硬边界 | 先量化压缩,仍不够再切卡 |
| 吞吐超单卡 | 容量瓶颈 | 加副本做负载均衡,其次考虑切卡 |
| 要高可用 | 单点故障 | 多副本,与模型大小无关 |
注意第二、三条的正确答案常常不是"把一个模型切到多卡",而是"多份完整模型多副本"——前者是本篇第 2、3 节的并行问题,后者是第 7 节的负载均衡问题,两者别混。
2、两种并行策略
把一个模型切到多张卡,只有两种切法。张量并行(Tensor Parallel,TP):把每一层内部的权重矩阵按行或列切开,各卡各拿一份,算完再拼。流水线并行(Pipeline Parallel,PP):把模型按层切成几段,一段一张(组)卡,数据像流过流水线一样穿过各段。
解读:两者的通信模式完全不同。TP 每一层前向都要一次 all-reduce,通信频繁、单次量大(正比于 hidden size × batch),但它换来了低延迟——所有卡同时算同一层,单请求延迟几乎不涨。PP 只在段与段的边界传一次激活值,通信量极小,但不同 micro-batch 之间会产生流水线"气泡"(前一段没算完,后一段闲着),吞吐有折损,且单请求延迟随段数近似线性增长。
| 维度 | 张量并行 TP | 流水线并行 PP |
|---|---|---|
| 切分对象 | 层内矩阵 | 按层分段 |
| 通信频率 | 每层 2 次 all-reduce | 段边界 1 次 |
| 通信量级 | 大,正比于 hidden × batch | 小,仅激活值 |
| 单请求延迟 | 几乎不变 | 随段数增长 |
| 互联要求 | 强依赖 NVLink 级带宽 | 普通以太网可接受 |
关键结论:机内用 TP、机间用 PP。TP 的 all-reduce 走 NVLink 时是微秒级,跨机后落到以太网上会慢一到两个数量级,直接吃掉并行收益——vLLM 官方文档明确建议:没有 NVLink 的卡(如 L40S)优先用 PP 换吞吐;跨机 TP 必须配 InfiniBand 或 RoCE 高速网络。而 PP 传的激活值只有 KB 级,普通机房网络就够。实践中的大集群部署(如 2 台 × 8 卡)通常组合成 TP=8、PP=2:TP 锁在单机内部,PP 跨机。
3、vLLM 多机实操
3.1、单机多卡
多机的第一步是先把单机多卡跑通,这一步不需要任何分布式配置——vLLM 检测到多卡时会自动切原生多进程,一个参数即可。以 4 张 4090 跑 Qwen2.5-32B(FP16 权重约 64 GB,单卡 24 GB 装不下,4 卡各分 16 GB)为例,启动命令如下。
vllm serve /models/Qwen2.5-32B-Instruct \
--tensor-parallel-size 4 \
--max-model-len 8192
解读:--tensor-parallel-size 4 把模型按 TP=4 切到 4 张卡上,其余参数与第 6 篇的单卡用法完全一致,OpenAI 兼容接口(第 13 篇)也不受影响。注意 TP=4 时每生成一个 token 都伴随全组 all-reduce,4090 没有 NVLink、靠 PCIe 通信,吞吐会比理论线性加速明显打折——单机消费卡上 TP 是"能用",不是"高效"。
3.2、Ray 多机集群
跨机后 vLLM 换用 Ray 做分布式运行时(多机场景默认且推荐的 backend)。流程三步:起 Ray 头节点、各机器入集群、头节点上起 vLLM。前两步的命令如下。
# 机器 A(头节点):初始化集群,装好与 worker 同版本的 vLLM
pip install ray
ray start --head --port=6379
# 机器 B(worker 节点):入队,指向头节点地址
ray start --address='192.168.1.10:6379'
解读:两台机器组成一个 Ray 集群后,在头节点上用 ray status 应能看到全部 GPU 资源;worker 节点必须装同版本 vLLM 且能以相同路径访问模型权重(NFS 或各机预下载)。此时启动 vLLM 只需声明总并行度并显式指定 Ray 后端,以 2 台 × 2 卡 A100 跑 Qwen2.5-72B(FP16 约 144 GB)为例,TP=2 锁在机内、PP=2 跨机。
vllm serve /models/Qwen2.5-72B-Instruct \
--tensor-parallel-size 2 \
--pipeline-parallel-size 2 \
--distributed-executor-backend ray
解读:Ray 会把 4 个 GPU worker 编排成两个流水线阶段,每阶段内部 TP=2。所有 worker 节点必须装同版本 vLLM 且能以相同路径访问模型权重(NFS 或各机预下载)。vLLM 官方还提供了免手工的 run_cluster.sh 脚本(Docker 方式一键起头节点与 worker),以及不依赖 Ray 的 --nnodes 2 --node-rank 0 --master-addr 多进程模式,详见官方并行文档。
注意:若坚持跨机 TP,先用
NCCL_DEBUG=TRACE vllm serve ...跑一次,日志里看到[send] via NET/IB/GDRDMA说明走了 InfiniBand 的 GPUDirect RDMA,高速通道生效;若是NET/Socket,通信走 TCP,TP 的收益大概率被吃光,改用 PP。
SGLang 的多机路径与此同构:同样先 ray start 组集群,再用 --tp-size、--pp-size、--dp-size 声明并行度(参数名与 vLLM 略异),第 7 篇装过的环境直接可用。
4、加卡还是加量化
70B 模型恰好卡在决策分界线上,拿它算笔账。FP16 权重约 140 GB,双卡 80 GB 理论可装(每卡 70 GB + KV Cache,偏紧);AWQ/GPTQ INT4 权重压到约 40 GB,单张 80 GB 卡轻松装下还有余量给长上下文。也就是说,第 10 篇的量化技术在这里是一条"优雅绕开多机"的路径。
| 方案 | 硬件 | 权重精度 | 质量损益 | 复杂度 |
|---|---|---|---|---|
| 单卡 INT4 | 1 × 80 GB | 约 40 GB | 轻微退化 | 单机单进程,最简 |
| 双卡 TP FP16 | 2 × 80 GB | 约 144 GB | 无损基线 | 需 TP 通信,机内 NVLink |
| 四卡 TP+PP FP16 | 4 × 80 GB | 约 144 GB | 无损基线 | 多机 Ray,运维最重 |
质量端:第 10 篇讲过 GPTQ/AWQ 在 4 bit 档的典型退化——通用任务感知不明显,但长链推理、代码补全、数学类任务会有可测的掉点。如果业务恰好是这几类,FP16 多卡不是浪费。工程端的决策顺序是:先量化,量化装得下就用单卡;量化装不下或质量不达标,再上多卡。为一个"其实 INT4 就能跑"的负载直接上多机 FP16,是最常见的过度建设。反过来,量化已到质量底线仍装不下,或者需要 FP16 级质量——那就别犹豫,本篇第 3 节就是为你写的。
5、K8s GPU 部署
多机裸奔 Ray 适合临时验证,长期跑要交给 K8s 管理:节点坏掉自动重建、滚动升级、多服务共享 GPU 池。第一步是让 K8s 认识 GPU——部署 NVIDIA device plugin(DaemonSet,节点需先装好驱动和 nvidia-container-toolkit),它把每张卡注册成 nvidia.com/gpu 扩展资源,Pod 就能像请求 CPU 一样请求 GPU。
说明:device plugin 的"整卡计数"模型正在被 DRA(Dynamic Resource Allocation)取代——Kubernetes 1.34 起 DRA 进入 GA,NVIDIA 已开源官方 DRA 驱动,支持拓扑感知与 MIG 切分;存量集群仍以 device plugin 为主流,新集群可关注迁移。
单卡推理的 Deployment 写法如下(模型权重放 PVC 共享存储,避免每个节点重复下载)。
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-32b
spec:
replicas: 2
selector:
matchLabels:
app: vllm-32b
template:
metadata:
labels:
app: vllm-32b
spec:
containers:
- name: vllm
image: vllm/vllm-openai:v0.32.0
args: ["vllm", "serve", "/models/Qwen2.5-32B-Instruct-AWQ"]
resources:
limits:
nvidia.com/gpu: "1"
volumeMounts:
- name: models
mountPath: /models
volumes:
- name: models
persistentVolumeClaim:
claimName: models-pvc
解读:nvidia.com/gpu: "1" 声明单卡,requests 与 limits 设成相等(GPU 不可超卖);调度器保证两个副本落在有空闲卡的节点上。PVC 指向 NFS 或云盘,装满模型的目录直接挂进容器。多卡 TP 在 K8s 上也不难:资源改成 nvidia.com/gpu: "4",容器内 --tensor-parallel-size 4,前提是节点有 4 张卡且调度器能整体分配。不想手写 yaml 的,vLLM 官方维护了 production-stack Helm chart,连路由和缓存层一起装。
6、弹性伸缩的真相
K8s 的招牌能力是弹性伸缩,但 LLM 推理直接套 HPA(Horizontal Pod Autoscaler)大概率失望,原因有三层。
第一层:推理 Pod 不是真无状态。模型权重是只读的没问题,但 KV Cache 与 prefix cache 都在副本本地内存里,缩容即丢;第 7 篇讲过 SGLang 的 radix tree、第 11 篇讲过 vLLM 的 prefix cache,命中的前提是"同一批卡见过这段前文",副本一换全部作废。第二层:冷启动是分钟级。新副本要拉起十几 GB 的镜像、从 PVC 加载几十到上百 GB 权重、预热 CUDA 图,2~5 分钟很平常;HTTP 流量突增时等它扩容完成,早过了流量尖峰。第三层:HPA 指标失真。HPA 原生只有 CPU/内存指标,GPU 利用率要走 DCGM exporter + 自定义指标适配器;更麻烦的是 LLM 推理的 GPU 利用率天然大幅波动,空转 0%、来请求冲高,按它伸缩会抖个不停——社区经验里 20%~30% 的利用率阈值才勉强可用。
真实做法是预留副本代替追涨式伸缩:按 P99 流量配 minReplicas,把"弹性"降级为"计划扩容"——大促前手动改副本数。KServe(基于 Knative 的 K8s 模型服务平台)提供了另一极端:缩容到零,省钱彻底,但代价是把分钟级冷启动暴露给第一个请求的用户。对延迟敏感的服务,缩容到零几乎不可用;对内部工具类、一天几波请求的场景,它省下的卡是真金白银。
7、多副本负载均衡
回到第 1 节的分支:吞吐不够的正确解法是水平扩副本。两个 replicas: 2 的 vLLM Pod 起来后,流量怎么分?最外层随便一个 Ingress/Nginx 轮询就能分,但轮询有一个隐藏成本:它打碎了 prefix cache。第 7 篇分析过,SGLang 的树命中是副本本地的——同一会话的第 N 轮请求被轮询到没见过前文的副本上,radix tree 零命中,整段 system prompt 重新 prefill,TTFT 直接退回首轮水平。vLLM 的 prefix caching 同理。
对策是会话亲和:让路由层按会话 ID 或 API key 哈希,把同一会话粘到同一副本。Nginx 的 hash $arg_session_id consistent、网关层的一致性哈希都能做;vLLM production-stack 的内置 router 也提供了按请求哈希的路由策略。亲和带来的收益在长 system prompt 场景尤其大——Agent 类负载动辄几万 token 的工具定义与历史,命中与不命中的 TTFT 差 3~5 倍。
代价是均衡与亲和的矛盾:哈希分布不均时某些副本过热,且副本扩缩容会引起哈希环重排、命中率的短暂坍塌(一致性哈希能缓解)。工程折中是"会话级亲和 + 副本数尽量稳定",又一次印证第 6 节:LLM 服务的弹性,宁稳勿弹。
8、成本与利用率陷阱
多机推理最反直觉的事实:卡数与吞吐不成正比,过了某个点甚至倒退。TP=2 时每层通信翻倍,TP=4 再翻,互联带宽跟不上时 GPU 互相等通信,总吞吐增速衰减;消费级 PCIe 卡上 TP=4 的实测吞吐常常只有单卡的 2 倍出头。多机的账要按"通信开销 + 利用率"两笔一起算。
利用率陷阱更隐蔽:推理负载有明显的潮汐,白天高峰、夜里低谷,为峰值配的 4 卡集群在低谷期利用率可能不到 10%,但电费、折旧、机房一分开销不少。第 6 节的预留副本、KServe 缩容到零,本质都是在跟这笔账博弈。堆卡解决的是"装得下"的问题,从来不是"省钱"的问题——省钱的顺序永远是:量化(第 9、10 篇)→ 批处理与缓存(第 5、7、11 篇)→ 投机解码(第 12 篇)→ 最后才是加卡。
一个实用的验收习惯:多机部署完成后,用第 8 篇的压测方法跑一轮,记录单卡基线与多机 TP 的总吞吐,算出并行效率(多机吞吐 ÷ 单卡吞吐 ÷ 卡数)。低于 70% 就别继续加卡了,先查互联:NVLink 是否真的启用、跨机是否走了 IB/RoCE,回到第 3 节那条 NCCL_DEBUG=TRACE 的检查。
9、总结
本篇把系列从单机推进到集群:多机的三条信号(权重超单卡、吞吐超单卡、要高可用)先分清"切模型"与"加副本"两个问题;并行策略记住八字——机内 TP、机间 PP,TP 强依赖 NVLink,跨机 TP 必须有 IB/RoCE;实操上 vLLM 单机多卡一个 --tensor-parallel-size 参数即达,多机走 Ray 三步(起头节点、worker 入队、vllm serve 声明并行度);70B 档先量化后加卡,量化是绕开多机的优雅路径;K8s 上 device plugin(迈向 DRA)暴露 GPU、Deployment 请求 nvidia.com/gpu、PVC 挂权重;弹性伸缩认清现实——预留副本优于 HPA,缩容到零只适合内部工具;多副本负载均衡要做会话亲和保护 prefix cache;最后记住卡数与吞吐不成正比,并行效率低于 70% 先查互联而不是加卡。
留一道成本练习。业务要上 70B 模型,日均 2000 万输入 token、500 万输出 token,三个候选方案:A,自建 1 张 80 GB 卡跑 INT4,卡时按 20 元/小时(示例价,随行情浮动);B,自建 2 张 80 GB 卡 TP 跑 FP16,卡时 40 元/小时;C,调云 API,输入 4 元/百万 token、输出 12 元/百万 token(示例价)。请分别算出三方案的月成本(按 730 小时),再算日流量涨到多少倍时方案 A 开始反超方案 C,并思考:如果业务属于长会话 Agent 负载、且数据不能出内网,答案会怎么变?这道题没有标准答案,但算过一遍,你对"自建还是调 API、单卡还是多卡"的判断会比绝大多数人清醒。

