16.vLLM 部署实战:从单卡到多卡的高性能推理服务
vLLM 部署实战:从单卡到多卡的高性能推理服务
《大模型知识与部署》系列 · No.16 / 35(部署服务化篇开篇)
适合人群:AI 工程师、后端开发、运维
阅读时间:约 30 分钟

写在前面
前 15 篇我们走过了认知 → 训练 → 推理优化的完整路径。从这一篇开始,我们进入整个系列的重头戏——部署服务化篇(第 16-20 篇)。
为什么这是重头戏?
回到系列规划的初衷——这个专栏叫**「大模型系列」,定位是后端工程师视角的 AI 工程化**。前面 15 篇打基础,从这一篇开始才是"真正干活"的内容:
- 拿到一个模型权重,怎么把它变成稳定可用的 API 服务?
- 单卡跑不下大模型怎么办?要几张卡?
- 怎么上 Docker / Kubernetes ?怎么做监控、告警、滚动升级?
- 长尾延迟怎么治?OOM 怎么排查?
这一篇我们就从最具代表性的推理框架——vLLM——开始。读完之后你应该能从零部署一个生产级的大模型 API 服务。
如果你做过相关工作,下面这些问题应该不陌生:
- 装了 vLLM 但启动报错?为什么不同 GPU 配置要不同启动参数?
- TP=4 和 PP=2 应该选哪个?怎么混合?
- vLLM 跑着跑着 OOM 了,是哪里设置错了?
- 想多副本部署,前面要不要加 nginx?还是用 K8s?
- 怎么平滑升级模型权重?老连接怎么处理?
读完本文你将能:
- 用 vLLM 部署单卡 + 多卡的生产级推理服务
- 写出完整的 Dockerfile 和 Kubernetes YAML
- 配置监控(Prometheus + Grafana)和告警
- 设计高可用架构(多副本、滚动升级、健康检查)
- 排查最常见的 5 类 vLLM 部署问题
我们开始。
一、vLLM 为什么是首选
1.1 推理框架四强
回顾第 1 篇我们做过的对比:
| 框架 | 来源 | 特点 | 当下地位 |
|---|---|---|---|
| vLLM | UC Berkeley | 易用、社区活跃 | 通用首选 ⭐ |
| SGLang | LMSYS | Agent / 结构化输出强 | 增长最快 |
| TensorRT-LLM | NVIDIA | 极致性能 | 生产追求极限 |
| TGI | HuggingFace | HF 生态融合 | HF 用户 |
| lmdeploy | OpenMMLab | 国产、轻量 | 中文场景 |
为什么本系列重点讲 vLLM?
- 生态最广:HuggingFace 模型几乎全支持
- OpenAI 兼容 API:客户端无缝迁移
- 社区活跃:每周都有新特性
- 文档完善:踩坑成本最低
- 生产就绪:大量公司在用
1.2 vLLM 当下能力清单(2026.05)
vLLM 已经把第 11-15 篇讲的所有优化集成了:
- ✅ PagedAttention + Continuous Batching(核心)
- ✅ Flash Attention v2 / v3
- ✅ Tensor Parallel + Pipeline Parallel + Context Parallel
- ✅ AWQ / GPTQ / FP8 / INT8 量化
- ✅ Prefix Caching + Chunked Prefill
- ✅ EAGLE / Medusa 投机解码
- ✅ YaRN 长上下文
- ✅ Multi-LoRA 热加载
- ✅ OpenAI 兼容 API
- ✅ Prometheus metrics
- ✅ 流式输出(SSE)
意思是:第 11-15 篇讲过的所有优化,vLLM 默认就帮你做了或者一个参数就能开启。
二、从零起步:单卡部署
2.1 环境准备
硬件要求:
| 模型规模 | 推荐显卡 |
|---|---|
| 7B (FP16) | A10 24GB / 4090 24GB |
| 7B (INT4) | 任何 12GB+ GPU |
| 14B (FP16) | A100 40GB / L40 48GB |
| 32B (INT4) | A100 40GB / 4090 24GB(紧张) |
| 70B (INT4) | A100 80GB / H100 80GB |
| 70B (FP16) | 2 × H100 80GB |
软件要求:
# CUDA 12.x,Python 3.10+
# 推荐用 conda 环境
conda create -n vllm python=3.10
conda activate vllm
# 安装 vLLM
pip install vllm==0.6.4
# 验证安装
python -c "import vllm; print(vllm.__version__)"
2.2 最简启动
最快上手的命令:
vllm serve Qwen/Qwen3-7B-Instruct
就这一句话——vLLM 会自动:
- 从 HuggingFace 下载模型
- 启动 OpenAI 兼容 API(默认端口 8000)
- 启用所有默认优化(PagedAttention、Continuous Batching 等)
测试:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen3-7B-Instruct",
"messages": [{"role": "user", "content": "你好"}]
}'
输出:
{
"id": "chat-xxx",
"object": "chat.completion",
"choices": [{
"message": {"role": "assistant", "content": "你好!有什么可以帮您的吗?"},
...
}]
}
完全 OpenAI 兼容——前端的 OpenAI SDK 客户端代码不用改一行就能切换过来。
2.3 用 Python SDK 调用
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="dummy" # vLLM 默认不验证
)
response = client.chat.completions.create(
model="Qwen/Qwen3-7B-Instruct",
messages=[{"role": "user", "content": "解释一下 KV Cache"}],
stream=True, # 流式输出
)
for chunk in response:
print(chunk.choices[0].delta.content or "", end="", flush=True)
三、生产级配置:所有最优参数
最简启动只是 demo。生产部署要把所有优化开关打开。
3.1 完整生产级启动命令
vllm serve Qwen/Qwen3-32B-Instruct \
\
`# === 模型相关 ===` \
--model Qwen/Qwen3-32B-Instruct \
--served-model-name qwen3-32b \
--tokenizer Qwen/Qwen3-32B-Instruct \
--trust-remote-code \
--dtype bfloat16 \
\
`# === 上下文与精度 ===` \
--max-model-len 32768 \
--quantization fp8 \
--kv-cache-dtype fp8 \
\
`# === 并行配置(单机 4 卡)===` \
--tensor-parallel-size 4 \
--pipeline-parallel-size 1 \
\
`# === 推理优化 ===` \
--enable-prefix-caching \
--enable-chunked-prefill \
--max-num-batched-tokens 16384 \
--max-num-seqs 256 \
\
`# === 显存管理 ===` \
--gpu-memory-utilization 0.92 \
--swap-space 16 \
\
`# === 服务相关 ===` \
--host 0.0.0.0 \
--port 8000 \
--api-key sk-your-secret-key \
--uvicorn-log-level info \
\
`# === 监控 ===` \
--disable-log-requests
3.2 关键参数详解
| 参数 | 含义 | 推荐值 |
|---|---|---|
--served-model-name |
API 返回的 model 字段 | 短名(如 qwen3-32b) |
--dtype |
计算精度 | bfloat16(A100/H100)/ float16(V100) |
--max-model-len |
最大上下文长度 | 看模型能力 + 显存 |
--quantization |
模型权重量化 | fp8 / awq / gptq |
--kv-cache-dtype |
KV Cache 精度 | fp8 / int8 |
--tensor-parallel-size |
TP 数 | 通常 = GPU 数(单机) |
--pipeline-parallel-size |
PP 数 | 通常 1(多机才用) |
--enable-prefix-caching |
KV Cache 复用 | 生产强烈推荐 |
--enable-chunked-prefill |
分块 prefill | 生产推荐 |
--max-num-batched-tokens |
单次 batch 总 token | 长上下文场景 8192-16384 |
--max-num-seqs |
并发上限 | 看显存 |
--gpu-memory-utilization |
显存上限比例 | 0.85-0.95 |
--swap-space |
CPU swap | 16(GB) |
--api-key |
鉴权 key | 必须设置 |
3.3 调优 Checklist
部署后必须验证的指标:
# 1. 服务健康
curl http://localhost:8000/health
# 应返回 200 OK
# 2. 模型列表
curl http://localhost:8000/v1/models -H "Authorization: Bearer sk-xxx"
# 3. 实际推理
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-xxx" \
-d '{"model":"qwen3-32b","messages":[{"role":"user","content":"hi"}]}'
# 4. Metrics
curl http://localhost:8000/metrics
四、多卡部署:TP / PP / 数据并行
模型一大,单卡装不下。这时候要上多卡。
4.1 三种并行策略选择
| 并行方式 | 切分目标 | 适用场景 | 通信开销 |
|---|---|---|---|
| Tensor Parallel (TP) | 单层算子 | 单机多卡 | 高(要 NVLink) |
| Pipeline Parallel (PP) | 模型层 | 多机部署 | 中(跨机) |
| Data Parallel (DP) | 不同请求 | 高并发 | 低(独立) |
核心规则:
TP 用于"装下"模型,DP 用于"扩大"并发,PP 用于跨机部署。
4.2 单机多卡:纯 TP(最常见)
8 卡 H100 部署 Llama-3-70B:
vllm serve meta-llama/Llama-3-70B-Instruct \
--tensor-parallel-size 8 \
--dtype bfloat16 \
--max-model-len 32768 \
--enable-prefix-caching \
--enable-chunked-prefill \
--gpu-memory-utilization 0.92 \
--port 8000
TP 的注意事项:
- TP 数必须是模型
num_attention_heads的因子(如 64 → TP 可以是 1/2/4/8/16) - TP 通常 ≤ 一个节点的 GPU 数(跨机 TP 通信开销大)
- 单机 8 卡 H100 用 TP=8 是常见配置
4.3 多机多卡:TP + PP
跨 2 台机器 × 8 卡 = 16 卡:
# Master 节点
vllm serve meta-llama/Llama-3-405B-Instruct \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--distributed-executor-backend ray \
--num-nodes 2 \
--node-rank 0 \
...
# Worker 节点
ray start --address=master_ip:6379
# 然后等 master 调度
实战提示:
- 单机优先 TP,跨机才用 PP
- 跨机互联要 100Gbps+ InfiniBand
- 用 Ray 作为分布式执行器
4.4 多副本:DP(业务层)
如果想提高吞吐 / 高可用,部署多个独立 vLLM 副本,前面加负载均衡:
┌─ vLLM 副本 1 (GPU 0-3)
nginx / nginx-LB ─┼─ vLLM 副本 2 (GPU 4-7)
└─ vLLM 副本 3 (其他机器)
nginx 配置示例:
upstream vllm_backend {
least_conn;
server vllm-1:8000 max_fails=3 fail_timeout=30s;
server vllm-2:8000 max_fails=3 fail_timeout=30s;
server vllm-3:8000 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
location / {
proxy_pass http://vllm_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off; # 重要!流式输出需要
proxy_read_timeout 600s;
}
}
关键参数:
least_conn:选连接最少的副本(不要用默认 round_robin)proxy_buffering off:流式输出必须关闭缓冲proxy_read_timeout 600s:长输出场景要够长
五、Docker / Kubernetes 部署
5.1 Dockerfile
# 基于 NVIDIA 官方 CUDA 镜像
FROM nvidia/cuda:12.4.1-cudnn-runtime-ubuntu22.04
# 安装 Python 和系统依赖
RUN apt-get update && apt-get install -y \
python3.10 python3-pip git curl \
&& rm -rf /var/lib/apt/lists/*
# 安装 vLLM
RUN pip install --no-cache-dir vllm==0.6.4
# 设置工作目录
WORKDIR /app
# 模型缓存目录(用 volume 挂载)
ENV HF_HOME=/models
VOLUME ["/models"]
# 暴露端口
EXPOSE 8000
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# 启动命令通过环境变量传入
CMD ["sh", "-c", "vllm serve $MODEL_NAME --host 0.0.0.0 --port 8000 $EXTRA_ARGS"]
Build & Run:
docker build -t my-vllm:0.6.4 .
# 启动(单卡)
docker run -d --name vllm \
--gpus all \
-p 8000:8000 \
-v /data/models:/models \
-e MODEL_NAME="Qwen/Qwen3-7B-Instruct" \
-e EXTRA_ARGS="--enable-prefix-caching --gpu-memory-utilization 0.9" \
--shm-size 8g \
my-vllm:0.6.4
⚠️ 关键点:
--gpus all必须,否则容器看不到 GPU--shm-size 8g必须,PyTorch 多进程通信需要- 模型 volume 挂载,避免每次启动重新下载
5.2 docker-compose.yml(开发环境)
version: "3.8"
services:
vllm:
image: my-vllm:0.6.4
container_name: vllm-qwen3
runtime: nvidia
environment:
- MODEL_NAME=Qwen/Qwen3-32B-Instruct
- EXTRA_ARGS=--tensor-parallel-size 4 --quantization fp8 --max-model-len 32768
- HF_TOKEN=${HF_TOKEN}
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 4
capabilities: [gpu]
ports:
- "8000:8000"
volumes:
- /data/models:/models
shm_size: 16g
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
5.3 Kubernetes 部署
Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-qwen3
namespace: ai
spec:
replicas: 3 # 3 个副本
selector:
matchLabels:
app: vllm-qwen3
template:
metadata:
labels:
app: vllm-qwen3
spec:
containers:
- name: vllm
image: my-vllm:0.6.4
env:
- name: MODEL_NAME
value: "Qwen/Qwen3-32B-Instruct"
- name: EXTRA_ARGS
value: "--tensor-parallel-size 4 --quantization fp8 --max-model-len 32768 --enable-prefix-caching"
resources:
limits:
nvidia.com/gpu: 4 # 每个 Pod 4 张 GPU
memory: 128Gi
requests:
nvidia.com/gpu: 4
memory: 96Gi
ports:
- containerPort: 8000
volumeMounts:
- name: models
mountPath: /models
- name: dshm
mountPath: /dev/shm
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 300 # 模型加载慢,给足时间
periodSeconds: 30
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 180
periodSeconds: 10
volumes:
- name: models
persistentVolumeClaim:
claimName: model-pvc
- name: dshm
emptyDir:
medium: Memory
sizeLimit: 16Gi
nodeSelector:
gpu-type: h100
Service + Ingress
apiVersion: v1
kind: Service
metadata:
name: vllm-qwen3
namespace: ai
spec:
selector:
app: vllm-qwen3
ports:
- port: 80
targetPort: 8000
sessionAffinity: ClientIP # ⚠ Prefix Caching 场景建议会话粘性
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vllm-qwen3
namespace: ai
annotations:
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
spec:
rules:
- host: llm-api.internal
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vllm-qwen3
port:
number: 80
滚动升级策略
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0 # 不允许同时下线
maxSurge: 1 # 只能多创建 1 个
滚动升级的关键考虑:
- 模型加载慢(70B 加载需 3-5 分钟),不能太激进
maxUnavailable: 0保证总有可用副本- 配合健康检查,确保新副本完全就绪后才下旧的
六、监控、告警、日志
6.1 Prometheus 监控
vLLM 内置 Prometheus metrics 端点:
curl http://localhost:8000/metrics
核心 metrics:
| Metric | 含义 | 告警阈值 |
|---|---|---|
vllm:time_to_first_token_seconds_histogram |
TTFT | p99 > 2s |
vllm:time_per_output_token_seconds_histogram |
TPOT | p99 > 100ms |
vllm:request_success_total |
成功请求 | - |
vllm:num_requests_running |
运行中请求 | - |
vllm:num_requests_waiting |
等待中请求 | > max_num_seqs × 80% |
vllm:gpu_cache_usage_perc |
KV Cache 使用率 | > 95% |
vllm:e2e_request_latency_seconds_histogram |
端到端延迟 | p99 > 30s |
Prometheus 配置:
scrape_configs:
- job_name: 'vllm'
static_configs:
- targets: ['vllm-1:8000', 'vllm-2:8000', 'vllm-3:8000']
metrics_path: /metrics
scrape_interval: 15s
6.2 Grafana 面板
推荐核心面板:
- 请求量:每秒成功 / 失败 / 排队请求
- 延迟分布:TTFT / TPOT / E2E(p50/p90/p99)
- GPU 利用率:与 vLLM 中的请求并发关联
- KV Cache:使用率随时间变化
- 错误率:5xx 错误 / OOM
6.3 告警规则
groups:
- name: vllm
rules:
# TTFT 过高
- alert: HighTTFT
expr: histogram_quantile(0.99, rate(vllm:time_to_first_token_seconds_histogram_bucket[5m])) > 2
for: 5m
annotations:
summary: "TTFT p99 > 2s"
# KV Cache 接近满
- alert: KVCacheFull
expr: vllm:gpu_cache_usage_perc > 0.95
for: 2m
annotations:
summary: "KV Cache 使用率 > 95%"
# 排队请求堆积
- alert: HighQueueDepth
expr: vllm:num_requests_waiting > 50
for: 3m
annotations:
summary: "排队请求 > 50"
# 服务 down
- alert: VLLMDown
expr: up{job="vllm"} == 0
for: 1m
annotations:
summary: "vLLM 副本下线"
6.4 日志聚合
vLLM 的日志输出到 stdout。在 K8s 中:
kubectl logs -f deployment/vllm-qwen3 -n ai
生产建议接到 ELK / Loki:
# Pod 注解
annotations:
fluentd.io/include: "true"
fluentd.io/parser: "json"
七、常见问题排查
7.1 OOM(最常见)
症状:启动时报 CUDA out of memory。
排查:
# 启动时打印显存使用情况
vllm serve ... --enforce-eager # 关闭 cuda graph,更易看到真实占用
对策(按优先级):
- 降低
--gpu-memory-utilization到 0.85 - 降低
--max-num-seqs - 启用
--kv-cache-dtype fp8或int8 - 启用
--quantization fp8或awq - 降低
--max-model-len - 增加 TP
7.2 TTFT 飙升
症状:偶尔 TTFT 从 200ms 跳到 5s+。
原因:长 prompt 阻塞了短请求。
对策:
--enable-chunked-prefill \
--max-num-batched-tokens 8192
7.3 服务卡死
症状:请求一直 pending,但 GPU 利用率低。
可能原因:
- 死锁(早期 vLLM bug,升级版本)
- 网络问题(kubelet 重启)
- 显存碎片(少见,重启服务)
应急方案:配合 K8s liveness probe 自动重启。
7.4 流式输出中断
症状:客户端流式输出中途断开。
原因:通常是 nginx / ingress 的 buffering 或 timeout。
对策:
proxy_buffering off;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
7.5 模型加载慢
症状:每次启动 70B 模型要 5-10 分钟。
对策:
- 模型存放在本地 SSD(不要用 NFS)
- 启用
--load-format auto(vLLM 会选最快的格式) - 用
safetensors格式(比.bin快 2-3×) - K8s 中给 livenessProbe 设
initialDelaySeconds: 300+
八、扩展话题与下一篇预告
8.1 vLLM 的"豪华套餐"
把前 15 篇所有优化打包到一个 vLLM 启动命令:
vllm serve Qwen/Qwen3-32B-Instruct \
--tensor-parallel-size 4 \
--max-model-len 131072 \
\
`# 量化优化(第 12 篇)` \
--quantization fp8 \
--kv-cache-dtype fp8 \
\
`# Flash Attention(第 13 篇)` \
`# 默认已启用 v3 (H100+)` \
\
`# 投机解码(第 14 篇)` \
--speculative-model yuhuili/EAGLE-LLaMA3-8B \
--num-speculative-tokens 5 \
\
`# 长上下文(第 15 篇)` \
--rope-scaling '{"type":"yarn","factor":4.0}' \
\
`# 推理三板斧(第 11 篇)` \
--enable-prefix-caching \
--enable-chunked-prefill \
\
--gpu-memory-utilization 0.92 \
--max-num-seqs 256 \
--port 8000
这就是 2026 年 vLLM 的"生产 SOTA 配置"。
8.2 不推荐 vLLM 的场景
虽然 vLLM 强,但有些场景不太合适:
- 需要复杂控制流 / JSON 严格输出 → SGLang
- 追求极致单卡性能 → TensorRT-LLM
- 嵌入式 / 端侧 → llama.cpp / Ollama
- 轻量本地开发 → Ollama
- HF 生态深度绑定 → TGI
下一篇我们会做完整的横向对比。
8.3 下一篇预告
- 第 17 篇:推理框架横评 - vLLM / TGI / TensorRT-LLM / SGLang —— 同样的硬件、同样的模型,4 个主流推理框架的性能、易用性、生态对比。看完这一篇你会知道每种场景选哪个框架。
- 之后是本地化部署(18 篇)、OpenAI 兼容 API 设计(19 篇)、分布式推理(20 篇)。
结语:vLLM 是大模型部署的「事实标准」
读完本文你应该明白:
- vLLM 是大多数场景的首选——生态、易用性、性能三优
- 单卡部署 → 多卡 TP → 多机 TP+PP → 多副本 DP,按业务规模选配
- 生产部署的关键是「全套优化 + 完整监控 + 高可用」
- Docker / K8s 部署有特殊注意事项:shm_size、健康检查、滚动升级
- 5 类常见问题(OOM、TTFT 飙升、卡死、流式中断、加载慢)都有套路化排查
下一篇我们继续部署服务化篇。我们下篇见。
📮 关于「码海寻道」
这里是一个聚焦 AI 工程化、大模型部署、后端架构实战的技术专栏。
写最一线的踩坑经验,做最务实的技术拆解。如果这篇文章对你有启发,欢迎点赞、转发、关注。我们下篇见。
更多推荐

所有评论(0)