K8s HPA 自定义指标弹性伸缩实战:用 Prometheus 指标控制 Pod 数量

K8s HPA Custom Metrics in Practice: Controlling Pod Count with Prometheus Metrics

| Ryan Zhang | 2026-07-26T13:07:23

CPU 和内存的默认 HPA 策略满足不了业务需求,这篇记录了怎么用 Prometheus 的自定义指标来做更精准的弹性伸缩。

A practical guide on implementing custom metrics-based HPA in Kubernetes using Prometheus for more precise autoscaling.

## 默认 HPA 的问题 K8s 自带的 HPA 只能根据 CPU 和内存来扩缩容。但我们的业务是消息队列消费者,CPU 使用率经常才 10% 但消息堆积了几万条。按 CPU 来算,HPA 觉得一切正常,不会扩容。 我们需要的是:**根据 Kafka 消费者的 lag(消息堆积量)来决定 Pod 数量**。 ## 整体架构 ``` Kafka → Consumer Pod → Prometheus(采集 lag 指标) ↓ Prometheus Adapter ↓ K8s Custom Metrics API ↓ HPA Controller ``` 核心思路:把 Prometheus 里的指标暴露成 K8s 的 Custom Metrics API,这样 HPA 就能读到了。 ## 第一步:Consumer 暴露指标 在 Java 消费者里用 Micrometer 暴露 Kafka lag 指标: ```java // 注册 gauge 指标 Gauge.builder("kafka.consumer.lag", this, ConsumerService::getLag) .tag("topic", "order-events") .tag("group", "order-processor") .register(meterRegistry); ``` Prometheus 抓取后会得到类似这样的指标: ``` kafka_consumer_lag{topic="order-events",group="order-processor"} 15234 ``` ## 第二步:部署 Prometheus Adapter ```yaml apiVersion: helm.sh/v3 # values.yaml prometheus: url: http://prometheus-server.monitoring.svc rules: custom: - seriesQuery: 'kafka_consumer_lag{namespace!="",pod!=""}' resources: overrides: namespace: {resource: "namespace"} pod: {resource: "pod"} name: matches: "^(.*)

quot; as: "kafka_consumer_lag" metricsQuery: 'avg(>{>}) by (>)' ``` 这个配置告诉 adapter 怎么把 Prometheus 指标映射到 K8s 的 custom metrics API。 ## 第三步:配置 HPA ```yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: order-consumer-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: order-consumer minReplicas: 2 maxReplicas: 20 metrics: - type: Pods pods: metric: name: kafka_consumer_lag target: type: AverageValue averageValue: "1000" # 每个 Pod 分摊 1000 条消息 behavior: scaleUp: stabilizationWindowSeconds: 30 policies: - type: Pods value: 4 periodSeconds: 60 scaleDown: stabilizationWindowSeconds: 300 policies: - type: Pods value: 1 periodSeconds: 120 ``` 几个关键参数: - `averageValue: 1000`:当每个 Pod 平均堆积超过 1000 条消息时扩容 - 扩容窗口 30 秒,缩容窗口 5 分钟:快速扩容,缓慢缩容 - 扩容每次最多加 4 个 Pod,缩容每次只减 1 个 ## 踩坑记录 **坑 1:指标延迟** Prometheus 默认 15 秒抓一次,adapter 有自己的缓存,HPA 默认 15 秒检查一次。加起来最坏情况下从堆积到扩容要 45 秒。对我们的业务来说可以接受,但如果你需要更快的响应,得把这些间隔都调短。 **坑 2:缩容震荡** 一开始缩容窗口设太短(60 秒),导致流量波动时反复扩缩容。Pod 刚起来还没注册消费者就被缩掉了。后来改成 300 秒就稳定了。 **坑 3:Adapter 的 metricsQuery 写法** `>`、`>`、`>` 这些模板变量容易搞混。建议先在 Prometheus 控制台手动跑一下 PromQL,确认没问题再填到配置里。 ## 效果 上线后: - 消息堆积从之前的峰值 5 万条降到了平均 2000 条以内 - Pod 数量从固定 10 个变成了 2-15 个动态调整 - 高峰期自动扩容,低谷期自动缩容,月成本降了约 35% 自定义指标 HPA 确实比默认的 CPU/Memory 策略灵活太多了,强烈推荐。


## The Problem Default HPA based on CPU/memory doesn't work for message queue consumers - CPU stays at 10% while thousands of messages pile up. We needed autoscaling based on Kafka consumer lag. ## Architecture Consumer pods expose lag metrics via Micrometer → Prometheus scrapes them → Prometheus Adapter maps metrics to K8s Custom Metrics API → HPA controller reads and scales. ## Key Configuration Set HPA to target average lag of 1000 messages per pod, with aggressive scale-up (30s window, +4 pods) and conservative scale-down (300s window, -1 pod) to prevent oscillation. ## Results Message backlog dropped from 50K peak to under 2K average. Pod count dynamically adjusts between 2-15, reducing monthly costs by ~35%.

← Back to News