vLLM 实战:用 OpenAI 兼容接口部署本地大模型推理服务

摘要

在企业内部部署大模型时,难点不只是把模型权重加载到 GPU,还包括吞吐、显存、并发、接口兼容、模型版本、安全暴露和故障恢复。vLLM 提供高性能大模型推理运行时,并可以通过 OpenAI 兼容接口对外提供聊天补全、文本补全和模型查询能力。

本文以一个本地推理服务为例,介绍:

  • vLLM 与 LiteLLM、Portkey 等网关的职责区别;
  • 如何准备模型、GPU、容器和访问令牌;
  • 如何启动 OpenAI 兼容服务;
  • 如何使用 curl、Python 和 Spring AI 调用;
  • 如何配置上下文长度、显存利用率和并发;
  • 如何处理模型许可证、数据安全和生产部署;
  • 如何验证服务是否真的适合业务流量。

项目资料核对时间:2026 年 9 月 28 日。

项目地址:

一、背景与问题

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 或统一网关,最后补充多副本、故障切换和版本回滚。

参考资料

相关推荐
sbjdhjd2 小时前
从签名重放到 Agent Harness:一堂安全课程如何把前端逆向、MCP 与工程约束串起来
网络安全·ai·github·agent·webshell·mcp·harness
sejinan3 小时前
极致轻量与秒级起播:NativeWasmTv 开源硬核技术解析与升级版体验
android·开源·webview·tv
程序员老赵3 小时前
Docker 部署 Filestash:轻松搭建浏览器文件管理平台
前端·docker·开源
缘友一世4 小时前
GLM5.3 CyberSec w4A16本地部署[2]:并行策略与 PP 切分
ubuntu·vllm·glm5.3 cybersec
ffyyhh9955114 小时前
记一次开源脚手架的登录认证流程
开源
逛逛GitHub4 小时前
火了两周的 30 万 Star 逆向 GitHub 开源神器,网友都用它来干啥?
github
1024奇点4 小时前
【仓颉语言入门 · 第二篇 第 8 课】
开发语言·数据库·ide·mysql·开源
分布式存储与RustFS4 小时前
RustFS 该跟 stable 还是 nightly?两条版本线的分工、风险与回退,一张表说清
运维·云原生·开源·对象存储·分布式存储·s3·性能基准
孙琦Ray4 小时前
开源MATLAB工具LIBquiv:650小时老化仿真仅139秒
matlab·开源·锂离子电池·等效电路模型·电池老化·libquiv·电池建模
wflynn5 小时前
GitHub 日榜趋势速报 | 2026-10-18
开源·github