# Kubernetes HPA 基于 Prometheus 自定义指标的自动扩缩容实战指南
## 背景介绍
在现代云原生架构中,应用的自动扩缩容已经成为保障服务稳定性和资源利用率的核心能力。Kubernetes 提供了 Horizontal Pod Autoscaler(HPA)来实现 Pod 的水平扩缩容。默认情况下,HPA 支持基于 CPU 和内存利用率进行自动扩缩容,这在很多场景下已经足够使用。
然而,在实际生产环境中,我们经常遇到这样的需求:当队列积压消息数量超过阈值时自动扩容消费者;当活跃用户连接数飙升时扩展 Web 服务;当订单处理延迟增加时扩展处理能力。这些场景都无法单纯依靠 CPU 或内存指标来触发扩缩容,需要基于业务自定义指标来实现。
Prometheus 是 Kubernetes 生态中最流行的监控系统。通过 Prometheus Adapter,可以将 Prometheus 中的自定义指标接入 HPA,实现基于业务指标的智能扩缩容。下面详细介绍具体配置方法。
## 问题描述
假设我们有一个订单处理服务,负责异步处理用户提交的订单。该服务使用消息队列接收订单任务,当队列积压订单数量过多时,处理延迟会明显增加,影响用户体验。
当前的架构如下:
– 订单服务部署为 Deployment,包含 3 个 Pod 副本
– 使用 Redis List 作为消息队列存储待处理订单
– Prometheus 监控收集了队列长度指标 `order_queue_length`
现在面临的问题是:即使队列积压了几千条订单,只要 CPU 和内存还在可用范围内,HPA 不会触发扩容。这导致订单处理延迟从正常的几百毫秒飙升至几分钟,严重影响用户体验。
我们需要实现:当队列长度超过 100 时自动扩容订单处理服务,确保订单能够被及时处理。
## 详细步骤
### 第一步:部署测试应用和 Redis
我们部署一个模拟的订单处理服务,使用 Redis 存储队列数据。
创建 order-consumer 的 Deployment 和 Service:
“`yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-consumer
labels:
app: order-consumer
spec:
replicas: 2
selector:
matchLabels:
app: order-consumer
template:
metadata:
labels:
app: order-consumer
spec:
containers:
– name: consumer
image: nginx:alpine
ports:
– containerPort: 80
env:
– name: REDIS_HOST
value: “redis-master”
– name: REDIS_PORT
value: “6379”
resources:
requests:
cpu: “100m”
memory: “128Mi”
limits:
cpu: “200m”
memory: “256Mi”
—
apiVersion: v1
kind: Service
metadata:
name: order-consumer
spec:
selector:
app: order-consumer
ports:
– port: 80
targetPort: 80
“`
同时启动一个 Redis 作为队列存储:
“`yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
– name: redis
image: redis:7-alpine
ports:
– containerPort: 6379
—
apiVersion: v1
kind: Service
metadata:
name: redis-master
spec:
selector:
app: redis
ports:
– port: 6379
targetPort: 6379
“`
应用这些配置:
“`bash
kubectl apply -f order-consumer.yaml
kubectl apply -f redis.yaml
“`
### 第二步:部署 Prometheus 和 Prometheus Adapter
如果集群中还没有 Prometheus,用 Helm 部署 kube-prometheus-stack:
“`yaml
apiVersion: v1
kind: Namespace
metadata:
name: monitoring
—
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: prometheus
namespace: monitoring
selector:
matchLabels:
prometheus: k8s
minAvailable: 1
“`
通过 Helm 安装 kube-prometheus-stack:
“`bash
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack \
–namespace monitoring \
–create-namespace \
–set prometheus.prometheusSpec.retention=30d \
–set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=50Gi
“`
等待 Prometheus Pod 就绪:
“`bash
kubectl wait –for=condition=ready pod/prometheus-k8s-0 -n monitoring –timeout=300s
“`
### 第三步:部署自定义指标采集服务
部署 Prometheus Adapter,将 Prometheus 指标转换为 HPA 可用的 API 格式:
创建 prometheus-adapter 的配置和部署文件:
“`yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-adapter-config
namespace: monitoring
data:
config.yaml: |
rules:
– seriesQuery: ‘order_queue_length{namespace!=””,pod!=””}’
resources:
overrides:
namespace: {resource: “namespace”}
pod: {resource: “pod”}
name:
matches: “^(.*)$”
as: “queue_length”
metricsQuery: ‘avg(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)’
—
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus-adapter
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
name: prometheus-adapter
template:
metadata:
labels:
name: prometheus-adapter
spec:
containers:
– name: prometheus-adapter
image: directxman12/k8s-prometheus-adapter:v0.12.0
args:
– –config=/etc/adapter/config.yaml
– –log-level=debug
– –metrics-relist-interval=1m
– –rate-interval=5m
– –v=4
volumeMounts:
– mountPath: /etc/adapter
name: config
ports:
– containerPort: 6443
resources:
requests:
cpu: 100m
memory: 128Mi
volumes:
– name: config
configMap:
name: prometheus-adapter-config
—
apiVersion: v1
kind: Service
metadata:
name: prometheus-adapter
namespace: monitoring
spec:
ports:
– port: 443
targetPort: 6443
selector:
name: prometheus-adapter
“`
应用配置:
“`bash
kubectl apply -f prometheus-adapter.yaml
“`
### 第四步:配置 HPA 使用自定义指标
现在我们可以创建使用自定义指标的 HPA。当队列长度超过 100 时,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: 10
metrics:
– type: Pods
pods:
metric:
name: queue_length
target:
type: AverageValue
averageValue: “100”
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
– type: Percent
value: 100
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
– type: Percent
value: 10
periodSeconds: 60
“`
应用 HPA 配置:
“`bash
kubectl apply -f hpa-custom-metric.yaml
“`
### 第五步:验证配置
检查 HPA 状态:
“`bash
kubectl get hpa order-consumer-hpa
“`
输出应该显示:
“`
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
order-consumer-hpa Deployment/order-consumer
“`
TARGETS 显示 `
### 第六步:生成测试数据并验证扩容
创建一个模拟指标导出器来生成测试数据:
“`yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: metrics-exporter
spec:
replicas: 1
selector:
matchLabels:
app: metrics-exporter
template:
metadata:
labels:
app: metrics-exporter
spec:
containers:
– name: exporter
image: busybox:1.36
command:
– /bin/sh
– -c
– |
while true; do
# 模拟队列长度在 50-500 之间波动
QUEUE_LEN=$((RANDOM % 450 + 50))
echo “order_queue_length $QUEUE_LEN” | tee /proc/1/fd/1
sleep 15
done
ports:
– containerPort: 8080
“`
应用并配置 ServiceMonitor 让 Prometheus 采集这个指标:
“`yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: order-queue-monitor
namespace: monitoring
spec:
selector:
matchLabels:
app: metrics-exporter
endpoints:
– port: metrics
interval: 15s
—
apiVersion: v1
kind: Service
metadata:
name: metrics-exporter
labels:
app: metrics-exporter
spec:
ports:
– name: metrics
port: 8080
targetPort: 8080
selector:
app: metrics-exporter
“`
应用这些配置:
“`bash
kubectl apply -f metrics-exporter.yaml
kubectl apply -f service-monitor.yaml
“`
等待几秒钟让 Prometheus 采集指标,然后再次检查 HPA:
“`bash
kubectl get hpa order-consumer-hpa -w
“`
应该能看到类似输出:
“`
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
order-consumer-hpa Deployment/order-consumer 250/100 2 10 4 10m
“`
注意到 TARGETS 显示 250/100,REPLICAS 已经从 2 变成了 4。这是因为当前队列长度(模拟值 250)超过了目标平均值(100),HPA 触发扩容。
### 第七步:测试缩容
降低模拟的队列长度值,验证 HPA 能够触发缩容。修改 metrics-exporter 的脚本,将队列长度降低:
“`bash
kubectl exec -it deploy/metrics-exporter — /bin/sh
# 在容器内修改生成逻辑,让队列长度降到 50 以下
“`
或者直接修改 Deployment 的环境变量来降低生成的队列长度。当队列长度降到 100 以下后,观察 HPA 的变化:
“`bash
kubectl get hpa order-consumer-hpa -w
“`
经过 5 分钟的稳定窗口期(scaleDown stabilizationWindowSeconds),Pod 数量会逐渐减少,最终稳定在最小副本数。
## 运行结果
通过上述步骤,我们成功实现了基于 Prometheus 自定义指标的 HPA 自动扩缩容。验证结果如下:
1. **初始状态**:部署完成后,order-consumer 有 2 个 Pod 副本
2. **触发扩容**:当队列长度超过 100 时(我们模拟了 250),HPA 在 60 秒内将 Pod 扩展到 4 个
3. **继续扩容**:如果队列继续增长,HPA 会按照配置的策略继续扩容,最大可达 10 个副本
4. **触发缩容**:队列长度降低到 50 后,经过 5 分钟的稳定窗口期,Pod 数量逐渐回缩到 2 个
通过 `kubectl describe hpa order-consumer-hpa` 可以查看详细的扩容事件:
“`
Events:
Type Reason Age From Message
—- —— —- —- ——-
Normal SuccessfulRescale 10m horizontal-pod-autoscaler scaling Pods from 2 to 4
Normal SuccessfulRescale 5m horizontal-pod-autoscaler scaling Pods from 4 to 3
Normal SuccessfulRescale 1m horizontal-pod-autoscaler scaling Pods from 3 to 2
“`
## 总结
本文介绍了在 Kubernetes 中配置 HPA 使用 Prometheus 自定义指标实现自动扩缩容的方法。
几个关键点:
1. **HPA 指标类型**:HPA 支持 Resource、Pod、Object 和 External 指标。自定义指标通过 Pod 或 External 类型接入。
2. **Prometheus Adapter**:连接 Prometheus 和 HPA API 的桥梁,将 Prometheus 查询结果转换为 HPA 可用的指标格式。
3. **配置 rules**:正确设置 seriesQuery 匹配指标名称,确保 resources 映射正确。
4. **behavior 字段**:控制扩容和缩容行为,包括稳定窗口时间和扩容速率限制。
5. **调试**:用 `kubectl get hpa -w` 和 `kubectl describe hpa` 查看状态,查看 Adapter 日志排查问题。
这种模式适合消息队列处理、连接池管理、任务调度等场景,能够基于业务负载精准扩缩容。