HPA自动扩缩容
- k8s
- 9小时前
- 0评论
官方文档:https://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale/
1.简介
HPA(Horizontal Pod Autoscaler,Pod水平自动伸缩),根据平均 CPU 利用率、平均内存利用率或你指定的任何其他自定义指标自动调整 Deployment 、ReplicaSet 或 StatefulSet 或其他类似资源,实现部署的自动扩展和缩减,让部署的规模接近于实际服务的负载。HPA不适用于无法缩放的对象,例如DaemonSet。
先决条件
- 必须安装 metrics-server(在metrics-server中添加--kubelet-insecure-tls参数跳过证书校验)
- 必须定义 Request参数
- 开启 API Aggregator参数
配置Aggregator参数:
cat /etc/kubernetes/manifests/kube-apiserver.yaml
# 添加这行
--enable-aggregator-routing=true
HPA 到底基于什么指标扩缩容?
很多人一提到 HPA,第一反应就是“CPU 使用率”。但在 Kubernetes 1.23+ 的 autoscaling/v2 API 中,HPA 支持的指标类型远不止 CPU 这一种。
实际生产中,一般使用这四类指标:
- Resource metrics——CPU核 和 内存利用率指标。
- Pod metrics——例如网络利用率和流量。
- Object metrics——特定对象的指标,比如Ingress, 可以按每秒使用请求数来扩展容器。
- Custom metrics——自定义监控,比如通过定义服务响应时间,当响应时间达到一定指标时自动扩容。
Resource 指标:最基础,但别只依赖它
Resource 指标是 HPA 最经典的指标类型,通过 Metrics Server 从 kubelet 采集 CPU 和内存数据。
但这里有个大坑:HPA 计算 CPU 利用率是基于 Request 而不是 Limit。如果 Pod 里的某个容器没有设置 Request,HPA 就无法计算利用率,会直接报错 failed to get cpu utilization: missing request for cpu。很多新手只给业务容器设了 Request,忘了给 sidecar 设,结果 HPA 完全不动。
不过要注意,“没有 Request 就完全不工作”这个说法并不完整。准确地说:如果容器没有设置 Resource Request,HPA 无法基于 Utilization(利用率)类型进行扩缩容,但如果使用 Absolute(绝对用量)类型(如 averageValue),则仍然可以工作。也就是说,你可以绕过利用率,直接基于绝对 CPU 用量来扩缩容。
而且,未设置 Request 的容器还会“稀释”整个 Pod 的利用率计算——在混合计算中,这些容器会拉低整体平均值,可能导致基于 Resource 指标的 HPA 扩容不灵敏甚至完全不扩容。这正是 ContainerResource 指标存在的核心价值之一。
另外,CPU 和内存利用率只能反映容器层面的“健康状况”,无法反映真实的业务负载。比如你的服务是 I/O 密集型的,CPU 利用率低但 QPS 暴涨,这时候用 CPU HPA 就完全是“瞎子”。
HPA 工作原理:控制循环、算法与核心参数
控制循环:15 秒是周期,不是响应时间
Kubernetes 将 HPA 实现为一个间歇运行的控制回路,不是一个连续的过程。
控制周期由 kube-controller-manager 的 --horizontal-pod-autoscaler-sync-period 参数控制,默认 15 秒。该参数可以在 kube-controller-manager 的启动配置中调整。但请注意:15 秒是控制循环的检查间隔,不是端到端的响应时间——指标采集、计算、API 调用都有额外延迟。实际端到端的扩缩容通常在 30 秒到几分钟不等,具体取决于 Metrics Server 的采集周期、API 响应速度以及控制器的调度情况。
在每个周期内,HPA 控制器会:
- 根据
scaleTargetRef找到目标资源(如 Deployment) - 通过
.spec.selector选择 Pod - 从对应的 Metrics API 获取指标数据
- 计算期望副本数
- 通过 Scale 子资源调整副本数
核心算法:期望副本数怎么算?
HPA 计算期望副本数的公式是:
期望副本数 = ceil(当前副本数 × 当前指标值 / 期望指标值)
举个例子:当前有 10 个 Pod,CPU 平均利用率是 50%,期望利用率是 80%:
期望副本数 = ceil(10 × 50% / 80%) = ceil(6.25) = 7
所以 HPA 会把副本数从 10 缩到 7。
如果配置了多个指标,HPA 会分别计算每个指标的期望副本数,取最大值作为最终结果。但这里有一个容易被忽略的细节:当某个指标无法获取时
- 扩容时:只要还有其他指标推荐扩容,HPA 仍然可以扩容
- 缩容时:如果某个指标无法获取且剩余指标推荐缩容,HPA 会跳过缩容
也就是说:指标获取失败会阻止缩容,但不会阻止扩容。这个设计是为了防止指标采集抖动导致误缩容,但排障时如果发现 HPA“只扩不缩”,记得检查一下是不是有指标挂了。
但这里要特别提醒:对于 ContainerResource 类型的指标,情况有所不同。在较老版本中,ContainerResource 指标缺失会导致 HPA 完全无法计算任何决策。这个行为在近期版本中已经被修复,使其与 Resource 类型指标的行为保持一致。如果你使用的是 1.26 或更早版本,需要特别注意这一点。
另外还有一个关键细节:HPA 在计算之前还会考虑 Pod 是否就绪(Ready)或正在终止(Terminating) ——未就绪或正在终止的 Pod 会被排除在指标计算之外。而且,Pod 变成 Ready 后,还需要经过 --horizontal-pod-autoscaler-initial-readiness-delay(默认 30 秒)才会被纳入指标计算。这个延迟是为了避免 Pod 刚启动时指标不稳定就触发扩缩容。如果 Ready 的 Pod 数量太少,HPA 也可能不会执行扩缩容。这点排障时也值得留意。
容忍度(Tolerance):那个让你困惑的 10%
HPA 中存在一个容忍度(Tolerance) ,默认是 0.1(即 10%)。
无论是否配置了 tolerance 字段,HPA 始终存在容忍度机制,默认值为 10% 。它的作用是:当 当前指标值 / 期望指标值 的比例在 0.9 到 1.1 之间时,HPA 不会触发任何扩缩容动作。
这也就是为什么你配置了 CPU 阈值 50%,实际到了 55% 还没扩容——因为 55/50 = 1.1,刚好在容忍边界上。
这里要特别留意版本差异:在较旧的 Kubernetes 版本中,这个 10% 的容忍度是集群级别的,只能通过 kube-controller-manager 的 --horizontal-pod-autoscaler-tolerance 全局参数修改。
从 Kubernetes 1.33 开始,HPAConfigurableTolerance 特性门控以 Alpha 状态引入。你需要显式启用该特性门控才能使用。从 Kubernetes 1.35 开始,该特性门控被提升为 Beta 并默认开启。启用后,你可以在每个 HPA 上单独配置容忍度,而且扩容和缩容可以设不同的值:
behavior:
scaleDown:
tolerance: 0.05 # 缩容容忍度 5%
scaleUp:
tolerance: 0 # 扩容容忍度 0%,更灵敏
Behavior 配置:精细控制扩缩容节奏
从 Kubernetes 1.23 开始,behavior 字段正式 GA。你可以通过它精细控制扩容和缩容的策略。
下面是一个完整示例:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-app
spec:
minReplicas: 2
maxReplicas: 50
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: 1000 # 每个 Pod 平均 1000 QPS
behavior:
scaleUp:
stabilizationWindowSeconds: 60 # 扩容观察 60 秒
policies:
- type: Percent
value: 100
periodSeconds: 60 # 每 60 秒最多扩容 100%
- type: Pods
value: 4
periodSeconds: 60 # 每 60 秒最多扩容 4 个 Pod
selectPolicy: Max # 取两种策略中扩容数量更大的
scaleDown:
stabilizationWindowSeconds: 300 # 缩容观察 5 分钟
policies:
- type: Percent
value: 10
periodSeconds: 60 # 每 60 秒最多缩容 10%
selectPolicy: Min
这个配置的关键点:
- 扩容时最多每 60 秒翻一倍或加 4 个 Pod
- 缩容时有 5 分钟稳定窗口,且每 60 秒最多缩 10%
- 适合有流量突发的关键业务:快速扩容应对高峰,缓慢缩容防止下一个高峰
注意:如果你打算把上面这个 YAML 直接用到 1.33 或 1.34 集群中,注意 tolerance 字段需要显式开启 HPAConfigurableTolerance 特性门控才会生效;1.35+ 默认开启。如果特性门控未开启,tolerance 字段会被静默忽略,不会报错——这个坑非常隐蔽。
版本演进:别再用 v1 和 v2beta2 了
| 版本 | 状态 | 说明 |
|---|---|---|
autoscaling/v1 |
已弃用 | 只支持单个 CPU 指标 |
autoscaling/v2beta2 |
Kubernetes 1.26 中正式移除 | 1.25 是最后一个支持版本 |
autoscaling/v2 |
GA(Kubernetes 1.23+) | 当前推荐使用 |
简单案例:
v1
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: hpa_name
spec:
maxReplicas: 5
minReplicas: 2
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
targetCPUUtilizationPercentage: 60
v2
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: hpa_name
spec :
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
targetAverageUtilization: 80
resource:
name: memory
targetAverageValue: 800Mi
v2
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: hpa-nginx
spec:
maxReplicas: 10 # 最大扩容到10个节点(pod)
minReplicas: 1 # 最小扩容1个节点(pod)
metrics:
- resource:
name: cpu
target:
averageUtilization: 50 # CPU 平局资源使用率达到50%就开始扩容,低于50%就是缩容
# 设置内存
# AverageValue:50
type: Utilization
type: Resource
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: hpa-nginx
---
apiVersion: v1
kind: Service
metadata:
name: hpa-test
spec:
type: NodePort
ports:
- name: "http"
port: 80
targetPort: 80
nodePort: 30080
selector:
service: hpa-test
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hpa-test
spec:
replicas: 1
selector:
matchLabels:
service: hpa-test
template:
metadata:
labels:
service: hpa-test
spec:
containers:
- name: hpa-test
image: nginx:latest
resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 200m
memory: 200Mi
主要参数解释如下:
- scaleTargetRef:目标作用对象,可以是Deployment、ReplicationController或ReplicaSet。
- minReplicas和maxReplicas:Pod副本数量的最小值和最大值,系统将在这个范围内进行自动扩缩容操作,并维持每个Pod的内存使用率为50%,这个值就是上面设置的阈值averageUtilization。
- metrics:目标指标值。在metrics中通过参数type定义指标的类型;通过参数target定义相应的指标目标值,系统将在指标数据达到目标值时(考虑容忍度的区间,见前面算法部分的说明)触发扩缩容操作。
- 对于CPU使用率,在target参数中设置averageUtilization定义目标平均CPU使用率。
- 对于内存资源,在target参数中设置AverageValue定义目标平均内存使用值。
压测:
ab -n 10000 -c 800 http://192.168.1.1:30080/
最佳实践
- 先定好 Resource Request:HPA 计算 CPU 利用率是基于 Request 而不是 Limit。Request 设得太大,利用率永远上不去;设得太小,Pod 容易被杀死。我推荐设置 Request = Limit 来保证资源独占。每个容器都要设,漏了一个 HPA 就可能不工作。
- 配合 Cluster Autoscaler:HPA 只管 Pod 扩缩,节点资源不够了还得靠 CA。两者要协同工作。
- 别一开始就把规则搞复杂:先选一两个高相关指标,经过压测和灰度验证后再推广。
- 配置 PodDisruptionBudget:防止 HPA 在节点维护时瞬间杀死所有 Pod。
- 关注冷启动延迟:镜像太大、启动脚本太复杂,扩容上去也扛不住流量。
要点总结
- HPA v2(autoscaling/v2)是当前标准,支持 Resource、ContainerResource、Pods、Object、External 五种指标类型
- 核心算法:期望副本数 = ceil(当前副本数 × 当前指标 / 期望指标),多指标时取最大值
- HPA 始终存在容忍度机制,默认 10%(集群级别)——1.33 引入可配置容忍度(Alpha,需显式启用),1.35 起默认开启
- 缩容默认 5 分钟稳定窗口,扩容默认立即执行——这是为了防止抖动
