教程·阅读约 4 分钟·
vLLM 生产部署完整指南:从 Docker 到 K8s 的每一步

vLLM 生产部署完整指南:从 Docker 到 K8s 的每一步

把 vLLM 从演示级推到生产级:镜像版本锁定、GPU 内存调优、前缀缓存、KEDA 自动扩缩、监控告警,一套可以直接抄的完整配置

原文来源:SitePoint — vLLM 生产部署完整指南:从 PagedAttention 原理到 Docker/K8s 部署、性能调优、监控告警的端到端教程

跑通一个 vLLM demo 很容易,一行 vllm serve 就行。但把它变成可靠、可观测、能自动扩缩的生产服务,中间隔着一条很宽的沟。这篇指南把沟里的每一步都补齐了:架构原理、Docker 部署、Kubernetes 编排、性能调优、监控告警,全部是可复制的配置。

先说结论——生产部署的八条军规:

  1. 锁定镜像版本:永远不用 latest,pin 到具体 release tag。
  2. --max-model-len 按实际需求设,不是模型的理论上限。
  3. --gpu-memory-utilization 设在 0.85–0.95,用压测找甜点。
  4. 开 Prometheus 指标,盯 TTFT p99、队列深度、KV 缓存占用。
  5. 告警:KV 缓存饱和超 95%、TTFT p99 超 SLA 都要报警。
  6. 限流放在反向代理层,vLLM 本身不支持按用户配额。
  7. 上线前用 benchmark_serving.py 压测,模拟真实流量。
  8. 凭据走 .env / Docker secrets / K8s Secrets,绝不进命令行参数。

为什么 vLLM 快:三个核心机制

生产部署前先理解 vLLM 为什么和其他推理服务器不一样。

PagedAttention:传统推理的 KV 缓存按请求分配连续显存块,请求长度参差不齐时内部碎片严重。PagedAttention 借鉴操作系统的虚拟内存思想,把 KV 缓存切成固定大小的块(页),逻辑位置映射到不连续的物理块——碎片被回收,同一块 GPU 能塞进更多并发请求。在 80GB H100 上跑 7B FP16 模型,可能是 30 个并发和 100+ 个并发的差别。

前缀缓存:多个请求共享 system prompt 或 few-shot 前缀时,vLLM 直接复用这些 KV 块,省掉重复的 prefill 计算。聊天和 agent 场景下每个请求都带相同 system prompt,收益最明显。

V1 引擎:2025 年发布后成为默认(v0.6.0 起)。核心改动是调度时不再在 GPU/CPU 之间复制中间张量,改用 host 内存固定 + 零拷贝 DMA。更大的变化是分离 prefill 和 decode:prefill 是计算密集的,decode 是显存带宽密集的,V1 把两个阶段放进独立的调度域,长 prefill 不再阻塞在途请求的 decode——这是生产环境延迟尖峰的头号原因。配合分块 prefill,长 prompt(比如 32K token)被切成小块与 decode 批次交错,不会独占 GPU 几百毫秒。

模型支持方面:Llama 3/3.1、Mistral/Mixtral、Qwen 2/2.5、DeepSeek-V2/V3,以及 LLaVA、Qwen-VL 等多模态。量化上,AWQ(4-bit)和 GPTQ 各有校准路线,AWQ 在 vLLM 里通常吞吐高 5–15%;Hopper 的 FP8 接近无损、比 FP16 快约 2 倍;GGUF 主要适合 CPU offload 场景,GPU-first 生产不推荐。

—— 广告 ——

Docker 部署:单卡起步

镜像用 vllm/vllm-openai:<tag>,先到 GitHub releases 确认 tag 存在。单卡部署的关键参数:

code
docker run -d \
  --name vllm-server \
  --gpus '"device=0"' \
  --shm-size=4g \
  -p 8000:8000 \
  -v ~/.cache/huggingface:/root/.cache/huggingface \
  --env-file .env \
  vllm/vllm-openai:<your-release-tag> \
  --model hugging-quants/Meta-Llama-3.1-8B-Instruct-AWQ-INT4 \
  --served-model-name llama-3.1-8b \
  --max-model-len 8192 \
  --quantization awq \
  --dtype auto \
  --gpu-memory-utilization 0.90 \
  --enable-prefix-caching \
  --port 8000

.env 文件:

code
HUGGING_FACE_HUB_TOKEN=<your-hf-token>
VLLM_API_KEY=<your-api-key>

几个坑要提前避开:

  • --quantization awq 要求预量化的 AWQ 权重。基础版 meta-llama/Llama-3.1-8B-Instruct 不是 AWQ 量化过的,直接跑会报错。用 hugging-quants/...-AWQ-INT4 这类变体,或去掉这个 flag。
  • --shm-size 必须给够,NCCL 通信靠共享内存。多卡场景直接 --ipc=host
  • 凭据绝不写进 docker run 命令行——docker inspect、进程列表、shell 历史里全能看到。用 --env-file .envVLLM_API_KEY 环境变量会被 vLLM 自动读取。
  • --max-model-len 设低一点是特性不是缺陷:它直接决定 KV 缓存预留多少显存,设低释放显存给更大 batch。

多卡用张量并行切分模型,--tensor-parallel-size 必须和分配的 GPU 数一致:

code
docker run -d \
  --name vllm-server-tp4 \
  --gpus '"device=0,1,2,3"' \
  --shm-size=16g \
  --ipc=host \
  -p 8000:8000 \
  -v ~/.cache/huggingface:/root/.cache/huggingface \
  --env-file .env \
  -e NCCL_DEBUG=WARN \
  vllm/vllm-openai:<your-release-tag> \
  --model meta-llama/Llama-3.1-70B-Instruct \
  --served-model-name llama-3.1-70b \
  --tensor-parallel-size 4 \
  --max-model-len 16384 \
  --dtype auto \
  --gpu-memory-utilization 0.90 \
  --enable-prefix-caching \
  --port 8000

NCCL 在多卡间通信,共享内存不够会报出令人抓狂的运行时错误。--ipc=host 是关键。

Kubernetes 部署:生产级编排

K8s 部署有四个要点:探针、拓扑分布、PVC 缓存、KEDA 自动扩缩。

探针:vLLM 的 /health 端点加载完模型返回 200。注意 start_period: 120s——模型加载很慢,别让探针在启动期就把 pod 杀了。

拓扑分布:用 topologySpreadConstraints 让副本落在不同节点,避免单节点故障带走全部推理能力。多副本 + 共享模型缓存时 PVC 必须用 ReadWriteMany(NFS、CephFS 或云厂商 RWX 存储类),ReadWriteOnce 只允许单节点挂载。模型加载速度直接受存储吞吐影响,PVC 要用 fast SSD 存储类。

Ingress 两个关键注解

code
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-buffering: "off"

proxy-read-timeout 300 秒是为了容纳长生成请求(高 max_tokens);proxy-buffering 必须关——开着的话 Server-Sent Events 会被缓存到整个响应结束才发送,流式输出直接废掉。

KEDA 自动扩缩:扩缩信号用"每副本等待请求数",而不是 GPU 利用率——利用率高是正常现象,不表示容量耗尽:

code
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: vllm-scaler
  namespace: llm-serving
spec:
  scaleTargetRef:
    name: vllm-inference
  minReplicaCount: 1
  maxReplicaCount: 8
  cooldownPeriod: 300
  triggers:
    - type: prometheus
      metadata:
        serverAddress: http://prometheus.monitoring:9090
        metricName: vllm_requests_waiting_per_replica
        query: |
          sum(vllm:num_requests_waiting{namespace="llm-serving"})
          / scalar(
              count(up{job="vllm", namespace="llm-serving"} == 1)
              or vector(1)
            )
        threshold: "5"
        activationThreshold: "2"

cooldownPeriod: 300 防抖——GPU pod 从调度到就绪要 1–5 分钟(取决于模型大小和存储速度),快速缩容等于白烧加载时间。or vector(1) 防止全挂时除以零,保证还能从零拉起。

多节点大模型:超过单节点显存(如 Llama 3.1 405B 要 8+ 卡)需要 Ray + NCCL 跨节点,网络要求苛刻——NCCL 在普通 TCP/IP 上表现很差,生产要用 InfiniBand 或 RoCE,70B 以上模型建议至少 100 Gbps 节点间带宽。实操建议:多数场景下,多副本跑小模型(或量化版)比单模型跨节点切分吞吐更高,通信开销是主要原因。

OpenAI 兼容 API 与结构化输出

vLLM 暴露 /v1/completions/v1/chat/completions/v1/embeddings--served-model-name 可以起别名(如 llama-production),现有应用代码不用改。注意 Assistants API、fine-tuning 等 OpenAI 专属端点不支持。

生产环境建议用 guided decoding 约束结构化 JSON 输出——对 tool calling 和 agent 工作流至关重要。配合流式:

code
from openai import OpenAI
import os, json, openai
 
client = OpenAI(
    base_url="https://llm-api.example.com/v1",
    api_key=os.environ["VLLM_API_KEY"],
)
 
try:
    response = client.chat.completions.create(
        model="llama-3.1-8b",
        messages=[
            {"role": "system", "content": "Extract entities as JSON."},
            {"role": "user", "content": "Apple announced the M4 chip in Cupertino."},
        ],
        max_tokens=512,
        temperature=0.1,
        top_p=0.95,
        stream=True,
        response_format={
            "type": "json_schema",
            "json_schema": {
                "name": "entities",
                "schema": {
                    "type": "object",
                    "properties": {
                        "entities": {
                            "type": "array",
                            "items": {
                                "type": "object",
                                "properties": {
                                    "name": {"type": "string"},
                                    "type": {"type": "string"},
                                },
                                "required": ["name", "type"],
                            },
                        }
                    },
                    "required": ["entities"],
                },
            },
        },
    )
    response_parts = []
    for chunk in response:
        if chunk.choices and chunk.choices[0].delta.content is not None:
            response_parts.append(chunk.choices[0].delta.content)
    print(json.loads("".join(response_parts)))
except openai.AuthenticationError as e:
    raise RuntimeError(f"认证失败,检查 VLLM_API_KEY: {e}") from e
except openai.RateLimitError as e:
    print(f"被限流: {e}")
    raise
except openai.APIConnectionError as e:
    print(f"连接错误(可重试): {e}")
    raise

性能调优三板斧

显存调优--gpu-memory-utilization 默认 0.90,压测后可以微调。--enforce-eager 关闭 CUDA graph 捕获,省 5–15% 显存(30B 以上模型),接近显存上限时有用。

批处理调优:continuous batching 动态把新请求加进在途批次。--max-num-seqs 默认 256,调低减内存压力但压吞吐;--max-num-batched-tokens 控制每轮 token 预算。延迟敏感场景开分块 prefill 并调小块,防止 decode 被长 prefill 阻塞。

投机解码:decode-bound 场景(延迟主导)用一个小 draft 模型并行提议 token,主模型验证,draft 接受率 ≥0.7 时提速 1.3–2 倍。draft 模型匹配不好可能没收益甚至倒退。配置:--speculative-model + --num-speculative-tokens

压测:用 vLLM 仓库自带的 benchmark_serving.py

code
git clone https://github.com/vllm-project/vllm.git && cd vllm
python benchmarks/benchmark_serving.py \
  --backend vllm \
  --endpoint /v1/completions \
  --model llama-3.1-8b \
  --dataset-name sharegpt \
  --num-prompts 500 \
  --request-rate 10 \
  --base-url http://localhost:8000

重点对比不同 --quantization--max-num-seqs 下的吞吐和 TTFT。

监控与告警

vLLM 暴露 /metrics(Prometheus 格式)。核心指标:

  • vllm:num_requests_running — 活跃请求数
  • vllm:num_requests_waiting — 队列深度,容量耗尽的主要信号
  • vllm:gpu_cache_usage_perc — KV 缓存饱和度
  • vllm:time_to_first_token_seconds — TTFT 直方图
  • vllm:e2e_request_latency_seconds — 端到端延迟直方图

Grafana 面板建议:请求吞吐、TTFT p50/p95/p99、KV 缓存利用率、队列深度、端到端延迟分位数。注意指标单位gpu_cache_usage_perc 有的版本报 0–100 百分比,有的报 0.0–1.0 小数,先用 curl http://<pod>:8000/metrics | grep gpu_cache_usage_perc 确认再设阈值。

告警规则:KV 缓存占用超 95% 持续 2 分钟(容量耗尽、请求即将被拒)、TTFT p99 超 SLA、错误率异常飙升。

请求级追踪:反向代理传入自定义 X-Request-ID,在客户端、代理、推理服务器日志中关联,就能做端到端定位。vLLM 日志自带请求 ID 和时间分解。

高可用与安全检查清单

多副本 + Service 提供基本 HA,/health 让负载均衡绕开不健康的 pod。terminationGracePeriodSeconds 设 60–120 秒,让在途请求完成再杀 pod。PVC 模型缓存把冷启动从分钟级(网络下载)降到秒级(本地磁盘加载)。

安全方面:vLLM 不做按用户限流,多租户必须靠反向代理(Nginx/Envoy/API 网关)做 per-client 配额;K8s 里用 NetworkPolicy 把 vLLM pod 的入口限制为只允许反向代理访问,防止其他工作负载直连。

最后过一遍 checklist:锁定镜像 tag、按需设 --max-model-len、压测调 --gpu-memory-utilization、开指标建面板、配 KV 饱和和 TTFT 告警、反代限流、真实流量压测、凭据走环境变量。八条全过,才算生产就绪。

分享到
微博Twitter

© 2026 四月

原文链接:https://www.aprilzz.com/tutorials/vllm-production-deployment-guide