一、vLLM vs Ollama vs TGI:生产推理服务选型决策树
先搞清楚选型,避免选错工具走弯路:
| 对比维度 | Ollama | vLLM | TGI(HuggingFace) |
|---|---|---|---|
| 定位 | 个人/开发体验 | 生产高并发 | 生产标准,功能全 |
| 并发吞吐 | 低(串行处理) | 极高(连续批处理) | 高 |
| 显存利用率 | 低(固定分配) | 高(PagedAttention) | 中高 |
| OpenAI 兼容 | ✅ | ✅ | ✅ |
| 多 GPU | ⚠️ 有限 | ✅ 原生支持 | ✅ |
| 量化支持 | GGUF | AWQ / GPTQ / FP8 | AWQ / GPTQ |
| 部署复杂度 | ⭐ 极简 | ⭐⭐⭐ 中等 | ⭐⭐⭐⭐ 较复杂 |
| 适合场景 | 本地开发、RAG原型 | API 服务、高并发生产 | 企业级、需要精细控制 |
选型结论:
- 自己电脑体验 / RAG 原型开发 → Ollama
- 对外提供 API 服务 / 企业内部推理服务 → vLLM
- 需要精细控制 + 企业 SLA → TGI
二、PagedAttention 原理:为什么 vLLM 吞吐量远超传统框架
传统推理框架的显存浪费问题:
bash
传统方式(静态 KV Cache 分配):
请求A:分配 2GB 显存(但实际只用了 500MB,剩余闲置)
请求B:分配 2GB 显存(等待中,因为显存不够了)
vLLM PagedAttention:
将 KV Cache 分成固定大小的 "Page"(类似操作系统内存分页)
请求A:按需分配 Page,用多少占多少
请求B:可以和请求A共享空闲 Page,无需等待
结果:同样的显存,vLLM 可以处理 3-10 倍的并发请求
同时,vLLM 使用 Continuous Batching(连续批处理):
bash
传统批处理:等一批请求都准备好,一起处理
→ 短请求等长请求,资源浪费
连续批处理:请求完成即退出,新请求立即加入
→ GPU 利用率接近 100%
三、环境安装
3.1 硬件要求
| 模型 | 最低显存 | 推荐显存 | 推荐 GPU |
|---|---|---|---|
| Qwen3-7B(FP16) | 16GB | 24GB | RTX 4090 / A100 40G |
| Qwen3-14B(FP16) | 30GB | 40GB | A100 80G |
| DeepSeek V4-Flash(量化) | 24GB | 48GB | A100 80G × 2 |
| DeepSeek V4-Pro(量化) | 80GB+ | 160GB+ | A100 80G × 4 |
3.2 安装 vLLM
bash
# 创建虚拟环境
conda create -n vllm python=3.11
conda activate vllm
# 安装 vLLM(CUDA 12.1)
pip install vllm
# 验证安装
python -c "import vllm; print(vllm.__version__)"
# 验证 GPU 识别
python -c "import torch; print(torch.cuda.get_device_name(0))"
bash
# 如果 pip 安装失败(网络问题),使用国内镜像
pip install vllm -i https://pypi.tuna.tsinghua.edu.cn/simple
四、启动 Qwen3 / DeepSeek V4 推理服务
4.1 基础启动命令
bash
# 启动 Qwen3-7B 推理服务(OpenAI 兼容格式)
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen3-7B-Instruct \
--served-model-name qwen3-7b \
--host 0.0.0.0 \
--port 8000 \
--max-model-len 32768 \
--tensor-parallel-size 1 # GPU 数量
# 启动后测试
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3-7b",
"messages": [{"role": "user", "content": "你好"}]
}'
4.2 生产级启动配置
bash
# 生产环境推荐配置(单 A100 80G)
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen3-14B-Instruct \
--served-model-name qwen3-14b \
--host 0.0.0.0 \
--port 8000 \
\
# 显存与性能
--max-model-len 65536 \ # 最大上下文长度
--gpu-memory-utilization 0.90 \ # 显存利用率(留 10% 余量)
--max-num-seqs 256 \ # 最大并发序列数
--max-num-batched-tokens 32768 \ # 最大批处理 token
\
# 量化(减少显存占用)
--quantization awq \ # AWQ 量化(精度损失小)
\
# 多 GPU
--tensor-parallel-size 2 \ # 2 块 GPU 张量并行
\
# 其他
--trust-remote-code \
--disable-log-requests # 生产环境关闭请求日志(减少IO)
4.3 多 GPU 张量并行
bash
# 2 块 GPU 部署 Qwen3-32B
CUDA_VISIBLE_DEVICES=0,1 python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen3-32B-Instruct \
--tensor-parallel-size 2 \
--gpu-memory-utilization 0.92
# 4 块 GPU 部署 DeepSeek V4-Flash 量化版
CUDA_VISIBLE_DEVICES=0,1,2,3 python -m vllm.entrypoints.openai.api_server \
--model deepseek-ai/DeepSeek-V4-Flash \
--tensor-parallel-size 4 \
--quantization fp8 \
--gpu-memory-utilization 0.88
五、量化选型:AWQ / GPTQ / FP8 / GGUF 对比
python
# 各量化方案的显存节省与质量损失(以 Qwen3-14B 为基准)
量化方案对比 = {
"FP16(原始)": {"显存": "28GB", "质量损失": "0%", "速度": "基准"},
"FP8": {"显存": "14GB", "质量损失": "<1%", "速度": "快 20%"},
"AWQ(INT4)": {"显存": "8GB", "质量损失": "2-3%","速度": "快 30%"},
"GPTQ(INT4)":{"显存": "8GB", "质量损失": "3-5%","速度": "快 25%"},
}
bash
# 使用 AWQ 量化模型(推荐)
# 1. 安装量化工具
pip install autoawq
# 2. 量化脚本
python -c "
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_path = 'Qwen/Qwen3-14B-Instruct'
quant_path = './qwen3-14b-awq'
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoAWQForCausalLM.from_pretrained(model_path)
quant_config = {
'zero_point': True,
'q_group_size': 128,
'w_bit': 4,
'version': 'GEMM'
}
model.quantize(tokenizer, quant_config=quant_config)
model.save_quantized(quant_path)
tokenizer.save_pretrained(quant_path)
print('量化完成!')
"
# 3. 启动量化模型
python -m vllm.entrypoints.openai.api_server \
--model ./qwen3-14b-awq \
--quantization awq \
--gpu-memory-utilization 0.90
六、并发压测:用 locust 验证吞吐量
python
# locustfile.py
from locust import HttpUser, task, between
import json, random
PROMPTS = [
"用 Python 实现一个快速排序算法,带注释。",
"解释什么是 Spring Boot 的自动装配原理。",
"MySQL 索引的底层数据结构是什么?",
]
class LLMUser(HttpUser):
wait_time = between(0.1, 0.5)
@task
def chat(self):
payload = {
"model": "qwen3-7b",
"messages": [
{"role": "user", "content": random.choice(PROMPTS)}
],
"max_tokens": 512,
"stream": False
}
with self.client.post(
"/v1/chat/completions",
json=payload,
catch_response=True
) as resp:
if resp.status_code == 200:
data = resp.json()
tokens = data["usage"]["completion_tokens"]
resp.success()
else:
resp.failure(f"HTTP {resp.status_code}")
bash
# 运行压测(100并发,持续60秒)
locust -f locustfile.py \
--host http://localhost:8000 \
--users 100 \
--spawn-rate 10 \
--run-time 60s \
--headless \
--csv results
# 查看结果
cat results_stats.csv
压测参考数据(RTX 4090,Qwen3-7B-AWQ):
| 并发数 | 平均延迟(TTFT) | 吞吐(tokens/s) | GPU 利用率 |
|---|---|---|---|
| 1 | 280ms | 45 | 35% |
| 10 | 350ms | 380 | 78% |
| 50 | 520ms | 1850 | 94% |
| 100 | 780ms | 3200 | 98% |
对比 Ollama 单并发约 45 tokens/s,vLLM 100 并发达到 3200 tokens/s,吞吐提升约 70 倍。
七、Docker + K8s 部署
7.1 Dockerfile
dockerfile
FROM nvidia/cuda:12.1-devel-ubuntu22.04
RUN apt-get update && apt-get install -y python3.11 python3-pip git && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN pip install vllm --no-cache-dir
# 启动脚本
COPY start.sh /app/start.sh
RUN chmod +x /app/start.sh
EXPOSE 8000
CMD ["/app/start.sh"]
bash
# start.sh
#!/bin/bash
python -m vllm.entrypoints.openai.api_server \
--model ${MODEL_NAME:-"Qwen/Qwen3-7B-Instruct"} \
--served-model-name ${SERVED_MODEL_NAME:-"qwen3"} \
--host 0.0.0.0 \
--port 8000 \
--gpu-memory-utilization ${GPU_MEMORY_UTIL:-0.90} \
--max-model-len ${MAX_MODEL_LEN:-32768} \
--tensor-parallel-size ${TENSOR_PARALLEL:-1}
7.2 K8s Deployment(含 GPU 资源限制)
yaml
# vllm-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-qwen3
namespace: ai-inference
spec:
replicas: 2
selector:
matchLabels:
app: vllm-qwen3
template:
metadata:
labels:
app: vllm-qwen3
spec:
containers:
- name: vllm
image: your-registry/vllm:latest
ports:
- containerPort: 8000
env:
- name: MODEL_NAME
value: "Qwen/Qwen3-7B-Instruct"
- name: GPU_MEMORY_UTIL
value: "0.90"
resources:
limits:
nvidia.com/gpu: 1 # 每个 Pod 使用 1 块 GPU
memory: "32Gi"
requests:
nvidia.com/gpu: 1
memory: "24Gi"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 120 # 模型加载需要时间
periodSeconds: 30
readinessProbe:
httpGet:
path: /v1/models
port: 8000
initialDelaySeconds: 90
periodSeconds: 10
nodeSelector:
accelerator: nvidia-gpu # 只调度到 GPU 节点
---
apiVersion: v1
kind: Service
metadata:
name: vllm-service
namespace: ai-inference
spec:
selector:
app: vllm-qwen3
ports:
- port: 80
targetPort: 8000
type: ClusterIP
---
# HPA:按 GPU 利用率自动扩缩容
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vllm-hpa
namespace: ai-inference
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm-qwen3
minReplicas: 1
maxReplicas: 4
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
八、Prometheus 监控接入
vLLM 内置 Prometheus 指标端点(/metrics):
bash
# vLLM 启动时开启 metrics
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen3-7B-Instruct \
--port 8000 \
--enable-metrics # 开启 /metrics 端点
yaml
# prometheus 抓取配置
scrape_configs:
- job_name: 'vllm'
static_configs:
- targets: ['vllm-service:8000']
metrics_path: /metrics
yaml
# Grafana 告警规则(alertmanager)
groups:
- name: vllm_alerts
rules:
- alert: VLLMHighLatency
expr: vllm:e2e_request_latency_seconds_p99 > 5
for: 2m
annotations:
summary: "vLLM P99 延迟超过 5 秒"
- alert: VLLMQueueFull
expr: vllm:num_requests_waiting > 100
for: 1m
annotations:
summary: "vLLM 请求队列积压超过 100 个"
- alert: VLLMGPUOOMRisk
expr: vllm:gpu_cache_usage_perc > 95
for: 30s
annotations:
summary: "GPU KV Cache 使用率超过 95%,有 OOM 风险"
关键监控指标说明:
| 指标名 | 含义 | 告警阈值建议 |
|---|---|---|
vllm:e2e_request_latency_seconds |
端到端请求延迟 | P99 > 5s 告警 |
vllm:num_requests_running |
正在处理的请求数 | > max_num_seqs * 0.9 |
vllm:num_requests_waiting |
排队等待的请求数 | > 50 告警,> 200 紧急 |
vllm:gpu_cache_usage_perc |
GPU KV Cache 使用率 | > 90% 告警 |
vllm:tokens_per_second |
推理吞吐 | 低于基准 50% 告警 |
总结
vLLM 是目前生产环境大模型推理的最优解,核心优势在于 PagedAttention + Continuous Batching 的组合,将 GPU 利用率从 Ollama 的 30-40% 提升到 95%+。
部署建议:
- 显存 < 24GB:使用 AWQ 量化,节省 50% 显存
- 多 GPU :
--tensor-parallel-size设置等于 GPU 数量 - K8s 部署 :
initialDelaySeconds必须设够,模型加载慢 - 监控:GPU Cache 使用率是最关键指标,超过 90% 必须扩容