跳到主要内容
推理优化

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 tokenprompt 分布:均值 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达标线边缘
+1max-num-seqs 32→64TTFT p95 2.8→3.4s ✗批次太贪,回滚
+2开投机 EAGLE-3TPOT 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 不是越大越好?
  • 调优记录表包含哪些字段?为什么一次只动一个变量?
  • 上线的”四件套”是什么?
  • 复盘后沉淀的六件套资产是什么?
  • 你能为你的业务做一遍同样的四步法吗?

📚 参考资料