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?
  • 怎么平滑升级模型权重?老连接怎么处理?

读完本文你将能:

  1. 用 vLLM 部署单卡 + 多卡的生产级推理服务
  2. 写出完整的 Dockerfile 和 Kubernetes YAML
  3. 配置监控(Prometheus + Grafana)和告警
  4. 设计高可用架构(多副本、滚动升级、健康检查)
  5. 排查最常见的 5 类 vLLM 部署问题

我们开始。


一、vLLM 为什么是首选

1.1 推理框架四强

回顾第 1 篇我们做过的对比:

框架 来源 特点 当下地位
vLLM UC Berkeley 易用、社区活跃 通用首选
SGLang LMSYS Agent / 结构化输出强 增长最快
TensorRT-LLM NVIDIA 极致性能 生产追求极限
TGI HuggingFace HF 生态融合 HF 用户
lmdeploy OpenMMLab 国产、轻量 中文场景

为什么本系列重点讲 vLLM?

  1. 生态最广:HuggingFace 模型几乎全支持
  2. OpenAI 兼容 API:客户端无缝迁移
  3. 社区活跃:每周都有新特性
  4. 文档完善:踩坑成本最低
  5. 生产就绪:大量公司在用

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 面板

推荐核心面板:

  1. 请求量:每秒成功 / 失败 / 排队请求
  2. 延迟分布:TTFT / TPOT / E2E(p50/p90/p99)
  3. GPU 利用率:与 vLLM 中的请求并发关联
  4. KV Cache:使用率随时间变化
  5. 错误率: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,更易看到真实占用

对策(按优先级)

  1. 降低 --gpu-memory-utilization 到 0.85
  2. 降低 --max-num-seqs
  3. 启用 --kv-cache-dtype fp8int8
  4. 启用 --quantization fp8awq
  5. 降低 --max-model-len
  6. 增加 TP

7.2 TTFT 飙升

症状:偶尔 TTFT 从 200ms 跳到 5s+。

原因:长 prompt 阻塞了短请求。

对策

--enable-chunked-prefill \
--max-num-batched-tokens 8192

7.3 服务卡死

症状:请求一直 pending,但 GPU 利用率低。

可能原因

  1. 死锁(早期 vLLM bug,升级版本)
  2. 网络问题(kubelet 重启)
  3. 显存碎片(少见,重启服务)

应急方案:配合 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 分钟。

对策

  1. 模型存放在本地 SSD(不要用 NFS)
  2. 启用 --load-format auto(vLLM 会选最快的格式)
  3. safetensors 格式(比 .bin 快 2-3×)
  4. 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 工程化、大模型部署、后端架构实战的技术专栏。
写最一线的踩坑经验,做最务实的技术拆解。

如果这篇文章对你有启发,欢迎点赞、转发、关注。我们下篇见。

Logo

免费领 200 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐