推理服务容量规划中的经验教训:从流量预测模型到资源预留的过度与不足
推理服务容量规划中的经验教训从流量预测模型到资源预留的过度与不足一、容量规划的两极困境推理服务的容量规划存在两极困境预留过多导致资源浪费GPU 实例利用率 30%预留不足导致服务质量下降P99 延迟 SLA。两极困境的根因是推理负载的不确定性——LLM 推理的延迟随输入长度、并发数、模型版本波动且流量模式难以预测对话类应用有明显的时段波动。七月参与一个推理服务的容量规划发现三类规划失误1流量预测模型基于历史均值而非峰值低估突发流量 40%2资源预留按最大并发计算而非基于延迟约束导致GPU 空闲但延迟仍超标3弹性扩容策略的冷启动时间被忽略——新实例加载模型需 30-60 秒突发流量期间扩容来不及。二、容量规划失误的分类模型将三类失误按规划维度分类流量预测、资源分配、弹性策略。流量预测失误基于均值而非峰值。历史流量的平均值掩盖了峰值波动。对话类应用在 9:00-10:00 的流量可能是凌晨 3:00-4:00 的 5-10 倍。基于均值规划时峰值时段的容量不足低谷时段的资源浪费。正模式使用 P95/P99 流量值而非均值。同时分析时段波动模式日周期、周周期在不同时段使用不同的容量基线。未建模突发流量概率。突发流量如营销活动、热门事件可能在短时间内将 QPS 提升 3-5 倍。突发流量的概率虽低但影响极大——SLA 违约的损失远超资源浪费的成本。正模式将突发流量建模为概率事件如每月 1-2 次持续 1-2 小时QPS 峰值 3-5 倍。为突发流量预留弹性缓冲而非固定资源。资源分配失误按最大并发而非延迟约束。最大并发数仅考虑 GPU 的计算容量能处理多少请求但未考虑延迟约束每个请求多快完成。LLM 推理的延迟随并发数非线性增长——并发从 1 到 10 时吞吐量提升 3 倍但每个请求的延迟增加 2 倍。正模式基于延迟约束计算容量。SLA 要求 P99 500ms 时最大并发数不是GPU 能处理多少而是延迟 500ms 时 GPU 能处理多少。需要实际基准测试确定延迟-并发曲线。忽略 KV Cache 动态占用。推理的显存占用分为固定部分模型权重和动态部分KV Cache。KV Cache 的占用随并发数和序列长度线性增长。规划时忽略 KV Cache 会导致并发数增加时显存不足但 CPU/GPU 计算容量仍有余量。正模式将显存占用分为固定动态两部分。总显存 模型权重 最大并发 × 平均序列长度 × KV Cache 单 token 占用。预留 10-20% 余量防止边界情况。弹性策略失误冷启动时间未纳入。新 GPU 实例从启动到可用需要模型下载5-10GB约 30s-2min 模型加载到 GPU约 10-30s 预热推理约 1-5s。总计 40s-150s。突发流量期间的 40s-150s 空窗可能导致 SLA 违约。正模式弹性扩容的响应时间应小于 SLA 允许的延迟容忍度。如果响应时间 容忍度应使用固定容量 弹性缓冲而非纯弹性策略。缩容阈值过于激进。流量低谷时立即缩容下一个流量高峰时又需要扩容——频繁缩容扩容的冷启动成本累积可能超过维持多余实例的成本。正模式缩容延迟应大于流量低谷的持续时间。使用冷却期策略流量低于阈值持续 15-30 分钟后才缩容。三、容量规划决策框架的实现以下代码展示基于延迟约束的容量计算和弹性策略的决策引擎。/// 基于延迟约束的容量计算 struct CapacityCalculator { // 延迟-并发曲线实测基准数据 latency_curve: LatencyConcurrencyCurve, // SLA 延迟约束 sla_latency_ms: u64, // 模型规格 model_spec: ModelSpec, // 硬件规格 hardware: HardwareProfile, } struct LatencyConcurrencyCurve { // 实测数据点(并发数, P50延迟, P99延迟) data_points: Vec(u32, f64, f64), } impl CapacityCalculator { /// 计算最大安全并发数延迟不超过 SLA 约束 fn compute_max_safe_concurrency(self) - u32 { // 从延迟曲线中找到 P99 SLA 的最大并发数 self.latency_curve.data_points.iter() .filter(|(_, _, p99)| *p99 self.sla_latency_ms as f64) .map(|(concurrency, _, _)| *concurrency) .max() .unwrap_or(1) // 至少支持 1 并发 } /// 计算显存需求固定动态 fn compute_memory_requirement(self, concurrency: u32) - MemoryRequirement { let model_weight_mb self.model_spec.weight_size_mb(); // KV Cache 并发数 × 平均序列长度 × 单token占用 let kv_cache_mb concurrency as f64 * self.model_spec.avg_seq_length as f64 * self.model_spec.kv_cache_per_token_bytes as f64 / (1024.0 * 1024.0); // 系统开销余量10-20% let overhead_mb (model_weight_mb kv_cache_mb) * 0.15; MemoryRequirement { model_weight_mb, kv_cache_mb, overhead_mb, total_mb: model_weight_mb kv_cache_mb overhead_mb, } } /// 计算所需实例数 fn compute_instance_count(self, target_qps: f64) - u32 { let max_safe_concurrency self.compute_max_safe_concurrency(); // 单实例吞吐 安全并发数 × 单并发QPS let single_instance_qps max_safe_concurrency as f64 * self.latency_curve.single_concurrency_qps(); // 实例数 目标 QPS / 单实例吞吐向上取整 let instances (target_qps / single_instance_qps).ceil() as u32; // 弹性缓冲额外 1-2 个实例应对突发流量 instances 2 } } /// 弹性策略决策引擎 struct ElasticityEngine { // 冷启动时间 cold_start_time_ms: u64, // 扩容阈值QPS 超过当前容量的 70% scale_up_threshold: f64, // 缩容冷却期流量低于阈值持续多久后缩容 scale_down_cool_down_ms: u64, // 当前实例数 current_instances: u32, // 目标容量基于 SLA 延迟约束 target_capacity: CapacityTarget, } impl ElasticityEngine { /// 弹性扩缩容决策 fn decide_scaling_action(self, current_load: LoadMetrics) - ScalingAction { let load_ratio current_load.qps / self.target_capacity.max_qps; if load_ratio self.scale_up_threshold { // 扩容冷启动时间是否可接受 if self.cold_start_time_ms self.target_capacity.max_latency_ms / 2 { // 冷启动时间过长使用固定容量而非弹性 ScalingAction::NoAction { reason: cold start time exceeds latency budget, recommendation: increase fixed capacity instead of elastic scaling, } } else { ScalingAction::ScaleUp { additional_instances: self.compute_scale_up_count(load_ratio), } } } else if load_ratio 0.3 { // 缩容检查冷却期 if current_load.low_duration_ms self.scale_down_cool_down_ms { ScalingAction::ScaleDown { remove_instances: 1, } } else { // 冷却期未满保持当前容量 ScalingAction::NoAction { reason: cool-down period not met, recommendation: wait for sustained low traffic, } } } else { ScalingAction::NoAction { reason: load within normal range, recommendation: maintain current capacity, } } } }四、容量规划策略的适用边界基于延迟约束的容量计算适用场景SLA 有明确延迟约束如 P99 500ms、延迟-并发曲线非线性LLM 推理、延迟比吞吐更关键。禁用场景延迟容忍度极高如批处理推理、延迟-并发曲线线性可简单按并发数计算、无 SLA 约束。弹性扩容策略适用场景流量有明显时段波动对话应用、冷启动时间 延迟容忍度、有自动化扩容基础设施。禁用场景冷启动时间 延迟容忍度、流量平稳无波动、无自动化基础设施手动扩容延迟不可控。缩容冷却期策略适用场景流量有短时低谷但很快回升、冷启动成本高不值得频繁缩扩容。禁用场景流量低谷持续数小时冷却期过长不必要、冷启动极快缩容成本低。P95/P99 流量预测适用场景流量有明显波动、SLA 违约代价高、资源成本可控。禁用场景流量极平稳均值足够、资源成本极高P99 预测导致大量浪费。五、总结容量规划的两极困境根因是推理负载的不确定性——流量波动、延迟非线性增长、KV Cache 动态占用。流量预测应使用 P95/P99 值而非均值并建模时段波动和突发流量概率。容量计算应基于延迟约束而非最大并发数LLM 的延迟-并发曲线是非线性的。显存需求需分固定模型权重和动态KV Cache两部分计算预留 10-20% 余量。弹性扩容的冷启动时间需纳入决策——冷启动超过延迟容忍度时应使用固定容量。