vLLM 高性能推理部署——企业内网大模型的标准答案
vLLM 高性能推理部署——企业内网大模型的标准答案
适读人群:需要在企业内网部署大模型的工程师 | 阅读时长:约16分钟 | 核心价值:vLLM部署完整方案+与Ollama的本质区别
去年在一个大客户的内网部署项目上,对方技术负责人问了我一个问题:
"我们已经用Ollama部署了Qwen2.5-14B,能跑,但是高峰期50个并发进来,平均响应时间接近3分钟。你有没有办法优化?"
我问了他几个问题:服务器什么配置?多少张卡?运维团队有多少人?
他说:两张A100 40GB,运维3个人,可以接受一定的部署复杂度。
我的答案是:换vLLM,不是优化Ollama。
这篇文章就是要解释清楚:为什么用了A100还跑不好Ollama,vLLM到底解决了什么问题,以及企业内网部署的具体方案。
Ollama和vLLM的本质区别
很多人以为Ollama和vLLM只是部署工具的区别,换一个工具就好了。这个认知是错的。它们在推理引擎的核心设计上有本质差异。
Ollama的内存管理方式:
Ollama基于llama.cpp,每个请求进来,都会独立分配一块KV Cache(键值缓存)。KV Cache是什么?是模型在处理每个token时,需要存储之前所有token的Key和Value矩阵,用于注意力计算。
简单说:你发一个2000 token的请求,模型需要给这2000个token各自分配KV Cache空间。
问题在哪?Ollama是静态分配的——每个请求进来,就预留一整块最大context长度的KV Cache。50个并发进来,就是50块。哪怕大多数请求只用了一小部分context,空间也被占着不放。
这导致GPU显存很快被占满,后续请求只能排队等,或者被拒绝。
vLLM的PagedAttention:
vLLM的核心创新是PagedAttention,来自UC Berkeley的一篇论文。思路借鉴了操作系统的虚拟内存分页机制。
KV Cache不再整块预分配,而是按需分配小块(page)。不同请求可以共享相同的前缀page(比如同一个系统提示),内存碎片大幅减少,GPU显存利用率从Ollama的30-40%提升到80-90%以上。
这就是为什么同样的硬件,vLLM能支撑的并发数可以是Ollama的5-10倍。
除此之外,vLLM还有连续批处理(Continuous Batching):不等待一个请求完全处理完再处理下一个,而是当一个请求生成完一个token、释放出计算资源时,立刻插入新请求的token计算。GPU利用率接近100%。
部署vLLM
先说环境要求:vLLM对CUDA版本的要求比较严格,推荐CUDA 12.1以上,Python 3.9+。
# 创建虚拟环境
conda create -n vllm python=3.11 -y
conda activate vllm
# 安装vLLM(自动匹配CUDA版本)
pip install vllm
# 验证安装
python -c "import vllm; print(vllm.__version__)"基础启动命令:
# 启动Qwen2.5-14B的OpenAI兼容API服务
python -m vllm.entrypoints.openai.api_server \
--model /models/Qwen2.5-14B-Instruct \
--served-model-name qwen2.5-14b \
--host 0.0.0.0 \
--port 8000 \
--gpu-memory-utilization 0.9 \
--max-model-len 8192 \
--dtype bfloat16主要参数解释:
--gpu-memory-utilization 0.9:使用90%的GPU显存,预留10%给系统。默认是0.9,可以根据实际情况调整。
--max-model-len 8192:最大context长度。这个值影响KV Cache的最大分配。如果你的任务不需要很长的context,设小一些可以支撑更多并发。
--dtype bfloat16:推理精度。A100支持bfloat16,比float16稳定性更好。
多GPU部署(张量并行):
对方的环境是两张A100,用张量并行可以把模型分散到两张卡上,同时运行,显存翻倍,速度也有提升:
python -m vllm.entrypoints.openai.api_server \
--model /models/Qwen2.5-14B-Instruct \
--served-model-name qwen2.5-14b \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 2 \ # 两张卡张量并行
--gpu-memory-utilization 0.9 \
--max-model-len 16384 \ # 两张A100,显存充足,可以跑更长的context
--dtype bfloat16调用方式:vLLM暴露OpenAI兼容的API,所以任何能调OpenAI的代码,改个base_url就能用:
from openai import OpenAI
client = OpenAI(
base_url="http://your-server:8000/v1",
api_key="not-needed" # vLLM默认不需要API key
)
response = client.chat.completions.create(
model="qwen2.5-14b",
messages=[
{"role": "system", "content": "你是一个专业的合同审查助手。"},
{"role": "user", "content": "请审查以下合同条款..."}
],
max_tokens=2048,
temperature=0.1 # 低temperature保证输出稳定
)
print(response.choices[0].message.content)实际性能对比数据
在那个客户的环境上,我做了切换前后的对比测试(两张A100 40GB,Qwen2.5-14B-Instruct,BF16精度):
并发50,每请求prompt约800 tokens,输出约400 tokens:
| 指标 | Ollama | vLLM |
|---|---|---|
| 平均首token延迟 | 45s | 2.8s |
| 平均完整响应时间 | 178s | 22s |
| P99响应时间 | 超时(>5分钟) | 48s |
| 请求成功率 | 72% | 99.8% |
| GPU利用率 | 38% | 87% |
| 吞吐(tokens/s) | ~120 | ~2400 |
这个数据差距是真实的。同样的硬件,吞吐量差了20倍。
原因就是上面说的:Ollama在高并发下GPU利用率严重偏低,大量时间在等待和调度;vLLM的PagedAttention让GPU基本满载运行。
进阶配置:针对企业场景的优化
开启量化(如果显存不够跑BF16):
# 使用AWQ量化(4bit,速度快,精度损失小)
python -m vllm.entrypoints.openai.api_server \
--model /models/Qwen2.5-14B-Instruct-AWQ \
--quantization awq \
--gpu-memory-utilization 0.9 \
--max-model-len 8192如果只有一张A100 40GB想跑32B模型,AWQ量化是可行的路径。32B模型AWQ量化后约20GB。
Prefix Caching(系统提示缓存):
企业场景里,通常会有一个很长的系统提示(比如合同审查的规则手册)。每个请求都要处理一遍这个系统提示,浪费计算资源。
vLLM支持Prefix Caching,相同前缀的KV Cache直接复用:
python -m vllm.entrypoints.openai.api_server \
--model /models/Qwen2.5-14B-Instruct \
--enable-prefix-caching \ # 开启前缀缓存
--gpu-memory-utilization 0.9 \
--max-model-len 8192开了Prefix Caching之后,如果系统提示是1000 tokens,后续同系统提示的请求,这1000 tokens的KV Cache直接命中缓存,首token延迟大幅下降。
在我们的合同审查场景,系统提示约1500 tokens,开了prefix caching后,首token延迟从2.8s降到0.8s。
API认证(生产环境必须):
vLLM默认不做认证,任何人访问都能用。企业内网要加:
python -m vllm.entrypoints.openai.api_server \
--model /models/Qwen2.5-14B-Instruct \
--api-key your-secret-token \ # 设置API key
--host 0.0.0.0 \
--port 8000然后在Nginx层再加一层IP白名单控制,只允许内网IP段访问。
vLLM的运维:比Ollama复杂,但值得
说实话,vLLM的运维比Ollama复杂。Ollama一条命令就起来了,vLLM需要你了解更多参数,遇到问题的时候日志也更难看懂。
但有几个地方值得投入:
日志和监控:
# vLLM原生支持Prometheus metrics
python -m vllm.entrypoints.openai.api_server \
--model /models/Qwen2.5-14B-Instruct \
--disable-log-requests # 关闭请求日志(减少日志量)
# metrics自动暴露在 /metrics 端点vLLM的Prometheus metrics非常完善,包括:
vllm:num_requests_running:当前运行中的请求数vllm:gpu_cache_usage_perc:GPU KV Cache使用率vllm:time_to_first_token_seconds:首token延迟分布vllm:e2e_request_latency_seconds:端到端延迟分布
有了这些metrics,Grafana仪表盘配起来很方便,比自己给Ollama写exporter省事多了。
用systemd管理服务:
# /etc/systemd/system/vllm.service
[Unit]
Description=vLLM Service
After=network-online.target
[Service]
Type=simple
User=vllm
WorkingDirectory=/opt/vllm
ExecStart=/opt/conda/envs/vllm/bin/python -m vllm.entrypoints.openai.api_server \
--model /models/Qwen2.5-14B-Instruct \
--served-model-name qwen2.5-14b \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 2 \
--gpu-memory-utilization 0.9 \
--max-model-len 8192 \
--enable-prefix-caching \
--api-key ${VLLM_API_KEY}
EnvironmentFile=/opt/vllm/.env
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target/opt/vllm/.env里存API key等敏感信息,不放到service文件里。
什么情况下还是用Ollama
vLLM不是银弹。有几个场景我还是会推荐Ollama:
开发和测试阶段:Ollama启动快,模型切换方便,适合快速验证想法。
低并发场景:如果你的服务每天就几百个请求,没有并发压力,Ollama完全够用,vLLM的运维复杂度反而是负担。
Windows服务器:vLLM对Windows支持很差,Linux才是一等公民。Ollama在Windows上运行顺畅。
团队没有运维能力:vLLM出了问题,需要有人能看懂日志、调整参数。如果团队完全没有这个能力,强行上vLLM会成为维护噩梦。
给那个客户的最终方案
回到那个问题:50并发,两张A100,响应时间要压到10秒以内。
我给他的方案:
架构:
Nginx (负载均衡 + SSL)
--> vLLM Server (双A100, tensor-parallel-size=2)
- model: Qwen2.5-14B-Instruct BF16
- max-model-len: 16384
- enable-prefix-caching
- api-key: 内部token
监控:Prometheus + Grafana
告警:P95延迟 > 30s 触发钉钉通知上线后的实际测试:50并发,平均响应时间18秒,P99 42秒,请求成功率100%。
指标满足要求,客户满意。
vLLM在企业内网高并发大模型部署这个场景,目前我没见到比它更合适的选择。如果你的场景符合:内网部署、多并发、有Linux服务器、有运维能力,那就直接上vLLM,不要在Ollama上浪费时间优化。
