大模型推理服务部署
从零搭建本地大模型推理服务——vLLM、TGI、Ollama 三大框架对比实战,涵盖模型选型、量化压缩、API 服务化、并发优化、GPU 资源管理、生产部署与监控,一套方案让你摆脱 API 依赖。
1. 为什么要自己部署推理服务
1.1 API 调用的四大痛点:成本、隐私、延迟、依赖
如果你的 AI 应用完全依赖 OpenAI / Claude 等 API,迟早会遇到这些问题:
API 调用的四大痛点:
① 成本失控 ─── 日均 10 万次调用 × $0.03/次 = $3,000/月 = $36,000/年
│ 规模越大越贵,没有边际成本递减
│
② 数据隐私 ─── 用户数据发送到第三方服务器
│ 金融/医疗/政务场景不合规
│
③ 延迟不可控 ── 跨境网络延迟 200-500ms + 排队等待
│ 国内访问 OpenAI 需要代理,稳定性差
│
④ 供应商依赖 ── API 限流、价格调整、服务下线
2024 年 GPT-4 涨价 50%,用户无能为力用一个真实的例子量化成本差异:
| 维度 | API 调用(GPT-4o) | 自部署(Qwen2.5-32B) |
|---|---|---|
| 单次成本 | ~$0.03 | ~$0.001(已摊设备成本) |
| 日均 10 万次/月 | $90,000 | $5,000(GPU 租用) |
| 数据传输 | 出境到美国 | 本地内网 |
| 延迟(P50) | 3-8s | 1-3s |
| 最大并发 | 受限于 Rate Limit | 受限于 GPU 显存 |
| 模型定制 | 不支持 | 可微调 |
1.2 自部署的优势与门槛
自部署不是银弹,它有明确的优势和门槛:
自部署的优势:
✅ 成本可预测 ── GPU 是固定成本,不随调用量线性增长
✅ 数据不出域 ── 完全本地化,满足合规要求
✅ 低延迟 ────── 内网部署,无网络开销
✅ 可定制 ────── 微调、量化、推理参数完全可控
✅ 无限流 ────── 并发上限取决于硬件,不受 API 限制
自部署的门槛:
⚠️ 硬件投入 ── 至少需要 1 张 A10(24GB)或 RTX 4090
⚠️ 运维成本 ── GPU 驱动、CUDA、模型更新都要自己管
⚠️ 模型质量 ── 开源模型整体弱于 GPT-4o / Claude 3.5 Sonnet
⚠️ 工程复杂度 ── 需要掌握推理框架、量化、并发优化💡 自部署的核心逻辑是"用工程能力换运营成本"——如果你的团队有 GPU 资源和运维能力,日均调用量超过 1 万次时,自部署几乎一定比 API 更划算。
1.3 什么时候该自己部署:决策树
用这个决策树快速判断你的场景是否适合自部署:
自部署决策树:
你的场景需要数据不出域吗?
├── YES → ✅ 必须自部署(金融/医疗/政务)
│
└── NO → 你的日均 LLM 调用量是多少?
│
├── < 1,000 次 → ❌ 用 API,自部署不划算
│
├── 1,000 - 10,000 次 → 需要看场景:
│ │
│ ├── 延迟敏感? → ✅ 自部署(内网秒级响应)
│ ├── 需要微调? → ✅ 自部署(API 不支持微调)
│ └── 都不需要? → ❌ 继续用 API
│
└── > 10,000 次 → ✅ 自部署几乎一定更划算
月成本可降低 5-10 倍不同场景的推荐方案:
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 个人开发/原型验证 | Ollama 本地 | 零成本,笔记本就能跑 |
| 小团队内部工具 | vLLM + 1×A10 | 性价比最高 |
| B 端 SaaS 产品 | vLLM + 多 GPU | 高并发,数据隔离 |
| 金融/医疗合规 | TGI/vLLM + 私有云 | 数据不出域 |
| 大规模 to C 产品 | 自部署 + API 混合 | 基线自部署,峰值用 API |
1.4 硬件需求速览:不同模型需要多少 GPU 显存
一张速查表,帮你快速评估需要什么硬件:
模型显存需求速查(推理阶段,不含 KV Cache):
模型参数量 FP16 显存 INT8 显存 INT4 显存
────────── ────────── ────────── ──────────
7B 14 GB 7 GB 4 GB
14B 28 GB 14 GB 8 GB
32B 64 GB 32 GB 18 GB
72B 144 GB 72 GB 40 GB
粗算公式:显存(GB) ≈ 参数量(B) × 每参数字节数
FP16 = 2 字节 → 7B × 2 = 14GB
INT8 = 1 字节 → 7B × 1 = 7GB
INT4 = 0.5 字节 → 7B × 0.5 = 3.5GB(实际约 4GB)常用 GPU 的显存与适配模型:
| GPU | 显存 | 价格(租用/月) | 推荐模型 |
|---|---|---|---|
| RTX 4090 | 24 GB | ~$300 | 7B FP16 / 14B INT8 / 32B INT4 |
| A10 | 24 GB | ~$200 | 同上(数据中心级,更稳定) |
| A100 40G | 40 GB | ~$800 | 32B FP16 / 72B INT4 |
| A100 80G | 80 GB | ~$1,500 | 72B FP16 / 72B INT8 |
| H100 | 80 GB | ~$2,500 | 72B FP16 + 大并发 |
| Mac M2 Ultra | 192 GB 统一内存 | 一次性 ~$5,000 | 72B(CPU 推理,较慢) |
💡 INT4 量化是性价比之王——32B 模型量化到 INT4 只需 18GB 显存,一张 RTX 4090 就能跑。质量损失约 2-5%,但成本降低 60%。对于大多数场景,INT4 的 32B 模型优于 FP16 的 7B 模型。
第 1 章核心知识回顾:
| 概念 | 一句话解释 |
|---|---|
| 四大痛点 | 成本失控、数据隐私、延迟不稳、供应商锁定 |
| 核心逻辑 | 用工程能力换运营成本 |
| 决策门槛 | 日均 > 1 万次 或 数据合规需求 → 自部署 |
| 显存公式 | 显存(GB) ≈ 参数量(B) × 每参数字节数 |
| 性价比首选 | INT4 量化 + RTX 4090 / A10 |
2. 推理框架选型:vLLM vs TGI vs Ollama
2.1 三大框架一览:定位与差异
三大框架的定位完全不同——选错框架比选错模型影响更大:
| 维度 | vLLM | TGI | Ollama |
|---|---|---|---|
| 开发者 | UC Berkeley | Hugging Face | Ollama Inc |
| 定位 | 高吞吐量推理引擎 | HF 生态集成方案 | 最简单的本地部署 |
| 核心优势 | PagedAttention + 极致性能 | 生态好 + Docker 一键部署 | 零配置 + 一行命令 |
| API 兼容 | OpenAI 格式 ✅ | 自有格式 + OpenAI 适配 | OpenAI 格式 ✅ |
| 量化支持 | AWQ / GPTQ / FP8 | GPTQ / bitsandbytes | GGUF(CPU+GPU 混合) |
| 多 GPU | Tensor Parallel ✅ | Tensor Parallel ✅ | ❌ 单 GPU |
| 部署方式 | pip / Docker | Docker 为主 | 独立安装包 |
| 适用场景 | 生产环境、高并发 | HF 生态、快速原型 | 开发调试、个人使用 |
一句话选型指南:
生产环境、追求性能 → vLLM
HF 生态、Docker 优先 → TGI
个人开发、快速体验 → Ollama2.2 vLLM:高吞吐量的首选(PagedAttention 原理)
vLLM 是目前性能最强的开源推理引擎,核心创新是 PagedAttention。
为什么 vLLM 快?——PagedAttention 原理:
传统推理引擎的 KV Cache 管理:
┌─────────────────────────────────────────────┐
│ 请求 A:预分配 2048 tokens 的 KV Cache │
│ ████████░░░░░░░░░░░░░░░░░░░░░░░░ │
│ 实际只用了 512 tokens → 75% 显存浪费! │
└─────────────────────────────────────────────┘
vLLM 的 PagedAttention:
┌─────────────────────────────────────────────┐
│ KV Cache 按"页"分配(类似操作系统虚拟内存) │
│ 请求 A:████ ████ ████ (按需分配 3 页) │
│ 请求 B:████ ████ (按需分配 2 页) │
│ 空闲页回收 → 显存利用率从 30% 提升到 90%+ │
└─────────────────────────────────────────────┘vLLM 的核心特性:
vLLM 特性速览:
① PagedAttention ──── KV Cache 按页管理,显存利用率 90%+
② Continuous Batching ── 新请求不等待,动态加入批次
③ Tensor Parallel ──── 多 GPU 自动拆分模型
④ Prefix Caching ──── 相同 System Prompt 共享 KV Cache
⑤ OpenAI API 兼容 ─── 无缝替换 OpenAI SDK
⑥ 量化支持 ────────── AWQ / GPTQ / FP8 / INT8💡 vLLM 的 Continuous Batching 比静态 Batching 吞吐量高 2-5 倍——传统框架必须等一批请求都完成才能处理下一批;vLLM 的请求完成一个就释放一个位置,新请求立即填入,GPU 永远不空闲。
2.3 TGI:Hugging Face 官方方案(生态集成优势)
TGI(Text Generation Inference)是 Hugging Face 官方的推理服务,最大的优势是和 HF 生态无缝集成。
TGI 的核心特性:
① HF Hub 直连 ──── 模型 ID 直接加载,无需手动下载
② Flash Attention 2 ── 原生支持,自动启用
③ 流式输出 ────── Server-Sent Events,原生流式
④ Token 级统计 ── 每个 token 的生成延迟、概率
⑤ 多格式量化 ─── GPTQ / bitsandbytes / EETQ
⑥ Docker 优先 ── 一行命令启动,镜像预装所有依赖TGI 的典型用法:
# 一行命令启动 TGI(Docker)
docker run --gpus all \
-p 8080:80 \
-v /data/models:/data \
ghcr.io/huggingface/text-generation-inference:latest \
--model-id Qwen/Qwen2.5-14B-Instruct \
--quantize gptq \
--max-input-tokens 4096 \
--max-total-tokens 8192
# 调用 API
curl http://localhost:8080/generate \
-d '{"inputs":"什么是 Transformer?","parameters":{"max_new_tokens":200}}'💡 TGI 适合已经深度使用 HF 生态的团队——如果你的模型在 HF Hub 上、训练用的 Transformers、数据集用的 Datasets,那 TGI 是最省事的选择。但性能上比 vLLM 低 20-30%。
2.4 Ollama:最简单的本地部署(开发体验优先)
Ollama 的设计哲学是**"像 Docker 管理镜像一样管理模型"**——一行命令下载、运行、切换模型:
# 安装 Ollama(macOS / Linux)
curl -fsSL https://ollama.com/install.sh | sh
# 下载并运行模型(一行命令)
ollama run qwen2.5:32b
# 其他常用命令
ollama list # 列出已下载的模型
ollama pull llama3.1:8b # 只下载不运行
ollama rm mistral:7b # 删除模型
ollama serve # 启动 API 服务Ollama 的 API 兼容 OpenAI 格式:
from openai import OpenAI
# 把 base_url 指向 Ollama,代码零改动
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
response = client.chat.completions.create(
model="qwen2.5:32b",
messages=[{"role": "user", "content": "什么是 PagedAttention?"}],
)
print(response.choices[0].message.content)Ollama 的定位:
✅ 适合的场景:
• 本地开发调试(Mac / Linux 笔记本)
• 快速体验、对比不同模型
• 个人知识助手、私人 AI 工具
❌ 不适合的场景:
• 生产环境高并发(不支持 Continuous Batching)
• 多 GPU 部署(不支持 Tensor Parallel)
• 极致性能优化(无 PagedAttention)2.5 性能基准对比:吞吐量 / 延迟 / 显存占用
以 Qwen2.5-14B-Instruct(INT4 量化)在单张 A10 (24GB) 上的测试数据:
基准测试条件:
模型:Qwen2.5-14B-Instruct-AWQ(INT4)
GPU:NVIDIA A10 24GB
输入:256 tokens | 输出:512 tokens
并发:16 个并发请求
┌───────────────────┬──────────┬──────────┬──────────┐
│ 指标 │ vLLM │ TGI │ Ollama │
├───────────────────┼──────────┼──────────┼──────────┤
│ 吞吐量(req/s) │ 12.8 │ 9.2 │ 2.1 │
│ 首 Token 延迟 │ 85ms │ 120ms │ 350ms │
│ 生成速度(tok/s) │ 142 │ 108 │ 45 │
│ 显存占用 │ 11.2 GB │ 12.8 GB │ 9.4 GB │
│ P99 延迟 │ 4.2s │ 5.8s │ 18.6s │
└───────────────────┴──────────┴──────────┴──────────┘
vLLM ████████████████████████████████ 12.8 req/s
TGI ███████████████████████ 9.2 req/s
Ollama █████ 2.1 req/s关键发现:
性能差异的根本原因:
vLLM 最快:
├── PagedAttention → 显存利用率高 → 能同时处理更多请求
├── Continuous Batching → GPU 利用率 95%+
└── CUDA Graph → 减少 kernel launch 开销
TGI 中等:
├── Flash Attention 2 → 计算效率高
├── 有 Continuous Batching,但实现不如 vLLM 优化
└── Rust 后端 → 稳定但灵活性不如 Python
Ollama 最慢:
├── llama.cpp 后端 → 针对 CPU 优化,GPU 利用率不高
├── 无 Continuous Batching → 请求串行处理
└── 但显存占用最低(GGUF 格式更紧凑)💡 不要被 Ollama 的低性能吓到——Ollama 虽然并发性能弱,但单请求延迟在个人使用场景已经足够快(首 token 350ms)。选框架要看场景,不是一味追求 benchmark。
第 2 章核心知识回顾:
| 概念 | 一句话解释 |
|---|---|
| vLLM | 性能之王,PagedAttention + Continuous Batching |
| TGI | HF 官方,Docker 一键部署,生态集成好 |
| Ollama | 最简单,一行命令跑模型,适合个人/开发 |
| PagedAttention | KV Cache 按页分配,显存利用率 30% → 90% |
| 选型建议 | 生产→vLLM,HF 生态→TGI,个人→Ollama |
3. 模型选型与获取
3.1 主流开源模型对比:Llama 3 / Qwen 2.5 / Mistral / DeepSeek
2024-2025 年开源模型已经能覆盖大部分生产场景。主流选择:
| 模型 | 可选尺寸 | 中文能力 | 代码能力 | 许可证 | 推荐场景 |
|---|---|---|---|---|---|
| Llama 3.1 | 8B / 70B / 405B | ⭐⭐ | ⭐⭐⭐⭐ | Llama License | 英文为主、代码生成 |
| Qwen 2.5 | 7B / 14B / 32B / 72B | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | Apache 2.0 | 中文场景首选 |
| Mistral | 7B / 8×7B (MoE) | ⭐⭐ | ⭐⭐⭐ | Apache 2.0 | 欧洲合规、多语言 |
| DeepSeek V3 | 671B (MoE) | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | MIT | 代码、数学推理 |
| Yi 1.5 | 6B / 9B / 34B | ⭐⭐⭐⭐ | ⭐⭐⭐ | Apache 2.0 | 中文通用 |
| GLM-4 | 9B | ⭐⭐⭐⭐ | ⭐⭐⭐ | 自定义 | 中文对话 |
快速选型指南:
中文为主?
├── YES → Qwen 2.5(中文最强开源模型)
│ ├── 资源充足 → 72B
│ └── 资源有限 → 14B / 32B INT4
│
└── NO → 英文/代码为主?
├── 代码 → DeepSeek Coder / Llama 3.1
└── 通用 → Llama 3.1 70B / Mistral 8×7B3.2 模型大小与质量的权衡:7B / 14B / 32B / 72B 怎么选
模型不是越大越好——要在质量和成本之间找到甜蜜点:
模型大小 vs 能力 vs 成本:
能力 ▲
│ ● 72B(接近 GPT-4 水平)
│ ● 32B(大部分生产场景够用)
│ ● 14B(性价比最高)
│ ● 7B(简单任务足够)
│ ● 3B(嵌入式/边缘设备)
└──────────────────────────────────→ 成本
低 高| 尺寸 | 典型质量 | INT4 显存 | 推荐场景 | 对标 API |
|---|---|---|---|---|
| 7B | 能完成简单指令 | ~4 GB | 分类、提取、简单问答 | GPT-3.5 基础版 |
| 14B | 流畅的多轮对话 | ~8 GB | 客服、FAQ、翻译 | GPT-3.5 Turbo |
| 32B | 复杂推理和创作 | ~18 GB | 代码生成、分析报告 | GPT-4o-mini |
| 72B | 接近 SOTA 水平 | ~40 GB | 高质量内容、复杂推理 | GPT-4o(部分场景) |
💡 14B INT4 是自部署的甜蜜点——一张 RTX 4090(24GB)就能跑,质量足以覆盖 80% 的生产场景(客服、翻译、提取、问答)。只有在复杂推理和代码生成场景才需要升级到 32B+。
3.3 模型下载:Hugging Face / ModelScope / 镜像加速
模型文件通常几十 GB,下载渠道的选择直接影响效率:
# ── 方法一:Hugging Face CLI(国际最全) ──
pip install huggingface_hub
huggingface-cli download Qwen/Qwen2.5-14B-Instruct-AWQ \
--local-dir /data/models/qwen2.5-14b-awq
# ── 方法二:ModelScope(国内最快,免翻墙) ──
pip install modelscope
modelscope download --model Qwen/Qwen2.5-14B-Instruct-AWQ \
--local_dir /data/models/qwen2.5-14b-awq
# ── 方法三:HF 镜像站(国内加速 HF) ──
export HF_ENDPOINT=https://hf-mirror.com
huggingface-cli download Qwen/Qwen2.5-14B-Instruct-AWQ \
--local-dir /data/models/qwen2.5-14b-awq
# ── 方法四:Ollama 直接下载(最简单) ──
ollama pull qwen2.5:14b # 自动下载 GGUF 格式| 渠道 | 速度(国内) | 模型数量 | 适用框架 |
|---|---|---|---|
| Hugging Face | 慢(需翻墙) | 最全(80 万+) | vLLM / TGI |
| HF 镜像站 | 快 | 同 HF | vLLM / TGI |
| ModelScope | 最快 | 较多(10 万+) | vLLM / TGI |
| Ollama Registry | 快 | GGUF 为主 | Ollama |
3.4 模型格式:SafeTensors / GGUF / AWQ / GPTQ
不同格式适用于不同推理框架——格式选错直接无法加载:
| 格式 | 精度 | 适用框架 | 特点 |
|---|---|---|---|
| SafeTensors | FP16 / BF16 | vLLM / TGI | 原始精度,最大显存占用 |
| GPTQ | INT4 / INT8 | vLLM / TGI | 离线量化,需校准数据集 |
| AWQ | INT4 | vLLM | 保护重要权重,质量更好 |
| GGUF | Q4_K_M / Q5 等 | Ollama / llama.cpp | CPU+GPU 混合推理 |
| FP8 | FP8 | vLLM(H100) | H100 专属,平衡精度和速度 |
格式选择流程:
你用什么推理框架?
│
├── vLLM → 首选 AWQ(质量最好的 INT4)
│ 次选 GPTQ → 再次 SafeTensors(FP16)
│
├── TGI → 首选 GPTQ → 次选 SafeTensors
│
└── Ollama → 只能用 GGUF
推荐 Q4_K_M(平衡质量和大小)💡 同一个模型,AWQ 的量化质量通常优于 GPTQ——AWQ 会分析哪些权重对输出影响大,保护这些"重要权重"不被激进量化。在 vLLM 上推荐优先选 AWQ 格式的模型。
第 3 章核心知识回顾:
| 概念 | 一句话解释 |
|---|---|
| 中文首选 | Qwen 2.5 系列(7B-72B) |
| 代码首选 | DeepSeek Coder / Llama 3.1 |
| 甜蜜点 | 14B INT4,一张 4090 就能跑 |
| 国内下载 | ModelScope 或 HF 镜像站 |
| 格式选择 | vLLM→AWQ,TGI→GPTQ,Ollama→GGUF |
4. 模型量化:用更少的显存跑更大的模型
4.1 量化原理:FP16 → INT8 → INT4 的精度与显存变化
量化的本质是用更低精度的数据类型存储模型权重,从而减少显存占用:
不同精度的表示范围与大小:
FP32(32位浮点)── 每个参数 4 字节
████████████████ 精度极高,但显存占用最大
FP16(16位浮点)── 每个参数 2 字节
████████ 标准推理精度,质量无损
INT8(8位整数)─── 每个参数 1 字节
████ 轻微精度损失(~1%),显存减半
INT4(4位整数)─── 每个参数 0.5 字节
██ 可感知精度损失(~3-5%),显存减 75%以 Qwen2.5-32B 为例的实际效果:
| 精度 | 显存占用 | 需要的 GPU | 推理速度 | 质量损失 |
|---|---|---|---|---|
| FP16 | 64 GB | 1×A100 80G | 基准 | 0% |
| INT8 | 32 GB | 1×A100 40G | +10% | ~1% |
| INT4 (GPTQ) | 18 GB | 1×RTX 4090 | +25% | ~3% |
| INT4 (AWQ) | 18 GB | 1×RTX 4090 | +25% | ~2% |
4.2 GPTQ 量化:离线量化的标准方案
GPTQ 是最成熟的离线量化方法——需要一个校准数据集来决定量化参数:
GPTQ 的工作原理:
原始模型(FP16)
│
▼
① 准备校准数据集 ── 128-256 条代表性文本
│
▼
② 逐层量化 ──────── 每一层找到最优的量化参数
│ 使量化误差最小化(OBQ 算法)
▼
③ 输出量化模型 ──── INT4/INT8 权重 + 量化配置
│
▼
加载到 vLLM / TGI 推理# 使用 AutoGPTQ 进行量化
pip install auto-gptq
# Python 量化脚本
python -c "
from transformers import AutoTokenizer
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
model_id = 'Qwen/Qwen2.5-14B-Instruct'
quantize_config = BaseQuantizeConfig(bits=4, group_size=128, damp_percent=0.1)
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoGPTQForCausalLM.from_pretrained(model_id, quantize_config)
# 校准数据集(通常用 C4、WikiText 或你的业务数据)
examples = [tokenizer(t, return_tensors='pt') for t in calibration_texts[:128]]
model.quantize(examples)
model.save_quantized('/data/models/qwen2.5-14b-gptq-int4')
"💡 大多数情况不需要自己量化——HuggingFace 上已经有大量预量化的模型。搜索
模型名-GPTQ或模型名-AWQ直接下载即可。自己量化只在使用私有微调模型时需要。
4.3 AWQ 量化:保护重要权重的智能量化
AWQ(Activation-aware Weight Quantization)的核心思想是不是所有权重同等重要:
AWQ vs GPTQ 的区别:
GPTQ:所有权重平等对待,统一量化
权重: [0.12, 0.83, 0.04, 0.91, 0.07]
量化: [ 0, 1, 0, 1, 0 ] ← 所有权重都被压缩
AWQ:分析哪些权重对输出影响大,保护它们
权重: [0.12, 0.83, 0.04, 0.91, 0.07]
重要度:[低, ★高, 低, ★高, 低 ]
量化: [ 0, 0.8, 0, 0.9, 0 ] ← 重要权重保留更高精度
→ AWQ 在同样 INT4 下,质量损失比 GPTQ 少 ~30%# AWQ 量化(使用 autoawq 库)
pip install autoawq
python -c "
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_id = 'Qwen/Qwen2.5-14B-Instruct'
quant_config = {'zero_point': True, 'q_group_size': 128, 'w_bit': 4, 'version': 'GEMM'}
model = AutoAWQForCausalLM.from_pretrained(model_id)
tokenizer = AutoTokenizer.from_pretrained(model_id)
model.quantize(tokenizer, quant_config=quant_config)
model.save_quantized('/data/models/qwen2.5-14b-awq')
"4.4 GGUF 量化:CPU + GPU 混合推理(Ollama 专用)
GGUF 是 llama.cpp 生态的量化格式,最大特点是支持 CPU + GPU 混合推理——显存不够时,把一部分层放到 CPU 内存运行:
GGUF 的 CPU + GPU 混合推理:
72B 模型(INT4 需要 ~40GB)
│
├── GPU 只有 24GB?
│ │
│ └── 前 40 层 → GPU(24GB 显存)
│ 后 40 层 → CPU(16GB 内存)
│ → 速度降低 50%,但至少能跑!
│
└── GPU 有 80GB?
└── 全部 80 层 → GPU
→ 全速运行GGUF 的量化等级(从高质量到高压缩):
| 量化等级 | 每参数占用 | 质量损失 | 适用场景 |
|---|---|---|---|
| Q8_0 | 8.5 bits | ~0.5% | 最高质量,显存充足时用 |
| Q6_K | 6.6 bits | ~1% | 高质量 + 适度压缩 |
| Q5_K_M | 5.7 bits | ~2% | 平衡选择 |
| Q4_K_M | 4.8 bits | ~3% | 推荐默认选择 |
| Q3_K_M | 3.9 bits | ~5% | 极致压缩,有感知质量损失 |
| Q2_K | 3.4 bits | ~10% | 不推荐,损失太大 |
# Ollama 下载特定量化等级
ollama pull qwen2.5:32b-instruct-q4_K_M # 推荐
ollama pull qwen2.5:72b-instruct-q4_K_M # 大模型低显存方案
# 手动转换为 GGUF(需要 llama.cpp)
python llama.cpp/convert_hf_to_gguf.py \
/data/models/qwen2.5-14b \
--outfile qwen2.5-14b-q4_k_m.gguf \
--outtype q4_k_m4.5 量化效果对比:精度损失 vs 速度提升 vs 显存节省
用 Qwen2.5-14B-Instruct 在 A10 (24GB) 上的综合对比:
量化方法综合对比(Qwen2.5-14B-Instruct):
┌───────────┬──────────┬──────────┬──────────┬──────────┐
│ 量化方法 │ 显存占用 │ 生成速度 │ 质量得分 │ 推荐度 │
│ │ │ (tok/s) │ (满分100) │ │
├───────────┼──────────┼──────────┼──────────┼──────────┤
│ FP16 │ 28 GB │ 82 │ 100 │ 显存够时用│
│ INT8 │ 14 GB │ 95 │ 99 │ ⭐⭐⭐⭐ │
│ GPTQ-INT4 │ 8 GB │ 118 │ 96 │ ⭐⭐⭐ │
│ AWQ-INT4 │ 8 GB │ 120 │ 97.5 │ ⭐⭐⭐⭐⭐ │
│ GGUF-Q4KM │ 9 GB │ 45* │ 97 │ 个人使用 │
└───────────┴──────────┴──────────┴──────────┴──────────┘
* GGUF 速度较低因为 Ollama 无 Continuous Batching
显存节省:FP16(28GB) → AWQ-INT4(8GB) = 节省 71%
速度提升:FP16(82t/s) → AWQ-INT4(120t/s) = 提升 46%
质量损失:FP16(100) → AWQ-INT4(97.5) = 仅损失 2.5%💡 量化不仅省显存,还能加速推理——INT4 模型的数据传输量只有 FP16 的 1/4,而 GPU 推理通常是 memory-bound(显存带宽瓶颈),所以更少的数据量 = 更快的推理速度。
第 4 章核心知识回顾:
| 概念 | 一句话解释 |
|---|---|
| 量化公式 | 显存 ≈ 参数量 × 每参数字节数 |
| GPTQ | 需校准数据,统一量化,最成熟 |
| AWQ | 保护重要权重,同精度下质量更好 |
| GGUF | Ollama 专用,支持 CPU+GPU 混合 |
| 推荐方案 | vLLM 用 AWQ-INT4,Ollama 用 Q4_K_M |
5. 部署实战:三大框架的完整搭建
5.1 vLLM 部署:安装 → 启动 → OpenAI 兼容 API
Step 1:安装 vLLM
# 前置要求:CUDA 12.1+, Python 3.9+
pip install vllm
# 验证安装
python -c "import vllm; print(vllm.__version__)"Step 2:启动 OpenAI 兼容 API 服务
# 最简启动(AWQ 量化模型)
vllm serve Qwen/Qwen2.5-14B-Instruct-AWQ \
--host 0.0.0.0 \
--port 8000
# 生产级启动(带关键优化参数)
vllm serve Qwen/Qwen2.5-14B-Instruct-AWQ \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 1 \
--gpu-memory-utilization 0.90 \
--max-model-len 8192 \
--max-num-seqs 64 \
--enable-prefix-caching \
--quantization awq \
--dtype half关键参数说明:
| 参数 | 作用 | 推荐值 |
|---|---|---|
--gpu-memory-utilization | GPU 显存使用比例 | 0.90(留 10% 余量) |
--max-model-len | 最大上下文长度 | 按需设置,越短越省显存 |
--max-num-seqs | 最大并发请求数 | 32-128 |
--enable-prefix-caching | 共享 System Prompt 缓存 | 生产必开 |
--tensor-parallel-size | 多 GPU 并行数 | GPU 数量 |
Step 3:调用 API(和 OpenAI SDK 完全兼容)
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")
response = client.chat.completions.create(
model="Qwen/Qwen2.5-14B-Instruct-AWQ",
messages=[{"role": "user", "content": "用 Python 实现快速排序"}],
temperature=0.7,
max_tokens=1000,
stream=True, # 支持流式输出
)
for chunk in response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")5.2 TGI 部署:Docker 一键启动 → 参数调优
TGI 推荐用 Docker 部署——镜像预装了所有 CUDA 依赖:
# 创建模型目录
mkdir -p /data/models
# 一键启动 TGI
docker run --gpus all -d \
--name tgi \
-p 8080:80 \
-v /data/models:/data \
-e HUGGING_FACE_HUB_TOKEN=$HF_TOKEN \
ghcr.io/huggingface/text-generation-inference:latest \
--model-id Qwen/Qwen2.5-14B-Instruct-GPTQ \
--quantize gptq \
--max-input-tokens 4096 \
--max-total-tokens 8192 \
--max-batch-prefill-tokens 4096 \
--max-concurrent-requests 64TGI 的关键调优参数:
| 参数 | 作用 | 推荐值 |
|---|---|---|
--max-input-tokens | 最大输入长度 | 按场景设置 |
--max-total-tokens | 输入+输出总长度 | 8192 |
--max-concurrent-requests | 最大并发 | 64-128 |
--max-batch-prefill-tokens | Prefill 批大小 | 4096 |
--num-shard | 多 GPU 分片数 | GPU 数量 |
# TGI 的 API 调用
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "tgi",
"messages": [{"role": "user", "content": "什么是 Transformer?"}],
"max_tokens": 500,
"stream": true
}'💡 vLLM 和 TGI 都兼容 OpenAI API 格式——只需要把
base_url从api.openai.com改成你的服务地址,应用代码零修改。这意味着你可以随时在 API 调用和自部署之间切换。
5.3 Ollama 部署:安装 → 下载模型 → 本地 API
Ollama 的部署流程最简单——三步搞定:
# Step 1:安装
curl -fsSL https://ollama.com/install.sh | sh
# Step 2:下载并启动模型
ollama run qwen2.5:14b
# Step 3:API 已自动启动在 http://localhost:11434
curl http://localhost:11434/v1/chat/completions \
-d '{
"model": "qwen2.5:14b",
"messages": [{"role": "user", "content": "你好"}]
}'自定义模型配置——用 Modelfile 控制行为:
# Modelfile - 自定义模型配置
FROM qwen2.5:14b
# 设置默认参数
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 8192
PARAMETER num_gpu 99
# 设置系统提示词
SYSTEM """你是一个专业的技术助手,请用中文回答所有问题。
回答要简洁、准确、有代码示例。"""# 创建自定义模型
ollama create my-tech-assistant -f Modelfile
# 运行自定义模型
ollama run my-tech-assistant5.4 Docker Compose 编排:一键启动完整推理服务
生产环境推荐用 Docker Compose 编排完整服务栈:
# docker-compose.yml
version: "3.8"
services:
# vLLM 推理服务
vllm:
image: vllm/vllm-openai:latest
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=all
volumes:
- /data/models:/models
command: >
--model /models/qwen2.5-14b-awq
--host 0.0.0.0
--port 8000
--gpu-memory-utilization 0.90
--max-model-len 8192
--max-num-seqs 64
--enable-prefix-caching
--quantization awq
ports:
- "8000:8000"
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
# Nginx 反向代理
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
vllm:
condition: service_healthy# nginx.conf
upstream vllm_backend {
server vllm:8000;
}
server {
listen 80;
location /v1/ {
proxy_pass http://vllm_backend/v1/;
proxy_set_header Host $host;
proxy_read_timeout 300s; # LLM 生成可能很慢
proxy_buffering off; # 流式输出必须关闭缓冲
}
}# 一键启动
docker compose up -d
# 查看状态
docker compose ps
docker compose logs -f vllm💡 Nginx 的
proxy_buffering off是流式输出的关键——如果不关闭,Nginx 会把 SSE 事件缓存起来一次性返回,用户看到的就不是"逐字打出"而是"等半天突然全出来"。
第 5 章核心知识回顾:
| 概念 | 一句话解释 |
|---|---|
| vLLM | vllm serve + OpenAI 兼容 API,生产首选 |
| TGI | Docker 一行命令,HF 生态集成好 |
| Ollama | 三步搞定,Modelfile 自定义配置 |
| Docker Compose | 编排 vLLM + Nginx,一键启动 |
| 关键配置 | --gpu-memory-utilization 0.90 + --enable-prefix-caching |
6. 并发优化与性能调优
6.1 Continuous Batching:为什么它是吞吐量的关键
传统的静态 Batching 和 vLLM 的 Continuous Batching 差异巨大:
静态 Batching(传统方式):
时间 →→→→→→→→→→→→→→→→→→→→→→→→→→→
请求A ████████████████████████████ (完成)
请求B ████████████████████████████ (完成)
请求C ████████████ (完成,但要等 A/B)
请求D █████████████████ (等整个批次完成才能开始)
→ 请求 C 早就完成了,但被 A/B 拖住
→ 请求 D 白白等待
→ GPU 利用率 ~50%
Continuous Batching(vLLM/TGI):
时间 →→→→→→→→→→→→→→→→→→→→→→→→→→→
请求A ████████████████████████████ (完成)
请求B ████████████████████████████ (完成)
请求C ████████████ (完成 → 位置立即让给 D)
请求D █████████████████ (C 完成后立即开始)
请求E ████████████████ (B 完成后立即开始)
→ 完成一个就填入一个
→ GPU 永远满载
→ GPU 利用率 ~95%💡 Continuous Batching 是选择 vLLM/TGI 而非 Ollama 的最核心原因——在 16 并发场景下,Continuous Batching 的吞吐量是静态 Batching 的 3-5 倍。
6.2 KV Cache 管理:PagedAttention 与显存分配
KV Cache 是推理阶段最大的显存消耗——模型权重是固定的,但 KV Cache 随并发动态增长:
显存的构成:
总显存 = 模型权重 + KV Cache + 临时计算
以 Qwen2.5-14B-AWQ 在 24GB GPU 上为例:
┌──────────────────────────────────────────────┐
│ 模型权重(AWQ INT4): 8 GB(固定) │
│ ██████████████ │
│ │
│ KV Cache(动态): ~14 GB(可配置) │
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ │
│ 临时计算 + 系统: ~2 GB(固定) │
│ ████ │
└──────────────────────────────────────────────┘
KV Cache 决定了能同时处理多少请求:
每个请求的 KV Cache = 层数 × 2 × 头数 × 头维度 × 序列长度 × 2字节
14B 模型:40 × 2 × 40 × 128 × 4096 × 2B ≈ 3.3 GB/请求
14 GB 可用 KV Cache ÷ 3.3 GB/请求 ≈ 4 个并发
但 PagedAttention 不预分配全部长度 → 实际可跑 16-32 并发!6.3 Tensor Parallel:多 GPU 并行推理
当单张 GPU 无法放下模型时,用 Tensor Parallel 把模型拆分到多张 GPU:
Tensor Parallel 的拆分方式:
单 GPU(72B FP16 需要 144GB → 单卡放不下):
┌──────────────────────────────┐
│ Layer 1-80 → 一张 GPU? │
│ ❌ 显存不够 │
└──────────────────────────────┘
2-GPU Tensor Parallel:
┌──────────────┐ ┌──────────────┐
│ GPU 0 │ │ GPU 1 │
│ 每层的前半部分 │ │ 每层的后半部分 │
│ 72 GB │ │ 72 GB │
└──────────────┘ └──────────────┘
→ 每张 GPU 负担一半参数
→ 需要 GPU 间高速通信(NVLink 最佳)# vLLM 多 GPU 启动
vllm serve Qwen/Qwen2.5-72B-Instruct \
--tensor-parallel-size 2 \ # 2 张 GPU
--gpu-memory-utilization 0.90
# TGI 多 GPU 启动
docker run --gpus all \
ghcr.io/huggingface/text-generation-inference:latest \
--model-id Qwen/Qwen2.5-72B-Instruct \
--num-shard 2 # 2 张 GPU| GPU 配置 | 推荐模型 | Tensor Parallel |
|---|---|---|
| 1× A10 (24G) | 14B INT4 / 7B FP16 | 不需要 |
| 2× A10 (48G) | 32B FP16 / 72B INT4 | --tp 2 |
| 4× A100 (320G) | 72B FP16 + 大并发 | --tp 4 |
| 8× H100 (640G) | 405B FP16 | --tp 8 |
💡 Tensor Parallel 的效率取决于 GPU 间通信带宽——2 张通过 PCIe 连接的 GPU,Tensor Parallel 效率约 70-80%;通过 NVLink 连接的效率可达 95%。尽量选 NVLink 互联的服务器。
6.4 请求调度策略:队列管理与优先级
当并发请求超过 GPU 处理能力时,需要请求排队和优先级调度:
import asyncio
from dataclasses import dataclass, field
from enum import IntEnum
class Priority(IntEnum):
HIGH = 0 # VIP 用户、付费接口
NORMAL = 1 # 普通用户
LOW = 2 # 后台任务、批量处理
@dataclass(order=True)
class InferenceRequest:
priority: int
created_at: float = field(compare=False)
prompt: str = field(compare=False)
future: asyncio.Future = field(compare=False)
class RequestScheduler:
"""推理请求调度器:优先级队列 + 超时管理"""
def __init__(self, max_queue_size: int = 200, timeout: float = 60.0):
self.queue = asyncio.PriorityQueue(maxsize=max_queue_size)
self.timeout = timeout
self.active_requests = 0
async def submit(self, prompt: str, priority: Priority = Priority.NORMAL) -> str:
"""提交推理请求"""
if self.queue.full():
raise Exception("推理队列已满,请稍后重试")
future = asyncio.get_event_loop().create_future()
request = InferenceRequest(
priority=priority, created_at=time.time(),
prompt=prompt, future=future,
)
await self.queue.put(request)
# 等待结果(带超时)
try:
return await asyncio.wait_for(future, timeout=self.timeout)
except asyncio.TimeoutError:
raise Exception(f"推理超时({self.timeout}s)")💡 队列深度是重要的监控指标——当队列持续大于 50 时,说明 GPU 处理能力不足。应该考虑扩容(加 GPU)或限流(拒绝低优先级请求)。
6.5 性能调优参数速查表
一张表汇总 vLLM 所有关键调优参数:
| 参数 | 默认值 | 推荐值 | 说明 |
|---|---|---|---|
--gpu-memory-utilization | 0.90 | 0.85-0.95 | 越高越多并发,但 OOM 风险增大 |
--max-model-len | 模型最大值 | 按需降低 | 降到 4096 可多 50% 并发 |
--max-num-seqs | 256 | 32-128 | 限制同时处理的请求数 |
--max-num-batched-tokens | 自动 | 8192-16384 | 每次 Prefill 的 token 数 |
--enable-prefix-caching | false | true | System Prompt 复用,必开 |
--enable-chunked-prefill | false | true | 长输入分块 Prefill |
--swap-space | 4 | 4-16 GB | CPU swap 空间 |
--enforce-eager | false | false | 关闭 CUDA Graph(调试用) |
--dtype | auto | half / bfloat16 | 计算精度 |
性能调优的优先级(投入产出比从高到低):
① 选对量化格式 ──── AWQ INT4 > GPTQ > FP16(ROI 最高)
② 开启 Prefix Caching ── 大部分场景 System Prompt 相同
③ 调整 max-model-len ─── 不需要 128K 就别设 128K
④ 调整 gpu-memory-utilization ── 0.90 通常最佳
⑤ Tensor Parallel ────── 多 GPU 线性提升第 6 章核心知识回顾:
| 概念 | 一句话解释 |
|---|---|
| Continuous Batching | 完成一个填入一个,GPU 利用率 95%+ |
| KV Cache | 随并发增长,PagedAttention 按页管理 |
| Tensor Parallel | 多 GPU 拆分模型,NVLink 效率最高 |
| 请求调度 | 优先级队列 + 超时管理 |
| 首要优化 | 选对量化 > Prefix Caching > 调参数 |
7. GPU 资源管理与多模型部署
7.1 GPU 显存规划:模型大小 × 并发数 × KV Cache
生产环境必须做精确的显存规划——OOM 会导致服务直接崩溃:
显存规划公式:
总显存需求 = 模型权重 + KV Cache + 计算开销 + 系统预留
模型权重 = 参数量 × 每参数字节数
14B × 0.5B (INT4) = 7 GB
KV Cache = 并发数 × 每请求 KV Cache 大小
每请求 = 层数 × 2 × 头数 × 头维度 × 平均序列长度 × 2B
14B 模型:≈ 800 MB/请求(平均 2048 tokens)
16 并发 → 16 × 0.8 GB = 12.8 GB
计算开销 + 系统 ≈ 2 GB
总计 = 7 + 12.8 + 2 = 21.8 GB → 需要 24GB GPU ✓| 模型 | 精度 | 8 并发 | 16 并发 | 32 并发 | 推荐 GPU |
|---|---|---|---|---|---|
| 7B | INT4 | 10 GB | 13 GB | 19 GB | RTX 4090 |
| 14B | INT4 | 14 GB | 22 GB | 36 GB | A10 / A100 40G |
| 32B | INT4 | 26 GB | 38 GB | 62 GB | A100 80G |
| 72B | INT4 | 52 GB | 68 GB | 100 GB | 2×A100 80G |
7.2 多模型共存:一台机器跑多个模型
多 GPU 服务器可以每张 GPU 跑不同的模型,通过不同端口对外提供服务:
# GPU 0 跑通用模型
CUDA_VISIBLE_DEVICES=0 vllm serve Qwen/Qwen2.5-14B-Instruct-AWQ \
--port 8000 --gpu-memory-utilization 0.90 &
# GPU 1 跑代码模型
CUDA_VISIBLE_DEVICES=1 vllm serve deepseek-ai/DeepSeek-Coder-V2-Lite-Instruct \
--port 8001 --gpu-memory-utilization 0.90 &
# Nginx 按路由分发
# /v1/chat → 8000(通用)
# /v1/code → 8001(代码)# nginx.conf - 多模型路由
upstream general_model { server localhost:8000; }
upstream code_model { server localhost:8001; }
server {
listen 80;
location /v1/chat/ { proxy_pass http://general_model/v1/; }
location /v1/code/ { proxy_pass http://code_model/v1/; }
}7.3 模型热加载与卸载:按需切换节省显存
如果 GPU 资源有限,可以按需加载模型——不用的模型卸载释放显存:
class ModelManager:
"""模型热加载管理器"""
def __init__(self, gpu_memory_limit_gb: int = 24):
self.loaded_models: dict[str, dict] = {}
self.gpu_memory_limit = gpu_memory_limit_gb
async def get_model(self, model_name: str) -> str:
"""获取模型的 API 地址,自动加载/卸载"""
if model_name in self.loaded_models:
self.loaded_models[model_name]["last_used"] = time.time()
return self.loaded_models[model_name]["url"]
# 显存不够时,卸载最久未使用的模型
while self._used_memory() + self._model_size(model_name) > self.gpu_memory_limit:
lru_model = min(self.loaded_models,
key=lambda k: self.loaded_models[k]["last_used"])
await self._unload(lru_model)
url = await self._load(model_name)
return url7.4 CPU 卸载:显存不够时的降级方案
当显存确实不够时,可以把模型的部分层卸载到 CPU 内存:
# Ollama 自动 CPU 卸载(GGUF 格式天然支持)
ollama run qwen2.5:72b # 自动判断:GPU 放不下的层放 CPU
# vLLM CPU 卸载(通过 swap space)
vllm serve Qwen/Qwen2.5-32B-Instruct-AWQ \
--swap-space 16 \ # 16GB CPU 内存作为 swap
--gpu-memory-utilization 0.95💡 CPU 卸载是"能跑"和"跑得快"的折衷——把 20% 的层放到 CPU 会让推理速度下降约 40%。如果预算允许,优先升级 GPU 而不是依赖 CPU 卸载。
第 7 章核心知识回顾:
| 概念 | 一句话解释 |
|---|---|
| 显存公式 | 模型权重 + KV Cache(并发相关)+ 开销 |
| 多模型 | CUDA_VISIBLE_DEVICES 隔离 + Nginx 路由 |
| 热加载 | LRU 策略按需加载/卸载模型 |
| CPU 卸载 | 显存不够的降级方案,速度下降明显 |
8. 生产部署:从开发到上线
8.1 API 网关:Nginx 反向代理 + 负载均衡
多实例 vLLM 通过 Nginx 做负载均衡:
upstream llm_cluster {
least_conn; # 最少连接数策略(推荐)
server 10.0.0.1:8000 weight=1;
server 10.0.0.2:8000 weight=1;
keepalive 32; # 保持长连接
}
server {
listen 80;
# API 限流:每秒 100 个请求
limit_req_zone $binary_remote_addr zone=llm:10m rate=100r/s;
location /v1/ {
limit_req zone=llm burst=50;
proxy_pass http://llm_cluster/v1/;
proxy_read_timeout 300s;
proxy_buffering off;
proxy_set_header Connection '';
proxy_http_version 1.1;
}
}8.2 健康检查与自动重启
# systemd 服务配置(自动重启)
# /etc/systemd/system/vllm.service
[Unit]
Description=vLLM Inference Server
After=network.target
[Service]
Type=simple
User=appuser
ExecStart=/usr/bin/vllm serve /models/qwen2.5-14b-awq \
--host 0.0.0.0 --port 8000 --gpu-memory-utilization 0.90
Restart=always
RestartSec=10
Environment=CUDA_VISIBLE_DEVICES=0
[Install]
WantedBy=multi-user.target# 启动并设置开机自启
sudo systemctl enable --now vllm
sudo systemctl status vllm8.3 监控与报警:GPU 利用率 / 推理延迟 / 队列深度
核心监控指标:
| 指标 | 采集方式 | 报警阈值 |
|---|---|---|
| GPU 利用率 | nvidia-smi / DCGM | < 50%(浪费)或 > 95%(过载) |
| GPU 显存 | nvidia-smi | > 90%(OOM 风险) |
| 推理延迟 P99 | vLLM /metrics | > 30s |
| 队列深度 | vLLM /metrics | > 50(需扩容) |
| 请求错误率 | Nginx 日志 | > 1% |
# vLLM 自带 Prometheus 指标
curl http://localhost:8000/metrics
# 关键指标:
# vllm:num_requests_running - 正在处理的请求数
# vllm:num_requests_waiting - 等待队列中的请求数
# vllm:gpu_cache_usage_perc - KV Cache 使用率
# vllm:avg_generation_throughput_toks_per_s - 生成吞吐量8.4 日志与调试:请求追踪与错误排查
# vLLM 日志级别调整
VLLM_LOGGING_LEVEL=DEBUG vllm serve ...
# 常见错误排查
# OOM → 降低 --gpu-memory-utilization 或 --max-model-len
# CUDA error → 检查驱动版本,升级到 CUDA 12.1+
# 模型加载慢 → 检查磁盘 IO,模型放 SSD8.5 自动扩缩容:基于队列深度的弹性伸缩
# 简易自动扩缩容脚本
import subprocess, requests, time
def get_queue_depth() -> int:
metrics = requests.get("http://localhost:8000/metrics").text
for line in metrics.split("\n"):
if "num_requests_waiting" in line and not line.startswith("#"):
return int(float(line.split()[-1]))
return 0
def scale(replicas: int):
subprocess.run(["docker", "compose", "up", "-d",
"--scale", f"vllm={replicas}"])
while True:
depth = get_queue_depth()
if depth > 50:
scale(2) # 扩容到 2 实例
elif depth < 5:
scale(1) # 缩回 1 实例
time.sleep(30)💡 GPU 服务的扩缩容比 CPU 服务慢得多——模型加载需要 30-120 秒,冷启动成本高。建议设置较保守的缩容策略(等待 10 分钟低负载才缩容),避免频繁抖动。
第 8 章核心知识回顾:
| 概念 | 一句话解释 |
|---|---|
| 负载均衡 | Nginx least_conn + proxy_buffering off |
| 自动重启 | Systemd Restart=always |
| 核心指标 | GPU 利用率 + 队列深度 + P99 延迟 |
| 扩缩容 | 基于队列深度,注意模型加载冷启动耗时 |
9. 成本分析:自部署 vs API 调用的盈亏平衡点
9.1 GPU 服务器成本:购买 vs 租用 vs 云 GPU
| 方案 | 代表 | 月成本 | 优势 | 劣势 |
|---|---|---|---|---|
| 购买服务器 | RTX 4090 工作站 | ~$150(折旧) | 长期最便宜 | 一次性投入大(~$5,000) |
| GPU 云租用 | AutoDL / vast.ai | $200-800 | 按需使用,灵活 | 数据在别人服务器 |
| 云厂商 GPU | AWS / 阿里云 | $800-3,000 | 企业级 SLA | 最贵 |
| Serverless GPU | RunPod / Modal | 按秒计费 | 零运维 | 冷启动慢(30-120s) |
各方案的月成本对比(24GB GPU,7×24 运行):
购买 4090 █████ $150/月(按 3 年折旧)
AutoDL ████████ $200/月
vast.ai ██████████ $250/月
阿里云 A10 ████████████████████ $500/月
AWS A10G ███████████████████████████ $700/月9.2 盈亏平衡点计算:多少请求量时自部署更划算
盈亏平衡点计算:
API 成本 = 日均请求量 × 单次成本 × 30天
自部署成本 = GPU 月租 + 运维人工(可忽略)
以 GPT-4o ($0.03/次) vs 自部署 Qwen2.5-14B ($200/月) 为例:
$0.03 × N × 30 = $200
N = $200 ÷ ($0.03 × 30)
N ≈ 222 次/天
→ 日均超过 222 次调用,自部署就比 GPT-4o API 划算!
更实际的对比(考虑模型质量差异):
┌──────────────┬──────────┬──────────┬──────────┐
│ 日均请求量 │ API 月费 │ 自部署月费 │ 结论 │
├──────────────┼──────────┼──────────┼──────────┤
│ 100 次 │ $90 │ $200 │ API 划算 │
│ 500 次 │ $450 │ $200 │ 自部署赢 │
│ 5,000 次 │ $4,500 │ $200 │ 自部署大赢│
│ 50,000 次 │ $45,000 │ $800 │ 自部署完胜│
└──────────────┴──────────┴──────────┴──────────┘9.3 混合策略:自部署兜底 + API 弹性扩展
最优的生产方案是自部署处理基线流量,API 处理峰值溢出:
class HybridLLMRouter:
"""混合路由:自部署优先,溢出到 API"""
def __init__(self):
self.local = OpenAI(base_url="http://localhost:8000/v1", api_key="local")
self.remote = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
self.local_queue_threshold = 30 # 本地队列超过 30 就溢出
async def chat(self, messages: list, **kwargs) -> str:
# 检查本地服务队列深度
if await self._local_queue_depth() < self.local_queue_threshold:
try:
return await self._call_local(messages, **kwargs)
except Exception:
pass # 本地失败,降级到 API
# 溢出到 API
return await self._call_remote(messages, **kwargs)
async def _local_queue_depth(self) -> int:
metrics = requests.get("http://localhost:8000/metrics").text
# 解析 num_requests_waiting 指标
for line in metrics.split("\n"):
if "num_requests_waiting" in line and not line.startswith("#"):
return int(float(line.split()[-1]))
return 0混合策略的成本优势:
纯 API(日均 5,000 次): $4,500/月
纯自部署(1×A10): $200/月,但峰值可能排队
混合策略:
├── 自部署处理 80% 请求 → $200/月
└── API 处理 20% 溢出 → $900/月
└── 总计 $1,100/月,节省 75%,无排队💡 混合策略是大多数生产系统的最优解——自部署覆盖日常基线,API 弹性应对峰值。既控制成本(节省 70%+),又保证了服务质量(零排队)。
第 9 章核心知识回顾:
| 概念 | 一句话解释 |
|---|---|
| 性价比最高 | AutoDL / vast.ai 租用 ~$200/月 |
| 盈亏点 | GPT-4o 替代:日均 >222 次自部署划算 |
| 混合策略 | 自部署兜底 + API 溢出,节省 70%+ |
附录
A. 推理框架选型决策表
| 你的场景 | 推荐框架 | 推荐模型 | 推荐量化 |
|---|---|---|---|
| 个人开发 / 体验 | Ollama | Qwen2.5:14b | GGUF Q4_K_M |
| 小团队内部工具 | vLLM | Qwen2.5-14B-AWQ | AWQ INT4 |
| B 端 SaaS 产品 | vLLM | Qwen2.5-32B-AWQ | AWQ INT4 |
| 代码辅助工具 | vLLM | DeepSeek-Coder-V2 | AWQ INT4 |
| 合规场景(金融/医疗) | vLLM / TGI | Qwen2.5-72B | FP16 / INT8 |
| 最大规模 to C | vLLM + API 混合 | 72B + GPT-4o | AWQ + API |
B. GPU 显存需求速查表
| 模型 | FP16 | INT8 | INT4 | 推荐 GPU(INT4) |
|---|---|---|---|---|
| 7B | 14 GB | 7 GB | 4 GB | RTX 4060 (8G) |
| 14B | 28 GB | 14 GB | 8 GB | RTX 4090 (24G) |
| 32B | 64 GB | 32 GB | 18 GB | RTX 4090 / A10 |
| 72B | 144 GB | 72 GB | 40 GB | A100 80G |
| 405B | 810 GB | 405 GB | 220 GB | 8×A100 / 4×H100 |
C. 常见问题与排错指南
| 问题 | 原因 | 解决方案 |
|---|---|---|
| OOM: CUDA out of memory | 显存不足 | 降低 --gpu-memory-utilization 或用更小模型 |
| 模型加载超级慢 | 磁盘 IO 瓶颈 | 模型放 NVMe SSD |
| 首 Token 延迟很长 | Prefill 阶段计算量大 | 减少 --max-input-tokens |
| 吞吐量上不去 | 未开启 Continuous Batching | 升级到 vLLM(自动开启) |
| 输出乱码 | 量化损失或 tokenizer 不匹配 | 换更高精度量化(Q5/Q6) |
| GPU 利用率低 | 请求不够多 | 增大 --max-num-seqs |
| CUDA 版本不兼容 | 驱动/CUDA/PyTorch 版本冲突 | 用 Docker(预装所有依赖) |