用 vLLM 在 VPS 上部署 OpenAI 兼容推理 API:从零到生产(2026)

手把手教你用 vLLM 在 GPU VPS 上拉起 OpenAI 兼容的 /v1/chat/completions 端点:Docker 部署、显存估算、张量并行、AWQ/GPTQ/FP8 量化、多 LoRA、网关鉴权与冷启动处理。

延伸阅读

更多相关攻略推荐:【API 中转 02】ChatGPT/Claude API 中转 V炒币/外汇 EA 机器人用什么 VPS?低延迟到交易所的选机指南量化/EA 交易要低延迟:跑外汇机器人该选哪台 VPS?就近机房实测2026 多语言/多区域 SEO 技术栈:hreflang+CDN Tailscale 连上了却卡成狗?自建 DERP/ZeroTier

为什么是 vLLM:不是又一个推理框架

到了 2026 年,如果你要在自己的服务器上把开源大模型当成 API 用,vLLM 基本是绕不开的选择。它和裸跑 transformers 的最大区别,在于两个工程化设计:PagedAttention 和连续批处理(continuous batching)。

PagedAttention 把 KV 缓存像操作系统管理内存一样分页,不再为每条请求预留一整块连续显存,显存碎片问题基本消失。连续批处理则让新进来的请求可以随时插进正在解码的批次里,GPU 几乎不再空转。这两点叠加,吞吐比 naive 方式高几个量级,这也是为什么大量自建推理服务都站在 vLLM 肩膀上。

对开发者最友好的地方是:vLLM 自带一套 OpenAI 兼容端点,包括 /v1/chat/completions、/v1/completions、/v1/embeddings。你原来给 OpenAI 写的 SDK 代码,只要把 base_url 指到自己的服务器,其余一行都不用改。换句话说,原本调 OpenAI 付费接口的产品后端,可以无缝切到自建模型。

先算账:显存怎么估(VRAM 数学)

实测里最容易踩的坑,不是命令写错,而是机器一启动就 OOM。根因是很多人只算了权重大小,忘了 KV 缓存和并发开销。给你一个能心算的公式:

第一步,权重占用 ≈ 参数量 × 每参数字节数。fp16/bf16 是 2 字节,fp8/int8 是 1 字节,int4 是 0.5 字节。第二步,实际占用还要再乘 1.3 到 2.0 的系数,这部分就是 KV 缓存、显存碎片和并发请求的开销。长上下文、高并发场景取上限。

举个具体例子,Qwen3-32B(约 328 亿参数,稠密模型,Apache-2.0):

  • fp16:32.8B × 2 ≈ 64GB 权重,加上 KV 缓存就超过单张 80GB 卡,必须双卡张量并行,或者直接上量化。
  • fp8:32.8B × 1 ≈ 32GB 权重,单张 H100(80GB)轻松装下,还能留不少空间给 KV 缓存。

常用的显存基准记住三个数:H100 是 80GB,H200 是 141GB,B200 是 192GB。配合上面的乘法,你基本上能秒算出任意模型该租几张卡。对独立开发者来说,约 30B 的稠密模型是甜点区,单卡就能跑,不用上集群。

一行 Docker 拉起 /v1/chat/completions

官方镜像 vllm/vllm-openai 的入口命令就是 vllm serve,所以镜像名后面的所有参数都是 serve 的参数。最朴素的起法:

docker run -d --gpus all --ipc=host -p 8000:8000 \
  -v $HOME/.cache/huggingface:/root/.cache/huggingface \
  vllm/vllm-openai:latest \
  --model Qwen/Qwen3-8B \
  --tensor-parallel-size 1 \
  --gpu-memory-utilization 0.9 \
  --max-model-len 8192 \
  --api-key $VLLM_API_KEY

其中 --ipc=host 很重要,它让容器共享宿主机内存,避免多进程通信时共享内存不足。--gpu-memory-utilization 默认就是 0.9,留给 CUDA 内核一点余量,别一上来就拉到 0.99。

启动后用 docker logs -f 看加载进度,确认出现 serving on http://0.0.0.0:8000 之类的日志再测。最小验证:

curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $VLLM_API_KEY" \
  -d '{"model":"Qwen/Qwen3-8B","messages":[{"role":"user","content":"你好,介绍一下你自己"}],"max_tokens":200}'

如果能正常返回 content 字段,说明端点已经跑通。在 Vultr 这类提供 GPU 实例的厂商开一台带 H100 的机器,按上面命令几分钟就能拉起一个可对话的 API。

量化:AWQ / GPTQ / FP8 到底怎么选

显存不够是常态,量化是首选解法。先说一个重要前提:vLLM 不会在运行时从 bf16 现场量化,你必须准备一份已经量化好的检查点。社区里 TheBloke 等账号提供大量现成量化权重,Qwen 官方也发布了 FP8 检查点。

  • AWQ(激活感知权重量化):它会根据激活幅度找出对量化最敏感的那部分通道,保留更高精度,所以 4-bit 下质量损失比朴素四舍五入小。Ampere 及以上架构可以用 awq_marlin 内核进一步加速。
  • GPTQ:用二阶 Hessian 信息逐层最小化量化误差,社区权重极多,支持度广。质量在同样比特数下和 AWQ 接近,差别主要看你的具体任务。
  • FP8:H100、H200、Blackwell 有原生 FP8 张量核,吞吐最高,而且 Qwen 官方直接提供 FP8 检查点,开箱即用。注意 A100、A10G 这类 Ampere 卡没有原生 FP8,千万别在它们上面强行用 fp8,否则要么报错要么退回慢路径。

起一个 FP8 模型:

docker run -d --gpus all --ipc=host -p 8000:8000 \
  vllm/vllm-openai:latest \
  --model Qwen/Qwen3-32B-FP8 \
  --quantization fp8 \
  --tensor-parallel-size 1 \
  --max-model-len 16384

如果只能用 4-bit,可以选 AWQ 检查点:

vllm serve Qwen/Qwen3-8B-AWQ \
  --quantization awq \
  --tensor-parallel-size 1 \
  --gpu-memory-utilization 0.95

多卡与张量并行

模型装不进单卡时,用 --tensor-parallel-size N 把权重切到 N 张卡上。比如 Qwen3-32B 的 fp16 权重约 64GB,单张 80GB 卡装了权重就没空间给 KV 缓存,稳妥做法是双卡:

vllm serve Qwen/Qwen3-32B \
  --tensor-parallel-size 2 \
  --gpu-memory-utilization 0.9 \
  --max-model-len 16384

卡间互联带宽直接影响并行效率,NVLink 全互联的机器明显更顺。Vultr 的 H100 实例多卡之间带宽充足,很适合这类张量并行场景。再大的模型可以叠加 --pipeline-parallel-size 做流水线并行,跨机则靠 Ray 编排。

一个模型服务多个微调版:多 LoRA

如果你的产品需要同时支持多个微调版本(比如不同行业、不同风格的对话),给每个版本都加载一份完整模型太浪费。vLLM 支持动态加载 LoRA 适配器:启动时开启 --enable-lora,用 --lora-modules 把名字映射到路径,运行时按名字调用,显存只占一份 LoRA 增量。

vllm serve Qwen/Qwen3-8B \
  --enable-lora \
  --max-loras 4 \
  --max-lora-rank 64 \
  --lora-modules sql-lora=/models/sql-lora code-lora=/models/code-lora

调用时,请求里的 model 字段直接填 LoRA 名字(例如 sql-lora)即可,无需重启服务。这对做个性化模型服务的团队非常省显存、好管理。

网关鉴权、限速与兜底

vLLM 自带的 --api-key 只能配一个共享密钥,单人开发够用,但团队一多就乱了:谁在调、花了多少、崩了怎么办都说不清。生产环境建议在前面挂一个网关,比如 LiteLLM。

LiteLLM 是一个开源代理,能在 vLLM 前面提供每用户虚拟密钥、预算配额、请求路由,以及最关键的兜底能力——当本地 GPU 节点冷启动还没起来时,自动把请求转发到商业 API,等对端恢复再切回本地,对客户端完全透明。

model_list:
  - model_name: local-qwen
    litellm_params:
      model: openai/Qwen/Qwen3-8B
      api_base: http://vllm:8000/v1
      api_key: $VLLM_API_KEY
  - model_name: fallback-gpt
    litellm_params:
      model: gpt-4o
      api_key: $OPENAI_API_KEY
router_settings:
  fallbacks:
    - {"local-qwen": ["fallback-gpt"]}

启动网关:litellm --config litellm_config.yaml --port 4000,客户端统一指向 http://网关:4000/v1。如果你不想引入 LiteLLM,也可以用十几行 FastAPI 反向代理做 key 校验和限速,但 LiteLLM 开箱即用更省心。

冷启动与运维:把坑提前填好

多 GB 模型加载进显存需要时间,第一个请求往往会卡很久,甚至因为平台健康检查超时而被判死、被重启。几个实测有效的做法:

  • 健康检查一定打 /health 这个 vLLM 自带端点,并且给平台 health check 一个足够长的启动宽限期(比如 180 秒起步,大模型更要加)。
  • 延迟敏感的服务不要缩容到零,保持实例常热;如果一定要省成本,就用上面说的 LiteLLM 兜底挡住冷启动期间的请求。
  • 监控看 /metrics:gpu_cache_usage_perc 是 KV 缓存占用,num_requests_waiting 是排队数,time_to_first_token 是首 token 延迟。KV 缓存满了就降 --max-model-len 或调高 --gpu-memory-utilization。

把 API 暴露到公网时,建议走一层反向代理加 HTTPS,别直接把 8000 端口裸奔出去。对于面向国内用户的场景,DMIT 这类高端线路机房带宽足、延迟低,很适合把自建推理 API 暴露给国内调用方,配合网关鉴权一起用更稳。

实测:把 OpenAI SDK 指过来

客户端改造量几乎为零,只换 base_url。下面这段 Python 用官方 openai 库直接打你自己的端点:

from openai import OpenAI

client = OpenAI(
    base_url="http://你的服务器IP:8000/v1",
    api_key="你的VLLM_API_KEY",
)

resp = client.chat.completions.create(
    model="Qwen/Qwen3-8B",
    messages=[{"role": "user", "content": "用一句话解释 PagedAttention"}],
    max_tokens=200,
    temperature=0.7,
)
print(resp.choices[0].message.content)

只要模型名和你在 vLLM 里 --served-model-name 或 --model 指定的对应上,原来所有 OpenAI 的工具调用、流式输出、JSON 模式都能照常工作。前端工具如 OpenWebUI、LiteLLM、Continue、Cline 也都天然兼容这个端点。

前缀缓存与压测:上线前先量一遍

很多团队直接裸上生产,结果第一个流量高峰就排队超时。两个上线前必做动作。第一,开启 --enable-prefix-caching(vLLM v1 默认开,但老版本要显式加),它能复用相同系统提示前缀的 KV 块,对成百上千请求共享同一条 system prompt 的场景,首 token 延迟能降三成以上。第二,用 vLLM 自带的压测脚本量一遍真实吞吐:

python benchmarks/benchmark_serving.py \
  --backend vllm \
  --model Qwen/Qwen3-8B \
  --dataset-name sharegpt \
  --num-prompts 1000 \
  --request-rate 10

重点看两个数字:TTFT 的 p95(首 token 延迟分位)和聚合 tok/s(吞吐)。经验上并发越高聚合吞吐越大,但尾部延迟也会变差。如果 p95 超过 500ms 还上不去并发,先调 --max-num-seqs 和 --max-num-batched-tokens,再考虑加卡。把这层摸清,上线后才不会被峰值打懵。