HPA 自动扩缩容
固定副本数的小服务跑不稳:负载高峰来时要么响应变慢,要么直接被压垮。HPA(HorizontalPodAutoscaler)持续读取指标,在允许的副本范围内自动调整 Pod 数量。本章讲透它的原理、配置与防抖动。
本篇目标:理解弹性伸缩体系(HPA vs VPA vs Cluster Autoscaler vs KEDA),掌握 HPA 的指标计算算法,能配置基于 CPU/内存的 HPA,并用 behavior 参数防止抖动。
1. K8s 弹性伸缩体系概述
| 组件 | 调整什么 | 解决什么问题 |
|---|---|---|
| HPA(本章) | 副本数量 | 负载升高 → 更多 Pod |
| VPA | 每个副本的 requests/limits | 资源规格不合适 → 自动调整规格 |
| Cluster Autoscaler | 节点数量 | 节点不足 → 自动加节点 |
| KEDA | 基于业务事件扩缩 | 消息队列积压、QPS 等自定义指标 |
负载升高 → HPA 增加 Pod → 节点容量不足 → Cluster Autoscaler 增加节点
资源规格不合理 → VPA 调整 requests/limits(通常需要重启 Pod)
业务事件驱动 → KEDA 监听队列深度等自定义指标触发扩容
2. HPA 工作原理与指标计算算法
2.1 前置条件:Metrics Server
CPU/内存 HPA 依赖 Metrics Server 提供的 metrics.k8s.io API:
# APIService 应为 True
kubectl get apiservice v1beta1.metrics.k8s.io
kubectl top nodes
kubectl top pods -A
只有 kubectl top 能持续返回数据后,才继续创建基于 CPU/内存的 HPA。
2.2 指标计算算法
HPA Controller 按同步周期读取指标并计算期望副本数。CPU 利用率场景可近似理解为:
期望副本数 = ceil(当前副本数 × 当前平均利用率 / 目标利用率)
例如当前有 3 个可用 Pod、CPU 平均利用率为 140%、目标为 70%,HPA 会建议扩到 ceil(3 × 140 / 70) = 6 个副本。实际结果还会受 minReplicas/maxReplicas、容忍区间、未就绪 Pod、冷却窗口和 behavior 限速影响。
2.3 关键概念
Utilization的分母是容器的resources.requests.cpu或resources.requests.memory,不是节点总资源,也不是 limits;- 没有 request 时,HPA 无法算出使用率百分比,常见事件是
missing request for cpu; - 多个指标同时配置时,HPA 分别计算每项指标的建议副本数,并采用其中的最大值;
- 指标获取失败时,控制器会保守处理,不能把"指标失效"当作缩容信号。
2.4 指标类型
| 指标类型 | 典型目标值 | 指标 API | 常见场景 |
|---|---|---|---|
Resource | CPU/内存平均利用率或平均值 | metrics.k8s.io | 通用无状态服务 |
ContainerResource | 指定容器的 CPU/内存 | metrics.k8s.io | 多容器 Pod 按主容器扩缩 |
Pods | 每个 Pod 的平均 QPS | custom.metrics.k8s.io | 每副本负载可独立度量 |
Object | Ingress QPS、队列深度 | custom.metrics.k8s.io | 围绕某个对象扩缩 |
External | 云队列积压、SaaS 指标 | external.metrics.k8s.io | 指标不属于 Kubernetes 对象 |
3. 基于 CPU / 内存指标的 HPA 配置实战
3.1 最小配置
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api
namespace: learning
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
kubectl apply -f hpa.yaml
kubectl get hpa api -n learning --watch
kubectl describe hpa api -n learning
3.2 完整实战案例
下面的案例部署一个 CPU 压测服务,平均 CPU 利用率达到 50% 时自动扩容。requests.cpu: 200m 是计算利用率的基线,不能删除:
# hpa-cpu-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hpa-cpu-demo
namespace: learning
spec:
replicas: 1
selector:
matchLabels:
app: hpa-cpu-demo
template:
metadata:
labels:
app: hpa-cpu-demo
spec:
containers:
- name: app
image: registry.k8s.io/hpa-example
ports:
- containerPort: 80
resources:
requests:
cpu: 200m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
name: hpa-cpu-demo
namespace: learning
spec:
selector:
app: hpa-cpu-demo
ports:
- port: 80
targetPort: 80
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: hpa-cpu-demo
namespace: learning
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: hpa-cpu-demo
minReplicas: 1
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
kubectl apply -f hpa-cpu-demo.yaml
kubectl get deployment,hpa -n learning
# 单独终端持续观察
kubectl get hpa hpa-cpu-demo -n learning --watch
# 启动负载发生器
kubectl run hpa-cpu-loadgen -n learning --rm -it --restart=Never --image=busybox:1.36 -- /bin/sh
# 在负载发生器内:
while true; do wget -q -O- 'http://hpa-cpu-demo/consume-cpu/consume-cpu?millicores=400&durationSec=30' >/dev/null; done
# 对照实际使用量
kubectl top pods -n learning -l app=hpa-cpu-demo
kubectl describe hpa hpa-cpu-demo -n learning
3.3 诊断 HPA
kubectl describe hpa api -n learning
| Condition | 含义 |
|---|---|
ScalingActive=False | 指标不可用或缺少 request |
ScalingLimited=True | 计算结果碰到 min/max 边界 |
AbleToScale=False | 目标工作负载的 scale 子资源有问题 |
若 TARGETS 显示 <unknown>,优先检查 Metrics Server、Pod 是否 Ready,以及 Deployment 是否保留了 CPU request。
4. 进阶 HPA 策略:behavior 防抖动与 KEDA 联动
4.1 默认行为的抖动问题
默认的 HPA 扩缩容可能过于激进——短暂 CPU 尖峰就扩容,负载回落就立即缩容,造成"抖动"(副本数频繁上下跳)。
4.2 behavior 参数控制节奏
spec:
behavior:
scaleDown:
# 缩容前等待 5 分钟,确认负载确实持续低位
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10 # 每次最多缩掉当前副本数的 10%
periodSeconds: 60
scaleUp:
# 扩容无需等待窗口,快速响应
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100 # 每次最多翻倍
periodSeconds: 15
- type: Pods
value: 4 # 或每次最多加 4 个 Pod
periodSeconds: 15
selectPolicy: Max # 取两个策略中的较大值
| 参数 | 作用 |
|---|---|
stabilizationWindowSeconds | 指标波动时延迟扩/缩,防止抖动 |
policies | 限定每次扩/缩的速度上限 |
selectPolicy | 多策略取 Max(激进)或 Min(保守) |
4.3 HPA 的边界与前提
- HPA 只改副本数,节点容量不足时 Pod 仍然 Pending——需配合 Cluster Autoscaler 或容量预留;
- 业务指标 HPA(QPS、延迟)需要 Prometheus Adapter 等 custom metrics adapter;
- HPA 与 GitOps、Helm 或人工
kubectl scale不能同时持续管理同一个 Deployment 的spec.replicas,否则会互相覆盖; - HPA 不适合有状态副本、无法安全并发运行的任务或启动耗时远超扩容收益的服务。
4.4 KEDA:事件驱动扩缩
KEDA(Kubernetes Event-driven Autoscaling)支持按业务事件扩缩,而不只依赖 CPU/内存:
| 触发源 | 示例 |
|---|---|
| 消息队列 | Kafka 分区积压、RabbitMQ 队列长度 |
| 数据库 | PostgreSQL 活跃连接数 |
| HTTP | Ingress 请求速率 |
| 自定义 | Prometheus 中任意指标 |
KEDA 本质上是一个"指标转换器 + HPA 控制器":它把外部事件转成 custom/external metrics,再由内置 HPA 执行扩缩。适合"队列积压时快速扩容消费者"这类 CPU 指标感知不到的场景。
5. 练习
练习 A
- 确认 Metrics Server 已部署:
kubectl get apiservice v1beta1.metrics.k8s.io与kubectl top pods - 基于第 3.2 节创建 Deployment + Service + HPA,观察
TARGETS从<unknown>变为百分比 - 用负载发生器持续请求,观察副本数上升但不超过
maxReplicas
练习 B
- 停止压测,观察缩容是否在
stabilizationWindowSeconds之后才发生 - 给 HPA 配置
behavior.scaleDown(10%/60s),对比默认行为的缩容速度 - 用
kubectl get hpa -w与kubectl describe hpa观察每次扩缩容的原因记录
下一篇:RBAC 权限管理。