推理资源的预算方法

📅 2026/8/21 14:04:50
推理资源的预算方法
推理资源的预算方法阅读说明本文以推理服务中的典型故障链路说明排查和设计方法。文中的告警、数字与“线上”叙述如未给出来源均应视为示例条件落地前请在自己的版本、负载和资源约束下复测。1. 业务高峰期的卡顿告警8 卡 A100 显存全红与并发暴跌下面用一个假设场景说明 推理服务 中应先检查哪些信号以及如何验证判断。晚高峰 20:30Prometheus 告警钉钉群连续推送了 12 条 GPU 资源耗尽消息。线上部署的 Llama-3-70B 推理集群突然出现严重的 P99 延迟飙升原本维持在 200ms 左右的首字延迟TTFT迅速冲到 4.5 秒并发 Token 吞吐量从 3500 tokens/s 断崖式跌至 400 tokens/s。登跳板机查看 kubectl top node发现 4 台配备了 8 卡 A100-80G 的节点显存利用率全部处于 99.2% 的饱和状态。然而用nvidia-smi深度观察GPU-Util算力利用率却奇特地徘徊在 28%~35% 之间。这意味着算力没有用满显存却被明显卡死。更糟糕的是Kubernetes 的 HPAHorizontal Pod Autoscaler因为仅仅依赖 CPU 和通用显存占用指标在并发骤增时触发了在未验证前扩容。由于大模型镜像高达 30GB 且权重加载耗时超过 3 分钟新 Pod 还没准备好原有的推理节点就已经因为 KV Cache 内存溢出OOM频繁重启。解决这个问题的关键在于必须搞清楚 LLM 推理过程中显存到底被哪些组件吃掉了以及如何通过确定性的指标来驱动 GPU Pod 的弹性伸缩。2. 算力成本拆解KV Cache 动态占用与 Token 吞吐的数学账评估大模型推理算力成本不能照搬传统 Web 应用的 CPU/RAM 预算逻辑。LLM 显存消耗由三部分硬性组成模型权重静态显存、上下文 KV Cache 动态显存、以及 CUDA Context 与临时激活值Activation。以 FP16 精度的 70B 模型为例其静态显存占用是固定的$$ Memory_{static} 70 \times 10^9 \times 2 \text{ Bytes} \approx 140 \text{ GB} $$若采用 4 卡 Tensor ParallelismTP4每张 A100 占用 35GB 静态显存。剩下 45GB 显存由 PagedAttention 划分为物理 Block 管理。此时决定单卡最大并发能力的是 KV Cache 的理论极限。假设模型 Layer 数量为 $L80$Key/Value 头数为 $H8$隐层维度为 $D128$最大上下文长度 $S4096$则单个 Request 满上下文时的 KV Cache 占用为$$ Memory_{KV} 2 \times L \times H \times D \times S \times 2 \text{ Bytes} 2 \times 80 \times 8 \times 128 \times 4096 \times 2 \approx 1.34 \text{ GB} $$这意味着剩余 45GB 显存理论上最多只能同时容纳大约 33 个满上下文请求。如果线上出现突发长文本并发KV Cache 短时间内填满推理引擎就不得不将物理 Block 换出到 CPU 内存Swap-out或者强行抢占Preemption中断请求导致整体吞吐下降 80% 以上。成本算账的逻辑显而易见在未验证前增加 GPU 节点是昂贵的。按单张 A100 每小时 2.5 美元计算扩容一个 8 卡节点意味着每月增加 1.4 万美元成本。因此必须将 KV Cache 的实际利用率vllm:gpu_cache_usage_perc与请求队列长度vllm:num_requests_waiting作为 K8s HPA 的核心 Metric实现精准伸缩。3. vLLM 与 K8s HPA 结合的弹性伸缩架构设计传统的 HPA 基于 CPU 利用率扩容在大模型场景下完全失效。必须引入 Custom Metrics API将 vLLM 暴露的 Prometheus 业务指标暴露给 K8s 伸缩控制器。伸缩决策不能采用简单的线性比例算法。如果只凭“显存超过 80%”就扩容冷启动的 3 分钟窗口期足以让服务明显级联故障。我们设计的弹性架构分为三层入口闸门Ingress Gate在 vLLM 前置 Nginx/Envoy 限制最大等待队列。超过安全阈值的请求直接返回 429 降级响应保护现有推理节点不崩溃。指标采集层Prometheus Prometheus-Adapter每 5 秒抓取一次vllm:gpu_cache_usage_perc和vllm:num_requests_waiting。阶梯式 HPA 策略当num_requests_waiting 5且持续 15 秒时触发 Pod 数量加倍当gpu_cache_usage_perc 30%持续 10 分钟时才平滑缩容避免频繁冷启动振荡。4. 生产级 Prometheus 监控指标提取与 Python 弹性扩缩防线代码下面的 Python 服务展示了如何安全地作为 K8s Custom Metrics 适配器获取 vLLM 暴露的指标并实施包含熔断与预热检查的扩缩容防线计算。import time import requests from typing import Dict, Tuple class LLMInferenceAutoscalerGuard: def __init__( self, vllm_metrics_url: str, max_gpu_cache_threshold: float 0.85, max_waiting_queue: int 10, warmup_cooldown_seconds: int 180 ): self.vllm_metrics_url vllm_metrics_url self.max_gpu_cache_threshold max_gpu_cache_threshold self.max_waiting_queue max_waiting_queue self.warmup_cooldown_seconds warmup_cooldown_seconds self.last_scale_time 0.0 def parse_prometheus_metrics(self, raw_text: str) - Dict[str, float]: metrics {} for line in raw_text.splitlines(): if line.startswith(#) or not line.strip(): continue parts line.split() if len(parts) 2: key, val parts[0], parts[1] try: metrics[key] float(val) except ValueError: continue return metrics def fetch_inference_health(self) - Tuple[bool, float, int]: try: resp requests.get(self.vllm_metrics_url, timeout2.0) if resp.status_code ! 200: # 监控端点异常触发安全降级防御 return False, 1.0, 999 parsed self.parse_prometheus_metrics(resp.text) cache_usage parsed.get(vllm:gpu_cache_usage_perc, 0.0) waiting_reqs int(parsed.get(vllm:num_requests_waiting, 0)) return True, cache_usage, waiting_reqs except Exception as err: # 网络超时或解析故障必须阻止危险的缩容操作 return False, 1.0, 999 def evaluate_scale_decision(self, current_replicas: int) - Tuple[str, int]: now time.time() is_healthy, cache_usage, waiting_reqs self.fetch_inference_health() if not is_healthy: # 指标异常时保持副本数拒绝在未验证前缩容 return HOLD_UNHEALTHY_METRICS, current_replicas # 防频繁冷却震荡检查 if now - self.last_scale_time self.warmup_cooldown_seconds: return COOLDOWN_WAITING, current_replicas # 紧急扩容条件KV Cache 接近红线 或 等待队列堆积 if cache_usage self.max_gpu_cache_threshold or waiting_reqs self.max_waiting_queue: target_replicas min(current_replicas * 2, 16) # 单次最多翻倍上限16 Pod if target_replicas ! current_replicas: self.last_scale_time now return SCALE_UP_EMERGENCY, target_replicas # 平滑缩容条件显存占用低于 30% 且无排队 if cache_usage 0.30 and waiting_reqs 0: target_replicas max(current_replicas - 1, 2) # 保持最小2副本保底 if target_replicas ! current_replicas: self.last_scale_time now return SCALE_DOWN_SMOOTH, target_replicas return HOLD_STABLE, current_replicas if __name__ __main__: guard LLMInferenceAutoscalerGuard( vllm_metrics_urlhttp://127.0.0.1:8000/metrics, max_gpu_cache_threshold0.80, max_waiting_queue5, warmup_cooldown_seconds180 ) # 模拟一次检测 action, replicas guard.evaluate_scale_decision(current_replicas4) print(f[Autoscaler Decision] Action: {action}, Target Replicas: {replicas})5. 压测回归从 1200 QPS 到 P99 降至 180ms 的成本效益对比实施该方案后我们在 Staging 环境使用 Vegeta 模拟了包含长短文本混合的突发流量QPS 从 200 短时间内飙升至 1200。对比数据非常清晰在使用传统 CPU HPA 策略时推理集群在第 40 秒出现大规模 429 拒答P99 延迟突破 5000ms整体 GPU 显存频繁引发 vLLM 强行 Swap实际完成的吞吐仅为 850 tokens/s。采用基于gpu_cache_usage_perc与等待队列梯度的自定义扩缩容方案后当等待队列首次突破 5 个请求时系统立即拦截溢出流量并平滑触发扩容。因为设置了 2 副本的温备基线流量被有效平摊。最终 P99 延迟稳定在 180ms无任何节点发生显存 OOM。集群在突发流量退去 10 分钟后顺畅缩容月度 GPU 算力成本相比固定配额方案降低了 42%。小结把结论留给可复现的结果本文的场景用于说明推理服务的检查顺序不代表某个环境的既成事故或固定收益。变更前应记录基线、版本与配置控制流量或样本并比较尾延迟、错误率和资源占用未达到预设门槛时应保留或回退原方案。