摘要
在企业内部部署大模型时,难点不只是把模型权重加载到 GPU,还包括吞吐、显存、并发、接口兼容、模型版本、安全暴露和故障恢复。vLLM 提供高性能大模型推理运行时,并可以通过 OpenAI 兼容接口对外提供聊天补全、文本补全和模型查询能力。
本文以一个本地推理服务为例,介绍:
- vLLM 与 LiteLLM、Portkey 等网关的职责区别;
- 如何准备模型、GPU、容器和访问令牌;
- 如何启动 OpenAI 兼容服务;
- 如何使用 curl、Python 和 Spring AI 调用;
- 如何配置上下文长度、显存利用率和并发;
- 如何处理模型许可证、数据安全和生产部署;
- 如何验证服务是否真的适合业务流量。
项目资料核对时间:2026 年 9 月 28 日。
项目地址:
- GitHub:https://github.com/vllm-project/vllm
- 官方文档:https://docs.vllm.ai/
- OpenAI 兼容服务:https://docs.vllm.ai/en/latest/serving/online_serving/openai_compatible_server.html
一、背景与问题
1. 为什么需要本地推理服务
云端模型接入速度快,但企业应用可能面临:
- 代码或业务数据不能离开内网;
- 长期调用成本难以预测;
- 需要定制模型或 LoRA;
- 需要控制模型版本和升级时间;
- 专线、区域或合规要求限制外部调用。
直接用 Transformers 在 Web 接口中加载模型可以完成实验,但生产环境容易遇到:
- 每个进程重复占用显存;
- 并发请求互相阻塞;
- 请求队列没有统一调度;
- 长上下文导致显存溢出;
- API 格式与现有 SDK 不兼容;
- 模型进程崩溃后无法自动恢复。
2. vLLM 在架构中的位置
text
业务服务
↓ OpenAI compatible API
vLLM 推理服务
↓
GPU + 模型权重
vLLM 负责模型推理和请求调度,不负责企业级租户、预算、模型路由和统一审计。需要多供应商治理时,可以在 vLLM 前面增加 LiteLLM、Portkey 或自建网关。
| 组件 | 主要职责 |
|---|---|
| vLLM | 单个或一组本地模型的高性能推理 |
| LiteLLM | 多模型统一接口、路由、Fallback 和预算 |
| Portkey | 网关治理、策略、观测和模型流量管理 |
| Spring AI | Java 应用侧的模型调用抽象 |
二、核心概念
1. OpenAI 兼容接口
vLLM 服务可以暴露常见接口:
text
GET /v1/models
POST /v1/chat/completions
POST /v1/completions
这意味着已有 OpenAI SDK、部分 Spring AI 客户端或自定义 HTTP 客户端通常只需修改 base_url 和模型名。
2. 连续批处理
传统批处理需要等一批请求全部结束后再处理下一批。连续批处理会在请求完成后立即为新请求安排执行,提高 GPU 利用率,尤其适合请求长度不同的在线服务。
3. KV Cache 与显存
模型推理会为已处理的上下文保存 Key/Value 缓存。上下文越长、并发越高,KV Cache 占用越大。显存不足不一定是模型权重太大,也可能是并发和最大上下文共同造成的。
4. 模型名与实际权重
服务端的 --model 指向模型仓库或本地目录,客户端的 model 必须使用服务暴露的模型标识。生产环境最好使用固定模型目录和固定版本,不依赖不断变化的远程别名。
三、工作原理
1. 启动流程
text
读取模型配置
↓
加载 tokenizer 和权重
↓
初始化 GPU memory manager
↓
启动请求调度器
↓
启动 OpenAI compatible server
↓
接收请求并排队
↓
批处理、采样、流式返回
2. 请求生命周期
text
HTTP 请求
↓
参数校验
↓
tokenize
↓
调度到运行中的 batch
↓
prefill:处理输入上下文
↓
decode:逐 token 生成
↓
stream:返回增量结果
prefill 通常更受输入长度影响,decode 更受输出长度和并发影响。压测时要分别构造短输入长输出、长输入短输出和多轮对话场景。
3. 服务边界
vLLM 默认适合在可信网络内作为推理后端,不应直接暴露到公网。公网访问需要由 API Gateway 处理身份认证、TLS、限流、审计和请求体大小限制。
四、实战示例
1. 运行前检查
先确认以下条件:
- Linux 主机或经过验证的 GPU 容器环境;
- NVIDIA 驱动、CUDA 和容器运行时版本匹配;
- GPU 显存能够容纳模型权重和 KV Cache;
- 模型许可证允许当前使用方式;
- 模型权重已提前下载或具备稳定的模型仓库访问;
- 服务端口只绑定到内网地址。
不要先启动服务再根据 OOM 猜配置。应先估算权重、上下文和并发,再决定 GPU 数量和量化方案。
2. 使用 Docker 启动
下面示例使用环境变量传入模型仓库令牌,并把模型缓存持久化到主机:
powershell
docker run --gpus all --rm `
--name local-vllm `
-p 127.0.0.1:8000:8000 `
-e HF_TOKEN=$env:HF_TOKEN `
-v ${env:USERPROFILE}\.cache\huggingface:/root/.cache/huggingface `
vllm/vllm-openai:latest `
--model Qwen/Qwen2.5-7B-Instruct `
--served-model-name local-qwen `
--host 0.0.0.0 `
--port 8000 `
--api-key $env:VLLM_API_KEY `
--dtype auto `
--gpu-memory-utilization 0.90 `
--max-model-len 8192
生产部署不要长期使用 latest。应将镜像固定到已经完成验证的版本,并把模型版本、容器版本和启动参数一起记录。
3. 查询模型
powershell
curl.exe http://127.0.0.1:8000/v1/models `
-H "Authorization: Bearer $env:VLLM_API_KEY"
返回结果中的模型 ID 应与客户端使用的 local-qwen 一致。
4. 使用 curl 进行聊天
powershell
$body = @{
model = "local-qwen"
messages = @(
@{ role = "system"; content = "你是一个严谨的 Java 后端助手。" }
@{ role = "user"; content = "解释什么是幂等性,并给出一个接口示例。" }
)
temperature = 0.2
max_tokens = 512
stream = $false
} | ConvertTo-Json -Depth 5
Invoke-RestMethod `
-Uri http://127.0.0.1:8000/v1/chat/completions `
-Method Post `
-Headers @{ Authorization = "Bearer $env:VLLM_API_KEY" } `
-ContentType "application/json" `
-Body $body
5. 使用 OpenAI Python 客户端
python
from openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:8000/v1",
api_key="local-vllm-key",
)
response = client.chat.completions.create(
model="local-qwen",
messages=[
{"role": "user", "content": "给出一个线程池参数配置的检查清单。"}
],
temperature=0.2,
max_tokens=512,
)
print(response.choices[0].message.content)
流式调用只需要设置 stream=True,但客户端仍要处理连接中断、空增量、服务端错误和用户取消。
6. Spring AI 接入
yaml
spring:
ai:
openai:
base-url: http://127.0.0.1:8000
api-key: ${VLLM_API_KEY}
chat:
options:
model: local-qwen
temperature: 0.2
max-tokens: 512
Java 代码可以继续使用 Spring AI:
java
@Service
public class LocalModelService {
private final ChatClient chatClient;
public LocalModelService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
public String answer(String question) {
return chatClient.prompt()
.user(question)
.call()
.content();
}
}
如果 Spring AI 版本和本地服务对工具调用、结构化输出或流式字段的支持不一致,应先用原始 HTTP 验证接口,再逐步启用高级能力。
7. 健康检查与压测
最小检查项包括:
text
/v1/models 是否可访问
基础聊天是否成功
stream 是否正常结束
错误模型名是否返回明确错误
超过上下文是否受控失败
并发时是否出现 OOM
服务重启后模型是否自动恢复
压测指标至少记录:
| 指标 | 说明 |
|---|---|
| 首 Token 延迟 | 用户感知响应速度 |
| Token 吞吐 | 单位时间生成量 |
| P95/P99 延迟 | 长尾请求情况 |
| 排队时间 | 调度是否拥堵 |
| GPU 利用率 | 硬件是否被充分使用 |
| 显存占用 | 是否接近 OOM |
| 错误率 | 超时、限流和生成失败 |
五、常见问题与实践建议
1. 显存不足
优先检查:
--max-model-len是否过大;- 并发是否过高;
--gpu-memory-utilization是否设置过激;- 模型是否使用了不必要的精度;
- 是否需要量化或张量并行。
降低最大上下文会直接减少可处理的长输入,但通常比让服务频繁 OOM 更容易控制。
2. 接口能通但输出异常
检查客户端模型名、聊天模板、停止词、工具调用格式和 max_tokens。不同模型的 Chat Template 不同,模型目录中缺少模板时,消息角色可能无法正确转换。
3. 本地服务不等于安全服务
API Key 只是一层简单认证。生产环境还需要:
- 网关身份认证;
- 来源 IP 或网络策略;
- TLS;
- 请求大小限制;
- 访问审计;
- 模型文件来源和完整性校验;
- 禁止把内部端口映射到公网。
4. 是否应该使用 latest
实验环境可以快速试用,生产环境不建议。镜像更新可能改变参数、默认行为或模型兼容性。使用固定镜像版本,并通过测试集验证升级。
5. 如何处理长对话
不要无限追加历史消息。可以采用摘要、滑动窗口、按需检索和会话分层存储。长上下文不仅增加延迟,也会持续占用 KV Cache。
六、进阶思考
1. vLLM 与网关分层
推荐结构:
text
业务服务
↓
统一 AI Gateway
├─ 身份认证
├─ 租户限流
├─ 模型路由
├─ 预算和审计
└─ 熔断与 Fallback
↓
┌───────────────┬───────────────┐
↓ ↓ ↓
vLLM 云端模型 备用 vLLM
vLLM 专注 GPU 推理,网关专注业务治理。把两类职责混在推理进程中,会增加升级和扩容难度。
2. 模型版本与可复现性
每次部署记录:
yaml
deployment:
image: vllm/vllm-openai:<verified-version>
model: Qwen/Qwen2.5-7B-Instruct
revision: <commit-or-snapshot>
dtype: auto
maxModelLen: 8192
gpuMemoryUtilization: 0.90
模型权重、Tokenizer、镜像和启动参数任一变化,都可能影响输出质量和性能。
3. 多 GPU 与扩容
单模型无法放入一张 GPU 时,可以考虑张量并行;吞吐增加时,可以部署多个副本并由网关做负载均衡。扩容前要确认模型副本的显存成本,不能只按 CPU 服务的方式增加实例。
4. 质量与性能一起验收
单纯追求 Tokens/Second 可能导致上下文过短、回答质量下降或错误率上升。上线门槛应同时包含:
- 固定样本集质量;
- 首 Token 延迟;
- P95 完成延迟;
- 并发吞吐;
- OOM 和重启次数;
- 单请求 GPU 成本;
- 敏感数据不外泄。
结论
vLLM 适合承担本地大模型推理这一层:它通过 GPU 调度、连续批处理和 OpenAI 兼容接口,把模型权重封装成可以被现有应用调用的服务。真正进入生产环境时,还需要在它前面补充网关、身份、限流、审计和模型发布能力。
落地顺序可以是:先用固定模型启动单机服务,再用真实请求压测显存和延迟,随后接入 Spring AI 或统一网关,最后补充多副本、故障切换和版本回滚。