推理优化
11.3 端到端部署实战:从需求分析到上线监控
完整实战案例:需求分析(SLO/负载画像)→ 方案设计(框架/量化/并行)→ 部署压测(vLLM 配置与调优)→ 上线监控(指标/告警/容量)
端到端实战部署SLOvLLM上线监控
收官实战:把一个真实的业务需求变成一条上线的推理服务。场景设定:“智能客服”业务,需要上线 Llama-3.1-70B-Instruct 对话服务。全程走四步:需求分析 → 方案设计 → 部署压测 → 上线监控。每一步都复用前面章节的方法与命令——这是模块四所有知识的”总装配”。
📑 目录
1. 场景与需求分析
1.1 业务需求(需求方给的原话)
场景:智能客服(多轮对话,固定 System Prompt,用户消息平均 300 token)
预期:日活 10 万用户,高峰集中在 10:00-12:00 / 14:00-16:00
要求:
- 首 token 平均 < 1.5s,p95 < 3s(TTFT SLO)
- 生成速度平均 > 30 token/s(TPOT SLO)
- 服务可用性 99.9%
1.2 转成工程参数(10.4 的输入)
| 业务参数 | 工程换算 | 说明 |
|---|---|---|
| 日活 10 万 | 高峰 QPS ≈ 30-60(每人每天 ~10 次请求,高峰集中) | 取 60 做设计目标 |
| System Prompt 固定 | 前缀缓存命中率预期 70%+(10.3 路由协同) | 缓存是核心杠杆 |
| 平均输入 300 token | prompt 分布:均值 500(含 system),长尾 4K | 长尾决定 KV |
| 平均输出 200 token | 输出分布:均值 200,长尾 2K | 长尾决定 TPOT |
1.3 约束
- 硬件预算:现有 8 × H100 80G(不能新买)
- 必须内网部署(数据不出域),云上方案排除
- 无量化精度损失容忍度:需先测量化对客服质量的影响
📌 关键点:需求分析产出的是”带数字的清单”——QPS、token 分布、SLO、约束,缺一不可。含糊的需求(“要快”)无法设计,每缺一个数字,后面就多一次返工。
2. 方案设计
2.1 选型决策(按 11.1 决策树)
显存检查(10.4):
Llama-3.1-70B FP16 = 140G → 单卡放不下
→ 选项 A:TP2(每卡 70G 权重)+ INT8 量化(每卡 35G)✓ 选 A
→ 选项 B:单卡 INT8(70G 权重,KV 空间剩 0)✗ 无 KV 空间
计算预估(2N 法则):
单卡 H100 FP16 ≈ 989 TFLOPS,利用率 0.35
TP2 + INT8 后算力需求减半 → 单副本理论 ~1200 token/s
实际按 60% 折扣(调度开销)→ ~700 token/s 预估
SLO 与并发:
60 QPS × 平均 250 输出 token ≈ 15k token/s 生成
→ 副本数 ≈ 15k / 700 ≈ 22 副本?→ 8 卡 H100 只够 4 副本(TP2)
→ 结论:**预算不够,必须上缓存与投机**(见 2.3)
2.2 方案 A:纯优化版(8 卡,硬扛)
TP2 × 4 副本,INT8 量化
+ 前缀缓存(system prompt 固定,命中率 70%+)
+ 投机解码 EAGLE-3(输出 200 token 场景,1.8-2.2x)
→ 有效吞吐:700 × 1.7(缓存+投机)≈ 1200 token/s/副本
→ 4 副本 ≈ 4800 token/s ≥ 15k?✗ 仍不够 → 调低目标或砍 SLO
2.3 方案 B:加 PD 分离(8 卡,最优分配)
把 8 卡按 7 章架构分配:
2 卡 → prefill 实例(算力型,扛 60 QPS 的 prefill)
6 卡 → decode 实例 × 3(TP2,访存型,每实例 ~800 token/s)
加前缀路由(10.3 cache_aware)保证 system prompt 同副本
能力核算:
prefill 侧:60 QPS × 500 token = 30k token/s prefill 需求
→ 2 卡 prefill(利用率高)+ 前缀缓存命中 70% → 实需 9k token/s ✓
decode 侧:60 QPS × 250 token = 15k token/s
→ 3 实例 × 800 × 1.5(投机)≈ 3.6k ✗ 还是差
→ 结论:8 卡带不动 60 QPS 的 70B → 与业务谈:峰值 QPS 降到 30,
或改用更小模型(8B 档)——这是容量规划的真正价值:**提前暴露不可能**
📌 关键点:容量规划(10.4)在方案阶段的真正作用:提前说”不”。8 卡跑 70B 扛 60 QPS 在算力上就不可能——方案设计阶段算清楚,比上线后被打爆好一万倍。最终结论:降到 30 QPS 目标 + 方案 B(或换 8B 模型,本例继续用 70B/30QPS 演示)。
3. 部署与压测调优
3.1 部署清单(10.1 + 6 章)
# K8s Deployment 要点(完整版见 10.1)
resources:
limits: { nvidia.com/gpu: "2" } # TP2
requests: { nvidia.com/gpu: "2" }
volumes:
- name: shm # TP 通信共享内存
emptyDir: { medium: Memory, sizeLimit: "8Gi" } # TP2 需要更大
readinessProbe:
httpGet: { path: /health, port: 8000 }
initialDelaySeconds: 120 # 70B 加载 + 图捕获 > 2 分钟
periodSeconds: 10
# 启动命令(v0.26)
vllm serve /models/llama-3.1-70b-int8 \
--tensor-parallel-size 2 \
--quantization int8 \
--max-model-len 8192 \
--enable-prefix-caching \
--gpu-memory-utilization 0.92 \
--max-num-seqs 64 \
--speculative-config '{"method": "eagle3", "model": "eagle3-70b-draft", "num_speculative_tokens": 3}' \
--api-key sk-prod-xxx
3.2 压测验证(9.2 流程)
# 1. 负载画像(贴近生产:system prompt + 300 token 用户消息 + 200 输出)
python3 bench/prompt_generator.py --style chat --sys-prompt ./system_prompt.txt -n 500
python3 bench/synthetic_requests.py --input 500.txt --output-len 200
# 2. 单请求时延(冒烟)
vllm bench latency --model /models/llama-3.1-70b-int8 --port 8000 \
--input-len 500 --output-len 200
# 3. 吞吐与 SLO 拐点(逐步加压)
for qps in 10 20 30 40; do
vllm bench serve --port 8000 --dataset bench/requests.jsonl \
--request-rate $qps --max-concurrency 256 | tee /tmp/bench_qps$qps.log
done
3.3 调优记录(11.2 纪律:一次一个变量)
| 轮次 | 改动 | 结果 | 结论 |
|---|---|---|---|
| 基线 | TP2 + INT8 + 缓存 | TTFT p95 2.8s,TPOT 45ms | 达标线边缘 |
| +1 | max-num-seqs 32→64 | TTFT p95 2.8→3.4s ✗ | 批次太贪,回滚 |
| +2 | 开投机 EAGLE-3 | TPOT 45→27ms ✓ 接受率 0.68 | 保留 |
| +3 | 前缀路由 cache_aware | 命中率 61%→78%,TTFT p95 3.2→2.1s ✓ | 保留 |
| 终版 | 全部 | TTFT p95 2.1s ✓,TPOT p95 27ms ✓ | 达标(SLO 见 1.1) |
💡 提示:注意第 1 轮的教训:max-num-seqs 不是越大越好——batch 大了 prefill 排队,TTFT 反而恶化(2 章调度原理)。这就是”一次一个变量”的价值:每轮结果都可归属。
4. 上线与监控
4.1 上线清单(10.3 安全 + 10.2 观测)
安全(10.3 三件套):
✓ 网关 API Key 校验 + TLS + 内网隔离
✓ vLLM --api-key 兜底
✓ 请求限流(单 key QPS、max_tokens 上限)
观测(10.2):
✓ Prometheus 抓取 /metrics + ServiceMonitor
✓ Grafana:总览(TTFT/TPOT/吞吐)、容量(KV 水位)、缓存(命中率)
✓ 结构化日志 → Loki
告警(10.2 清单):
P1:kv_cache_usage_perc > 0.95 持续 5m → 扩容/降负载
P1:corrupted_requests 增长
P2:TTFT p95 > 3s 持续 10m
P2:num_preemptions 速率 > 0 持续 5m
4.2 容量与成本(10.4 闭环)
上线后 2 周收集真实指标:
真实峰值 QPS、命中率、TTFT/TPOT 分布
→ 回填容量模型(10.4 公式)
→ 结论:峰值 28 QPS < 设计 30 → 无需扩容;命中率 78% 高于预期 → 缓存收益兑现
→ 月成本核算:8 × H100 预留实例 + 存储 ≈ 预算内 ✓
4.3 回归门禁(9.4 收尾)
把 3.2 的压测固化为 CI 门禁:
- 每次改配置/升级 vLLM → 自动跑基准场景 → 与基线对比
- 退化 > 10% 即阻塞合并
- 门禁场景:本案例的 chat 负载画像(系统 prompt + 300/200 token)
📌 关键点:上线的定义不是”能访问”,而是”有观测、有告警、有容量模型、有回归门禁”。四件套缺一,后续每一次变更都像盲飞。
5. 复盘:把本次实战沉淀为模板
本次实战产出的可复用资产:
① 需求分析模板:QPS/token 分布/SLO/约束 四要素
② 选型记录:为什么 TP2+INT8、为什么 EAGLE-3、为什么 PD 分离
③ 调优记录表:改动/结果/结论(3.3 的表)
④ 压测负载画像生成脚本(3.2)
⑤ 告警清单(4.1)+ 容量计算脚本(4.2)
⑥ 回归门禁场景(4.3)
下次上线新模型/新业务时:
复制模板 → 替换数字 → 跳过已沉淀的坑
💡 提示:端到端实战的真正产出是”模板”而非”一次上线”。把这次踩过的坑、量过的数、定过的阈值沉淀成文档/脚本/CI 配置,下次(换个模型、换个业务)只改参数。工程能力的标志:同样的事,第二次比第一次快 10 倍。
📝 总结
- 四步法:需求分析(带数字的清单)→ 方案设计(显存/算力先算账)→ 部署压测(一次一个变量)→ 上线监控(观测/告警/门禁四件套)
- 容量规划在方案阶段的真正价值:提前说”不”(8 卡扛不住 60QPS×70B 就早点降目标)
- 调优纪律:一次一个变量,每轮记录改动/结果/结论
- 上线定义:可访问 + 有观测 + 有告警 + 有容量模型 + 有回归门禁
- 终极产出是模板:需求/选型/调优/压测/告警/门禁六件套可复用资产
🎯 自我检验清单
- 需求分析的四要素是什么?为什么”要快”不算需求?
- 方案设计中显存/算力先算哪一步?为什么?
- 本案例为什么选择 TP2 + INT8?(每卡权重 35G 且留 KV 空间)
- 为什么 max-num-seqs 不是越大越好?
- 调优记录表包含哪些字段?为什么一次只动一个变量?
- 上线的”四件套”是什么?
- 复盘后沉淀的六件套资产是什么?
- 你能为你的业务做一遍同样的四步法吗?
📚 参考资料
- 本模块各章(实战每一步的原理出处:第 2/3/4/5/6/7/9/10 章)
- vLLM 官方文档:Quickstart(https://docs.vllm.ai/en/latest/getting_started/quickstart.html)
- vLLM 官方文档:Performance Benchmarking(https://docs.vllm.ai/en/latest/performance/benchmarks/benchmarks.html)