Kubernetes 推荐服务扩缩容:流量波峰波谷的 HPA 配置 Kubernetes 推荐服务扩缩容流量波峰波谷的 HPA 配置一、定时扩缩容不够用推荐流量的不可预测性推荐系统是一个流量波动极大的服务。早上 8 点是低谷——用户刚起床中午 12 点慢慢爬升晚上 8-10 点是峰值——下班刷手机的黄金时段QPS 可能是凌晨的十倍以上。如果所有 Pod 都 7x24 全量跑算一笔 GPU 的账一张 A10 按 5 元/小时算10 张 GPU 一天 1200 元其中至少 40% 的时间 GPU 利用率不到 20%。一个月就是一万多块的浪费。CronHPA 定时扩缩容是最直观的方案——早上 8 点缩到 5 个 Pod晚上 8 点扩到 20 个 Pod。看似解决了问题实际上有两个致命缺陷。第一大促和突发事件完全打乱节奏。618 当天中午流量就飙到平时的峰值水平定时策略根本反应不过来。第二扩缩容时机的滞后。晚上 8 点准时扩容但流量可能 7:30 就开始涨了这半小时内现有 Pod 被打到 CPU 90% 以上延迟陡增。真正有效的方案是HPAHorizontal Pod Autoscaler CronHPA 配合使用。HPA 基于实时指标动态扩缩容应对突发流量CronHPA 做预测性扩容兜底。两者搭配才能在推荐场景的波峰波谷之间找到平衡点。二、HPA 指标选型CPU 不够还得看业务指标HPA 默认只支持 CPU 和内存作为扩缩容指标但推荐服务里 CPU 使用率不是一个好指标。一台跑推理的 GPU 节点CPU 可能只有 30%但 GPU 已经 100% 了——HPA 会认为一切正常不做扩容而实际延迟已经炸了。推荐服务至少配三个 HPA 指标GPU 使用率对于模型推理这种 GPU 密集负载GPU 利用率是最直接的饱和度信号。Triton Inference Server 和 TensorFlow Serving 都支持通过 DCGM Exporter 暴露 GPU 指标到 Prometheus。HPA 通过 Prometheus Adapter 读取 GPU 利用率设置扩容阈值为 75%。为什么是 75% 而不是 90%因为 GPU 从 75% 涨到 100% 可能只需要几秒钟给扩容留出响应时间。请求延迟 P99这是最贴近用户体验的指标。但直接用 latency 做 HPA 有个问题——它属于已发生的问题不能预判。解决方案是用延迟的增长率而非绝对值当 P99 延迟在 1 分钟内增长了 50% 以上触发扩容。这样比等延迟突破阈值再扩容要快 30 秒左右。请求队列深度推荐服务的请求通常在 gRPC 层排队。当队列深度超过 Pod 数 × 10每个 Pod 最多排队 10 个请求说明处理能力跟不上了需要扩容。配置 YAML 如下apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: rec-inference-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: rec-inference minReplicas: 3 maxReplicas: 20 metrics: - type: External external: metric: name: dcgm_gpu_utilization selector: matchLabels: app: rec-inference target: type: AverageValue averageValue: 75 - type: Pods pods: metric: name: grpc_server_request_latency_p99_rate target: type: AverageValue averageValue: 30 # P99延迟增长率% - type: Pods pods: metric: name: grpc_queue_depth target: type: AverageValue averageValue: 50 behavior: scaleUp: stabilizationWindowSeconds: 30 # 30秒窗口快速响应 policies: - type: Pods value: 3 periodSeconds: 30 scaleDown: stabilizationWindowSeconds: 300 # 5分钟窗口缓慢缩容 policies: - type: Pods value: 1 periodSeconds: 60三、缩容要慢、扩容要快——稳定性优先的配置策略HPA 的行为配置behavior字段是很多团队忽视的关键点。推荐系统的扩缩容策略有一个黄金原则扩容要快缩容要慢。扩容慢的代价是延迟飙升用户直接感受到卡顿。因此scaleUp.stabilizationWindowSeconds设为 30 秒——指标超标后等 30 秒就扩容不磨蹭。scaleUp.policies里一次最多扩 3 个 Pod——不能一次扩太多因为新 Pod 启动时模型加载需要 20-30 秒瞬间涌入太多新 Pod 会造成一段时间内所有新 Pod 都在加载模型、无人处理请求。缩容快的代价是振荡。流量高峰期 QPS 可能因为用户刷新行为波动CPU 瞬间掉到 30%HPA 立刻缩容然后 2 分钟后流量又回来又要扩容。Pod 反复创建销毁模型反复加载成了来回折腾。因此scaleDown.stabilizationWindowSeconds设为 300 秒——5 分钟内指标持续低于阈值才缩容。scaleDown.policies一次最多缩 1 个 Pod慢慢退。还有一个细节是minReplicas不能设为 1。推荐服务至少保留 3 个 Pod——不是为了扛流量而是为了扛故障。跑在 1 个实例上Pod 挂了整个推荐就挂了。3 个副本配合 PodAntiAffinity 分散到不同节点单节点故障不影响服务。验证 HPA 配置有效性的标准测试是波峰波谷压测。用 Vegeta 或 Locust 模拟流量曲线从 500 QPS 匀速爬升到 5000 QPS模拟晚高峰观察 HPA 的响应速度。合格的配置在流量上升 2 分钟内完成扩容P99 延迟波动不超过 50%。四、HPA 的边界条件冷启动延迟和模型加载成本HPA 解决了很多问题但在推荐场景下有一个独特的限制模型加载延迟。推理服务的 Pod 启动后第一件事是从对象存储OSS/S3下载模型文件。一个推荐模型通常 500MB 到 2GB下载需要 30-60 秒。在这段时间内Pod 虽然 Ready Probe 会延迟但 HPA 已经认为它可用了。如果不做特殊处理新 Pod 启动后会立即被分配流量但模型还没加载完请求全部失败。解决方案是在 Pod 的 Readiness Probe 中加入模型加载检测readinessProbe: exec: command: - /bin/sh - -c - | # 检查模型是否加载完成 curl -s http://localhost:8000/v2/models/recommendation/ready | grep -q READY initialDelaySeconds: 60 # 给模型下载留足时间 periodSeconds: 10 failureThreshold: 5 # 允许最多5次失败共110秒另一个边界条件是集群 GPU 资源池耗尽。HPA 可以无限发扩容指令但如果 GPU 节点池只有 8 张卡扩到 8 个 Pod 之后就没有新 Pod 能调度了。这个问题 HPA 处理不了需要配合 Cluster Autoscaler 动态扩节点。但 GPU 节点初始化比 CPU 节点慢得多——NVIDIA 驱动加载 CUDA 初始化需要 3-5 分钟。所以 GPU 服务需要更大的资源 buffer不能像 CPU 服务那样恰好够用。五、总结推荐服务的 HPA 配置远不是设几个 CPU 阈值那么简单。核心要点有三个指标选型要贴近业务GPU 利用率 延迟增长率 请求队列深度而非只看 CPU行为策略要偏稳定性扩容快缩容慢缩容窗口至少 5 分钟冷启动时间要考虑模型加载成本Readiness Probe 延迟、GPU 节点资源 buffer。HPA CronHPA 的组合是目前性价比最高的方案。HPA 应对突发CronHPA 应对已知的周期性峰值。两者叠加不是 112而是 11 兜住了彼此覆盖不到的场景。最终目标是推荐服务的可用性和延迟不受流量波动影响GPU 利用率在高峰期 80% 以上、低谷期也有 40%——不浪费每一张 GPU 的算力。

本月热点