深色模式
HPA 基于指标的横向弹性伸缩
摘要:本文面向生产 SRE / 平台工程师,解释 HorizontalPodAutoscaler(HPA)如何在控制平面内周期性地依据观测指标调整 Deployment/StatefulSet 的副本数。覆盖
autoscaling/v2的五种指标来源、缩放算法、behavior稳定窗口、metrics-server 与自定义/外部指标适配器的链路,以及 HPA 与 VPA 同资源冲突的边界。适用 Kubernetes v1.28+。
适用版本与前提
- Kubernetes:v1.28+(HPA
autoscaling/v2自 v1.23 起稳定;ContainerResource指标自 v1.30 起稳定,特性门已移除) - 工具:
kubectl、对目标命名空间具备get/describeHPA 与 Pod 的权限 - 前提:metrics-server 已部署(Resource 指标来自
metrics.k8s.io)
背景与问题
当流量上涨时,横向扩展(加副本)比垂直扩展(加大单 Pod 资源)更安全、更符合无状态服务的弹性模型。HPA 解决的核心问题是:让副本数自动跟随指标变化,而不是人工反复调整 replicas。
但 HPA 不是"银弹":
- 它只看
scaleTargetRef指向的工作负载的.spec.selector选中的 Pod,依赖指标流水线可用。 - 它对 DaemonSet 无效(DaemonSet 不可被缩放)。
- 它默认每 15 秒一次同步(
--horizontal-pod-autoscaler-sync-period,由 kube-controller-manager 控制),不是连续控制,存在采样延迟。
核心概念:指标来源
autoscaling/v2 支持以下 metrics[].type:
| 类型 | 含义 | 取值方式 | 典型用途 |
|---|---|---|---|
Resource | Pod 资源(CPU/内存)利用率 | Utilization(百分比)或 AverageValue(原始值) | CPU 使用率 60% |
ContainerResource | 单个容器的资源利用率(v1.30+ 稳定) | 同 Resource,需指定 container | 只按主容器扩缩,忽略 sidecar |
Pods | 每个 Pod 的自定义指标原始值 | AverageValue | 每 Pod 每秒包数 |
Object | 某 Kubernetes 对象(如 Ingress)的指标 | Value 或 AverageValue | Ingress 总 RPS |
External | 集群外指标(如云队列长度、Prometheus 查询) | Value 或 AverageValue | Kafka lag、SQS 深度 |
为什么需要 metrics-server
Resource 指标来自 metrics.k8s.io API,通常由 metrics-server 这个独立插件提供(它从 kubelet 拉取并聚合)。若 kubectl top pods 失败,HPA 的 Resource 类型指标也无法工作。自定义/外部指标则分别来自 custom.metrics.k8s.io 与 external.metrics.k8s.io,需要额外的 Metrics Adapter(如 Prometheus Adapter、KEDA 自带 adapter)。
架构与原理
算法要点(官方文档):
- 对每个指标计算期望副本数,取所有指标中的最大值作为本次期望副本。
- 若某容器未设对应
requests,其 CPU 利用率无定义,HPA 对该指标不采取任何动作。 - 容忍度默认
0.1(10%):当期望副本与当前副本差距在 10% 以内时不触发变更,避免抖动。 behavior中的stabilizationWindowSeconds会回看窗口内历史推荐值,缩放时使用窗口内更保守(缩放时取窗口内最值)的结果。
利用率陷阱
Resource 指标把所有容器的用量加总后与 Pod 的 requests 比较。若某 sidecar 容器耗 CPU 很高,但主容器很低,Pod 整体利用率可能"被平均",导致 HPA 不扩。此时应使用 ContainerResource 仅对主容器计算(autoscaling/v1 不支持,需 v2 + v1.30+)。
生产实践
- 永远为 Pod 设置
resources.requests:没有 requests,Resource 类型 HPA 完全失效。 - 用
behavior控制缩放速率:缩放过快会击穿下游,缩放过慢会丢失请求。生产上通常"快上慢下"。 - 内存指标慎用做缩容:内存不会像 CPU 那样瞬时回落,用内存利用率缩容可能导致 OOM 循环。
- 外部/自定义指标要配倒排与兜底:指标断了,HPA 会卡在
ScalingActive=False。
操作步骤
步骤 1:验证环境
bash
kubectl version --short
kubectl top pods -n default # 确认 metrics-server 可用
kubectl get apiservice v1beta1.metrics.k8s.io -o jsonpath='{.status.conditions[0].message}'1
2
3
2
3
步骤 2:声明 HPA(autoscaling/v2 + behavior)
yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-hpa
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
- type: External
external:
metric:
name: sqs_approximate_age_of_oldest_message
target:
type: AverageValue
averageValue: "30" # 每条消息平均处理延迟目标 30s
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 缩容前看 5 分钟历史,防抖动
policies:
- type: Percent
value: 50
periodSeconds: 60
- type: Pods
value: 4
periodSeconds: 60
selectPolicy: Min
scaleUp:
stabilizationWindowSeconds: 0 # 扩容立即执行,不延迟
policies:
- type: Pods
value: 4
periodSeconds: 30
selectPolicy: Max1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
步骤 3:应用与观察
bash
kubectl apply -f web-hpa.yaml
kubectl get hpa web-hpa -w
kubectl describe hpa web-hpa1
2
3
2
3
验证
bash
kubectl get hpa web-hpa
# 观察 conditions:AbleToScale / ScalingActive / ScalingLimited
kubectl describe hpa web-hpa | sed -n '/Conditions:/,/Events:/p'1
2
3
2
3
status.conditions 中的三类条件:
| 条件 | True 含义 | False 常见原因 |
|---|---|---|
AbleToScale | 可以缩放 | 距上次缩放太近 / 目标找不到 |
ScalingActive | 已算出副本数 | 取不到指标(metrics-server 或 adapter 故障) |
ScalingLimited | 副本在 min/max 区间内 | 已触顶 max 或触底 min |
回滚与清理
bash
kubectl delete -f web-hpa.yaml # 删除 HPA 不影响现有 Pod 副本数
# 若误扩,可临时调低 maxReplicas 或删除 HPA 后手动设置 replicas
kubectl scale deployment/web --replicas=21
2
3
2
3
生产危险
删除 HPA 不会把副本数回滚到任何"原始值"——副本数会保持在删除那一刻的实际值。依赖 HPA 缩放的 Deployment,在删除 HPA 后请显式 kubectl scale 到期望副本,否则可能残留异常高的副本数继续消耗资源。
故障排查
常见现象与处理:
<unknown>/60%:指标尚未就绪或 Pod 无 requests。先kubectl top pods验证。- 达到 max 仍不够:提高
maxReplicas,或排查是否单 Podrequests设得过高导致扩容缓慢。 - 反复抖动(flapping):加大
scaleDown.stabilizationWindowSeconds,或放宽容忍度。
HPA 与 VPA 的冲突
HPA 与 VPA 同时作用于**同一资源(如 CPU)**会相互打架:VPA 调高 requests → HPA 算出的利用率下降 → 缩副本;副本减少 → 单 Pod 用量上升 → VPA 再调高 requests…… 两者都"正确",但系统不收敛。
冲突边界
- 不能让 HPA 与 VPA 同时管理同一资源(如都管 CPU)。
- 可行组合:HPA 管 CPU、VPA 仅管内存(
controlledResources: [memory],updateMode: Auto);或 VPA 设为Off仅出推荐值,由人工/CI 应用。 - 详见 vpa.md 与 capacity.md 的冲突矩阵。
安全与合规
- HPA 本身不修改 Pod 规格,风险主要在"扩太多花太多钱、扩太少丢请求"。务必设
maxReplicas上限并配合 Cluster Autoscaler 的节点组上限(capacity.md)。 - 外部指标来源(Kafka/SQS/Prometheus)需鉴权,避免在
metadata中明文写密钥,使用 TriggerAuthentication / Secret 引用。
性能、容量与成本
- HPA 缩放延迟 = 指标采样延迟 + 15s 同步周期 + 新 Pod 启动时间。对亚分钟级尖峰,HPA 可能跟不上,需要预热副本或 KEDA 事件驱动(见 keda.md)。
- 副本上限应受节点组容量约束:若 HPA
maxReplicas=50但节点组最多容纳 20 个 Pod,多余副本将永久Pending,需 Cluster Autoscaler 协同(见 ca.md)。
替代方案与权衡
| 方案 | 适用 | 不适用 |
|---|---|---|
| HPA(Resource) | CPU/内存驱动的无状态服务 | 事件驱动、队列积压型负载 |
| HPA(External) | 队列长度、消息年龄 | 需要 scale-to-zero |
| KEDA | 事件/队列驱动、scale-to-zero | 纯资源利用率驱动 |
| VPA | 资源规格长期偏差 | 与 HPA 同资源冲突 |
FAQ
Q:HPA 能缩到 0 吗? A:标准 HPA 不能缩到 0(minReplicas 最低为 1,部分版本支持 0 但需特性配合)。需要 scale-to-zero 请用 KEDA(keda.md)。
Q:多个指标时怎么决策? A:对每个指标分别算期望副本,取最大值。ScalingLimited 条件会告诉你是否触顶/触底。
参考资料
- Kubernetes 官方文档 - Horizontal Pod Autoscale,访问日期:2026-10-08。
- Kubernetes 官方文档 - Horizontal Pod Autoscaler Walkthrough,访问日期:2026-10-08。
- Kubernetes 官方文档 - Support for metrics APIs,访问日期:2026-10-08(用于确认
autoscaling/v2与指标 API 稳定性)。