跳到主内容
快讯直播
AI智模界
教程

跨两台 DGX 用 vLLM 部署 GLM 并压测调优

这篇教程拆解一个真实场景:模型大到单机 8 卡装不下(或者装得下但 AI 词典:KV Cache">KV Cache 不够用),需要把权重切到两台 DGX 上,用 vLLM 的多节点张量并行跑起来,再通过压测把输出吞吐稳定推到 600+ tok/s 这一档。

文中出现的吞吐数字(例如 607.8 tok/s)来自特定测试环境下的实测结果,只作为调优目标参考。你的机器互联带宽、模型精度、输入输出长度分布不同,结果会明显不同——复现思路一致,具体数值以你自己压测为准。模型权重仓库名、镜像 tag、vLLM 版本号请一律以官方文档当前版本为准。

适用场景

手头有两台各 8 卡的 DGX(或同级别 GPU 服务器),要部署一个单机显存放不下的超大规模 MoE / 长上下文模型,并且对首 token 延迟和整体吞吐都有要求。这套方案适合做内网推理服务、模型评测基线、私有化交付前的性能验证。如果你只是想让几个人试用,单机 + 量化就够了,不必上多节点。

环境与前置条件

  • 操作系统:Ubuntu 22.04 LTS 或同代服务器发行版,两台机器内核版本保持一致。
  • 驱动 / CUDA:GPU 驱动、CUDA 版本两台完全一致,用 nvidia-smi 右上角确认。驱动不一致是多节点故障的高发原因。
  • GPU 互联:每台机器 8 卡,卡间走 NVLink/NVSwitch;跨机必须有一张 RDMA 网卡(InfiniBand 或 RoCE),千兆/万兆以太网只能跑通、跑不快。
  • 运行时:Python 3.10+,vLLM 主版本与 CUDA 编译版本匹配(以官方文档当前版本为准);需要 Ray 作为多节点分布式执行后端。
  • 内存:每节点系统内存 ≥ 模型权重体积的 1.5 倍(加载时会先落到内存再分发)。
  • 显存:模型参数量 × 每参数字节数 ÷ 16 要显著小于单卡显存。以 FP16/BF16 部署为例,权重总量 800 GB 时,16 卡平均每卡 50 GB 权重,再留出 KV Cache 和激活的空间。
  • 磁盘:模型权重放在两台机器都能访问的同一路径下(NFS / 并行文件系统),或者两台各存一份、路径完全一致。预留 ≥ 权重体积 2 倍的磁盘。
  • 网络:两台机器同网段,能互相 ping 通,防火墙放行 Ray 端口(默认 6379、8265)和 vLLM 通信端口。

分步骤部署

第 1 步:确定并行切分方案

先算清楚怎么切。设模型有 N 个注意力头、L 层、16 张卡:

  • 优先用张量并行(TP):--tensor-parallel-size 16,把每层的权重按头和中间维度切成 16 份。要求注意力头数能被 TP 整除,且单层权重切片后放得进单卡。
  • 如果头数不够整除,退一步用 TP × PP 组合:例如 --tensor-parallel-size 8 --pipeline-parallel-size 2,每台机器内部做 8 路张量并行,两台机器之间做流水线并行。代价是多一段流水线气泡,吞吐略降但更容易跑通。
  • 跨机 TP 的通信量远大于 PP,如果 RDMA 带宽有限(比如只有单口 100G),先用 TP=8 + PP=2 验证,再考虑上 TP=16。

把结论记下来,后面启动命令直接用。

第 2 步:系统与硬件自检

两台机器分别执行,结果要能对得上。

```bash

驱动与 GPU 数量

nvidia-smi

nvidia-smi topo -m # 确认卡间是 NVLink,不是全部走 SYS

RDMA 网卡状态

ibstat # IB 网卡应显示 State: Active,Rate 为实际速率

ip -br addr # 记下业务网卡名,后面 NCCL_SOCKET_IFNAME 要用

```

成功标志:nvidia-smi 显示 8 张卡且型号一致;nvidia-smi topo -m 里 GPU 之间标 NV#;ibstat 状态 Active。如果 ibstat 看不到设备,先排查驱动和固件,别急着往下走。

第 3 步:写统一的环境变量文件

两台机器都创建 /etc/profile.d/nccl.sh,内容一致,只有 VLLM_HOST_IP 按本机 IP 改:

```bash

RDMA 网卡名,多口用逗号分隔,可用 ibdev2netdev 查询

export NCCL_IB_HCA=mlx5_0,mlx5_1

RoCE v2 一般是 3,IB 环境可用 show_gids 确认

export NCCL_IB_GID_INDEX=3

控制面网卡,写成业务网卡名,不要写 lo

export NCCL_SOCKET_IFNAME=bond0

export GLOO_SOCKET_IFNAME=bond0

允许 GPU 直接读写网卡内存

export NCCL_NET_GDR_LEVEL=SYS

export NCCL_DEBUG=WARN

export NCCL_DEBUG_SUBSYS=INIT,NET

本机 IP,每台机器改成自己的

export VLLM_HOST_IP=<本机业务IP>

```

然后 source /etc/profile.d/nccl.sh 让它生效。

这一步在做的事:告诉 NCCL 走哪张 RDMA 网卡、用哪个 GID、控制面走哪张以太网卡。多节点跑不起来的情况里,一大半是这几个变量没设或设错。

第 4 步:用 nccl-tests 验证跨机通信

在启动大模型之前,先用小工具确认两台机器能高效通信,避免把网络问题误判成模型问题。

```bash

机器 A

./all_reduce_perf -b 8 -e 8G -f 2 -g 8

机器 B(与 A 同时启动)

./all_reduce_perf -b 8 -e 8G -f 2 -g 8

```

成功标志:输出里出现 Avg bus bandwidth,大包(GB 级)的带宽接近网卡标称速率的 70% 以上,且没有 NCCL WARN。带宽只有几十 MB/s,说明流量走了 TCP 回退,回头检查 NCCL_IB_HCA 和 GID。

第 5 步:准备模型权重

在两台机器上使用完全相同的路径,例如 /models/glm:

```bash

ls /models/glm

config.json tokenizer_config.json *.safetensors ...

```

如果是 NFS,确认两台机器 ls 出来的文件列表和大小一致;如果是本地各存一份,用 sha256sum 抽查几个分片确认一致。路径不一致会导致 worker 端起不来。

第 6 步:拉起 Ray 集群

先检查端口:

```bash

ss -lntp | grep -E '6379|8265'

```

头节点(机器 A):

```bash

ray stop --force

ray start --head \

--node-ip-address=<机器A业务IP> \

--port=6379 \

--dashboard-host=0.0.0.0 \

--num-gpus=8 \

--disable-usage-stats

```

工作节点(机器 B):

```bash

ray stop --force

ray start \

--address=<机器A业务IP>:6379 \

--node-ip-address=<机器B业务IP> \

--num-gpus=8 \

--disable-usage-stats

```

回到头节点验证:

```bash

ray status

```

成功标志:输出里 Resources 一段显示 GPU: 16.0,Node status 里有两个节点,状态都是 ALIVE。这时候 Ray 的 Dashboard 也能在 http://<机器A业务IP>:8265 打开。

第 7 步:启动 vLLM 服务

在头节点执行。注意 --distributed-executor-backend ray 不能省,否则 vLLM 会用单机的 multiprocessing 后端,worker 节点被晾在一边。

```bash

vllm serve /models/glm \

--served-model-name glm-5.3 \

--tensor-parallel-size 16 \

--distributed-executor-backend ray \

--gpu-memory-utilization 0.90 \

--max-model-len 32768 \

--max-num-seqs 256 \

--enable-chunked-prefill \

--kv-cache-dtype fp8 \

--trust-remote-code \

--host 0.0.0.0 \

--port 8000

```

几个参数的作用,按需调整:

  • --gpu-memory-utilization 0.90:显存切分的关键旋钮。vLLM 先按比例吃掉显存做权重和激活,剩下的全部拿来做 KV Cache。压到 0.95 能多出缓存,但留给 CUDA Graph 和碎片的余量变少,容易在压测中途 OOM。
  • --max-model-len:直接决定 KV Cache 单条占用。上下文长度翻倍,单请求缓存近似翻倍,能同时服务的请求数就减半。
  • --max-num-seqs:同时在跑的序列数上限,是吞吐调优的主要手段之一。
  • --enable-chunked-prefill:长输入场景下把 prefill 分块,避免一个超长请求把其他请求的 decode 卡住,对 TPOT 稳定性帮助明显。
  • --kv-cache-dtype fp8:KV Cache 用 FP8 存,缓存容量大约翻倍,代价是极轻微的质量损失,需要你的 GPU 架构支持。

启动过程中会有 1~3 分钟(大模型更久)的权重加载和显存 profiling。日志里看到类似 KV cache blocks: N、Maximum concurrency for ... requests 和 Application startup complete,就说明服务起来了。

验证部署是否成功

1. 健康检查

```bash

curl -s http://<机器A业务IP>:8000/health -o /dev/null -w '%{http_code}\n'

```

预期返回 200。

2. 模型列表

```bash

curl -s http://<机器A业务IP>:8000/v1/models | python3 -m json.tool

```

预期能看到 "id": "glm-5.3"。

3. 跑一次真实推理

```bash

curl -s http://<机器A业务IP>:8000/v1/chat/completions \

-H 'Content-Type: application/json' \

-d '{

"model": "glm-5.3",

"messages": [{"role": "user", "content": "用三句话解释张量并行"}],

"max_tokens": 256,

"temperature": 0.2

}' | python3 -m json.tool

```

预期返回结构完整的 choices[0].message.content,且 usage.completion_tokens 大于 0。

4. 确认两台机器都在干活

服务跑推理时,在两台机器上分别执行:

```bash

nvidia-smi --query-gpu=index,utilization.gpu,memory.used --format=csv

```

预期:两台机器的 8 张卡都有显存占用(每卡几十 GB)且 GPU 利用率同时抬起来。如果只有机器 A 在动,说明请求没分发出去,回看第 7 步的 backend 参数。

5. 压测看吞吐

```bash

vllm bench serve \

--backend openai-chat \

--model glm-5.3 \

--base-url http://<机器A业务IP>:8000 \

--endpoint /v1/chat/completions \

--dataset-name random \

--random-input-len 1024 \

--random-output-len 256 \

--num-prompts 500 \

--max-concurrency 32 \

--request-rate inf

```

预期输出里有 Request throughput、Output token throughput、Mean TTFT、Mean TPOT。要冲 600+ tok/s 这一档,做法是固定输入输出长度,把 --max-concurrency 从 8 开始按 8 / 16 / 32 / 64 / 128 逐档跑,记录每一档的输出吞吐和 TPOT,画出曲线找到拐点——吞吐不再涨、TPOT 明显变差的那一档,就是这台机器的实际工作区间。

调参的几个有效顺序:先加大 --max-num-seqs(配合并发),再看 KV Cache 是否够(日志里的 blocks 数),不够就 --kv-cache-dtype fp8 或降 --max-model-len,最后才动 --gpu-memory-utilization。通信瓶颈明显的,先把 --tensor-parallel-size 降到 8 加 --pipeline-parallel-size 2 对比一下。

常见报错与解决

1. NCCL WARN ... no device found / 跨机通信直接卡住

原因:NCCL_IB_HCA 写的网卡名和实际不符,或者容器的 /dev/infiniband 没挂进去。

解决:

```bash

ibdev2netdev # 查出真实网卡名

export NCCL_IB_HCA=<真实名>

容器启动时确保挂载

docker run --gpus all --network=host --ipc=host \

--device=/dev/infiniband \

-v /etc/profile.d/nccl.sh:/etc/profile.d/nccl.sh ...

```

2. OSError: /models/glm does not exist 或 worker 端加载权重失败

原因:两台机器模型路径不一致,或者用了 NFS 但挂载点没同步。

解决:

```bash

两台机器都执行,输出应完全一致

ls -l /models/glm | md5sum

mount | grep /models

```

统一路径后 ray stop --force 再重新拉起集群。

3. ValueError: Total number of attention heads must be divisible by tensor parallel size

原因:模型的注意力头数不能被 16 整除,TP=16 不成立。

解决:改用组合并行。

```bash

--tensor-parallel-size 8 --pipeline-parallel-size 2

```

4. No available memory for the cache blocks / 启动时 OOM

原因:--gpu-memory-utilization 太高,或者 --max-model-len 太大,权重加载完没有余量做 KV Cache。

解决:依次尝试

```bash

--gpu-memory-utilization 0.85

--max-model-len 16384

--kv-cache-dtype fp8

```

5. Ray worker 一直 PENDING,ray status 里 GPU 数只有 8

原因:worker 节点没连上头节点,或者端口被防火墙拦了。

解决:

```bash

头节点放行

sudo ufw allow 6379/tcp && sudo ufw allow 8265/tcp

worker 上重连

ray stop --force

ray start --address=<头节点IP>:6379 --num-gpus=8 --disable-usage-stats

ray status

```

6. 压测中途 CUDA out of memory,但启动时正常

原因:并发上来后激活内存和 CUDA Graph 占位叠加,启动时的余量被吃掉。

解决:降 --max-num-seqs 或 --max-model-len,同时关掉不必要的图捕获:

```bash

--enforce-eager # 先排除 CUDA Graph 因素,稳定后再开回来

```

后续维护

备份:把 /etc/profile.d/nccl.sh、Ray 启动脚本、vLLM 启动命令(建议写成 serve.sh)、压测脚本和每轮压测的原始输出,一起放进 Git 或配置仓库。压测记录要带日期、模型版本、参数组合和结果数字——只记“跑到过 600+”,下次就复现不出来了。

升级:两台机器必须同时升级,不允许一高一低混跑。升级顺序是先 ray stop --force 停服务,再统一换 vLLM / Ray / 驱动版本,然后重跑第 4 步的 nccl-tests 和第 5 步的压测基线,确认吞吐没有回退再切流量。镜像 tag 和版本号以官方文档当前版本为准,升级前先在测试环境验证。

日志:vLLM 日志按天切割,保留至少两周;NCCL_DEBUG=WARN 常开,出问题时临时调成 INFO 复现一次。Ray 的 worker 日志在 /tmp/ray/session_latest/logs/ 下,多节点故障第一现场就在这里。

监控:vLLM 自带 Prometheus 指标,curl http://<机器A业务IP>:8000/metrics 能看到队列长度、KV Cache 使用率、TTFT 等。建议采集这几个:

  • vllm:gpu_cache_usage_perc:KV Cache 使用率持续 > 0.9 就该扩容或降并发
  • vllm:num_requests_waiting:等待队列长期非零,说明并发上限设低了
  • vllm:time_to_first_token_seconds:P99 突增通常是长输入把 prefill 挤爆了

再配合 DCGM Exporter 采 GPU 的温度、功耗、XID 错误,两台机器的指标放同一个面板对照看——两边显存使用率或利用率明显不对称,基本就是切分不均或者网络单边拥塞。

日常巡检:每周跑一次小规模压测(比如 100 条请求),和基线对比;每月检查一次 RDMA 网卡的误包计数(ibstat 里的 Symbol errors、PortRcvErrors),网卡劣化是吞吐缓慢下滑的隐形原因。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。