Kubernetes HPA基于自定义指标(Prometheus)的Pod弹性伸缩实践

传统 Horizontal Pod Autoscaler(HPA)依赖 CPU/内存指标,无法反映业务真实负载。本文深入讲解如何利用 Prometheus 自定义指标驱动 HPA,实现基于请求速率、队列深度等业务维度的精准弹性伸缩,包含完整配置步骤、风险规避与回滚策略。

Kubernetes HPA基于自定义指标(Prometheus)的Pod弹性伸缩实践
封面图:ZuCDN · ZuCDN 原创

为什么需要基于自定义指标的弹性伸缩

Kubernetes 内置的 Horizontal Pod Autoscaler(HPA)默认只支持 CPU 和内存两种资源指标。然而在真实生产环境中,CPU 使用率并不能准确代表业务的并发压力——比如一个 Web 服务可能 CPU 占用很低,但请求队列已经堆积;一个消息消费者可能内存充足,但消费积压导致延迟飙升。单纯依赖资源指标往往导致扩缩容滞后或误判,造成资源浪费或服务抖动。

Prometheus 作为云原生生态的事实标准监控系统,能够采集任意业务指标(如 http_requests_totalqueue_depthgrpc_latency_seconds)。通过将 Prometheus 指标注册为 Kubernetes 的自定义指标,HPA 可以像使用 CPU 一样根据业务负载动态调整副本数。这种方式让弹性伸缩回归业务本质:用业务指标衡量实际压力,而非间接的资源消耗

HPA 自定义指标的工作机制

Kubernetes HPA 通过 metrics.k8s.io(资源指标)、custom.metrics.k8s.io(自定义指标)和 external.metrics.k8s.io(外部指标)三个 API 组获取数据。自定义指标特指那些由用户应用程序暴露的、与特定 Pod 或 Namespace 相关的度量值。当 HPA 检测到自定义指标值偏离目标阈值时,它会计算期望副本数并执行扩缩容。

实现这一链条的关键组件是 Prometheus Adapter(旧称 k8s-prometheus-adapter)。它运行在集群内部,定期从 Prometheus 查询指标,经过转换后暴露为 custom.metrics.k8s.ioexternal.metrics.k8s.io 端点,供 HPA 控制器消费。

整体架构与核心组件

Prometheus 数据源

Prometheus 通过 pull 方式采集应用暴露的指标(如 /metrics 端点),存储时间序列数据。你需要保证目标应用已经正确暴露了有价值的业务指标,并且 Prometheus 已配置好 ServiceMonitor 或直接抓取。

Prometheus Adapter

Adapter 负责两个核心翻译:

  • 指标发现:通过配置文件中的 rules 定义哪些 Prometheus 查询应该被暴露为 Kubernetes 自定义指标。
  • API 注册:将查询结果以 /apis/custom.metrics.k8s.io/v1beta1 的形式注册到 API 聚合层。

HPA 对象

HPA 使用 type: Pods(针对每个 Pod 的指标)或 type: Object(针对集群内对象的指标,如 Service)来引用自定义指标。HPA 控制器每隔默认 15 秒轮询一次指标 API,根据当前值与目标值计算副本数。

完整配置步骤

前提条件

确保集群满足:

  • Kubernetes 版本 ≥ 1.11(建议 1.20+)
  • 集群已启用 API 聚合层(默认开启)
  • Prometheus 已部署且能成功抓取目标应用指标

步骤一:部署 Prometheus Adapter

推荐使用 Helm 安装,这样配置更灵活:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install prometheus-adapter prometheus-community/prometheus-adapter -n monitoring -f values.yaml

核心在于 values.yaml 中的 rules 配置段。例如,我们要将名为 http_requests_per_second 的 Prometheus 指标暴露给 HPA:

rules:
  - seriesQuery: '{__name__=~"http_requests.*", namespace!=""}'
    resources:
      overrides:
        namespace: {resource: "namespace"}
        pod: {resource: "pod"}
    name:
      matches: "http_requests_(.*)"
      as: "${1}_per_second"
    metricsQuery: 'sum(rate(<>{<>}[2m])) by (<>)'

关键字段说明:

  • seriesQuery:告诉 Adapter 去 Prometheus 搜索哪些 metric。
  • resources.overrides:将 Prometheus label(如 pod)映射到 Kubernetes 资源。
  • name:自定义指标的名字(最终出现在 HPA 中)。
  • metricsQuery:实际的 PromQL 查询模板,<> 等是模板变量。

步骤二:验证自定义指标可用

部署完成后,通过 kubectl 查看注册的指标:

kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 | jq .
kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests_per_second

如果返回正确数据(例如某个 Pod 的当前值),说明 Adapter 工作正常。

步骤三:创建 HPA 对象

假设我们有一个 Deployment 名为 my-web-service,期望每个 Pod 的每秒请求数不超过 1000。创建如下 HPA:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-web-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-web-service
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: 1000

这里 type: Pods 表示指标是每个 Pod 独有的,averageValue 表示所有 Pod 平均值的目标。如果使用 type: Object 则可以指定 Service 级别的指标。HPA 会持续比较每个 Pod 的实际值与目标值,当平均值超过 1000 时自动扩容,低于 1000 时缩容(需考虑冷却时间)。

验证弹性伸缩与回滚方案

验证方法

可以使用压测工具(如 wrk、hey)向应用发送请求,观察 HPA 的行为:

kubectl get hpa -w
kubectl get pods -l app=my-web-service

同时通过 Prometheus 监控 http_requests_per_second 指标的变化,确认 HPA 的扩缩容决策与业务负载同步。一般需要几分钟的预热期,因为 Adapter 的数据缓存和 Prometheus 的采集间隔可能导致延迟。

风险与应对

  • 指标延迟:Prometheus 通常以 15~30s 为间隔抓取,加上 Adapter 的缓存,HPA 决策可能滞后。建议在 HPA 的行为策略(behavior)中设置合理的稳定窗口和扩容加速。
  • Adapter 过载:当自定义指标数量庞大时,Adapter 可能成为性能瓶颈。需监控 Adapter 的响应时间,必要时增加副本或拆分规则。
  • 指标缺失导致 HPA 降级:如果某个 Pod 的指标暂时未采集到,HPA 会跳过该 Pod 忽略。确保应用始终暴露指标,且 Prometheus 配置了重试。

回滚到原生指标

若自定义指标方案出现问题,可以快速回退:

  1. 修改 HPA 的 metrics 字段,将自定义指标替换回 CPU:type: Resource + resource: cpu
  2. 或者删除 HPA,重新创建基于 CPU 的 HPA(如果之前有备份)。

建议在测试环境充分验证后再上线。同时保留一份原有的 HPA 配置 YAML,便于紧急回滚。

哪些场景最适合使用自定义指标 HPA

  • Web/API 服务:基于请求速率(QPS/RPS)扩容比 CPU 更敏感及时。
  • 消息队列消费者:根据队列深度(如 Kafka 堆积量)自动扩缩消费者 Pod。
  • 流处理作业:根据处理延迟或吞吐量动态调整 worker 数量。
  • 游戏服务器:根据在线玩家数(自定义指标)调整游戏房间 Pod。

相反,对于纯粹计算密集型的任务(如批处理作业),CPU 指标可能仍是更简单的选择。

常见陷阱与优化思路

  • 命名冲突:自定义指标名称在集群中必须唯一。如果多个应用暴露相同名称的指标,HPA 可能会错误关联。
  • 聚合层级选择:是使用 type: Pods 还是 type: Object?如果指标已经聚合到 Service 级别(如整个服务的平均 RPS),用 Object 更简单;如果是每个 Pod 独立统计(如连接数),用 Pods。
  • PromQL 性能:复杂的 PromQL 查询会增加 Adapter 和 Prometheus 的负担。尽量使用简单的 rate/avg,并设置合理的 range duration。

总结

基于 Prometheus 自定义指标的 HPA 让 Kubernetes 的弹性伸缩从资源层上升到业务层,是云原生应用实现精细化运维的重要工具。通过部署 Prometheus Adapter 并配置适当的规则,你可以让 HPA 感知诸如请求速率、队列深度、业务延时等真正驱动扩容的指标。本文给出了从部署到验证、回滚的完整路径,实践中应结合业务特点选择指标类型,并建立监控告警机制避免指标异常导致的扩缩容故障。将自定义指标 HPA 与 Kubernetes 原生的行为策略(稳定窗口、扩容率限制)相结合,能构建出既灵敏又稳健的自动扩缩容体系。

延伸阅读