K8S 之 Horizontal Pod Autoscaler(HPA)

摘要

Horizontal Pod Autoscaler(HPA) 简介

  • Horizontal Pod Autoscaler(缩写为 hpa)基于资源 CPU 利用率自动调整 deployment、replication controller 或者 replica 中 pod 的数量,这有助于您的应用程序进行扩展以满足增长的需求,或在不需要资源时进行缩减,从而释放出节点用于其他应用程序。当您设置目标 CPU 利用率百分比时,HPA 扩展或缩减应用程序来尝试满足该目标。

  • Kubernetes 本身已经包含了 HPA 的 controller,所以不需要额外的安装或部署。

  • HPA 需要获取 metrics 信息,metrics 信息需要从 Metrics Server 中获取,所以需要先安装 Metrics Server。

  • HPA 会周期性(默认15秒)查询目标资源的使用情况,然后和 HPA 中定义的值做比较,并根据比较结果相应的调整 pod 数量。

  • 创建pod时,必须为其设定cpu资源,用于与目标值进行比较,目前最新的v2版本的HPA除了支持CPU的对比,还可以设定其它指标,具体参考HorizontalPodAutoscaler 演练中的“基于多项度量指标和自定义度量指标自动扩缩”。

  • 博主之前写过一篇在aws的eks中使用hpa的文章 AWS-EKS-17--Horizontal Pod Autoscaler(HPA),和在自己搭建 k8s集群 中使用 hpa 基本一致,所以本文不再对重复内容进行赘述,建议读者先阅读 AWS-EKS-17--Horizontal Pod Autoscaler(HPA)

安装 Metrics Server

  • Kubernetes 的 HPA 是依赖 Metrics API 的,默认并不内置。需要通过 metrics-server 来收集和提供节点/Pod 的资源用量数据(CPU、内存等)。

  • 安装 metrics-server

1
2
3
4
5
6
7
8
9
10
11
$ curl -L -o metrics-server.yaml https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
$ kubectl apply -f metrics-server.yaml
serviceaccount/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created
service/metrics-server created
deployment.apps/metrics-server created
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created
  • 部署后发现pod没有运行成功

1
2
3
4
5
6
7
8
9
10
11
12
$  k get all -n kube-system -l k8s-app=metrics-server
NAME READY STATUS RESTARTS AGE
pod/metrics-server-867d48dc9c-fgdjq 0/1 Running 0 16s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/metrics-server ClusterIP 10.96.91.221 <none> 443/TCP 16s

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/metrics-server 0/1 1 0 16s

NAME DESIRED CURRENT READY AGE
replicaset.apps/metrics-server-867d48dc9c 1 1 0 16s
  • 检查日志

1
2
3
4
5
$ k logs deployment.apps/metrics-server -n kube-system
## 输出类似于
E0703 23:02:38.120831 1 scraper.go:149] "Failed to scrape node" err="Get \"https://10.211.55.15:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 10.211.55.15 because it doesn't contain any IP SANs" node="k8s-worker1"
E0703 23:02:38.127774 1 scraper.go:149] "Failed to scrape node" err="Get \"https://10.211.55.16:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 10.211.55.16 because it doesn't contain any IP SANs" node="k8s-worker2"
E0703 23:02:38.140583 1 scraper.go:149] "Failed to scrape node" err="Get \"https://10.211.55.11:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 10.211.55.11 because it doesn't contain any IP SANs" node="k8s-master"
  • 意思是 metrics-server 在通过 https://:10250 请求 kubelet 的时候,发现 kubelet 的证书不包含该 IP 的 Subject Alternative Name (SAN),因此 TLS 校验失败。

  • 解决方案: 添加 --kubelet-insecure-tls 参数,跳过 kubelet 的 TLS 证书校验(metrics-server 本身权限有限,风险可控,kubelet 自签证书维护成本高)

1
2
3
4
5
6
7
8
9
10
11
kubectl edit deployment metrics-server -n kube-system
## 在 args 中添加 --kubelet-insecure-tls
spec:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=10250
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls # 添加此行
  • wq保存后,等一会再次查看所有资源均已正常

  • 查看所有 node 的资源

1
2
3
4
5
$ k top nodes
NAME CPU(cores) CPU(%) MEMORY(bytes) MEMORY(%)
k8s-master 216m 10% 1712Mi 44%
k8s-worker1 79m 3% 1025Mi 26%
k8s-worker2 82m 4% 982Mi 25%
字段 说明
NAME 节点名称,Kubernetes 集群的节点名字。
CPU(cores) 当前节点 CPU 的瞬时使用量,单位是核心数(Core),比如 216m = 0.216 核心。m = millicores,1000m = 1核。
CPU(%) 当前 CPU 使用率,相对于该节点 CPU 总核数的百分比。比如 10% 表示节点 CPU 的 10% 被使用。
MEMORY(bytes) 节点当前使用的内存,单位是字节(例如 1712Mi = 1712 Mebibytes ≈ 1.7 GB)。
MEMORY(%) 节点内存使用率,占总内存的百分比。比如 44% 表示该节点内存使用了 44%。
  • 查看所有 pod 的资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ k top pods -A
NAMESPACE NAME CPU(cores) MEMORY(bytes)
default nginx-5869d7778c-5682r 0m 9Mi
default nginx-5869d7778c-thw8f 0m 4Mi
kube-system calico-kube-controllers-7bfdc5b57c-q5xwp 6m 35Mi
kube-system calico-node-7pbbq 42m 281Mi
kube-system calico-node-v4hzr 47m 260Mi
kube-system calico-node-w47qq 40m 263Mi
kube-system coredns-674b8bbfcf-2tvld 2m 23Mi
kube-system coredns-674b8bbfcf-h6kx7 2m 22Mi
kube-system etcd-k8s-master 27m 85Mi
kube-system kube-apiserver-k8s-master 49m 404Mi
kube-system kube-controller-manager-k8s-master 28m 100Mi
kube-system kube-proxy-nkbns 1m 41Mi
kube-system kube-proxy-plqw8 1m 25Mi
kube-system kube-proxy-sbgh6 1m 34Mi
kube-system kube-scheduler-k8s-master 8m 38Mi
kube-system metrics-server-56fb9549f4-rtt7n 4m 21Mi

创建 HPA

  • 前面的文章中多次使用 nginx 镜像创建了 deployment,但是都没有配置过资源,为了测试 hpa,需要为其指定资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 直接修改 deployment,改变 spec.template,Pod 会被重建。保存后立刻生效
kubectl edit deployment nginx
# 增加资源限制
spec:
replicas: 1 # 修改副本数位 1,仅改变 replicas,Pod 不会被重建
template: # 修改 template,Pod 会被重建
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
cpu: 500m
memory: 500Mi
requests:
cpu: 200m
memory: 200Mi
  • 命令行创建 HPA

1
2
3
4
5
6
7
8
9
10
11
12
# 创建了一个叫“hpa-nginx”的 HPA,默认与 deployment 的名称相同,可以用 --name='hpa-nginx' 指定hpa的名称
# replicas 变动范围是最小 3,最大 10
# 目标cpu利用率为 50%,上面我们设定 CPU request 值为 100m,所以当平均cpu值为 100m 时就会触发 autoscale
# 这里说平均cpu,是指所有pod的cpu利用率的平均值
$ k autoscale deployment nginx --name="hpa-nginx" --cpu-percent=50 --min=3 --max=10
horizontalpodautoscaler.autoscaling/hpa-nginx autoscaled
# 观察,大约 15秒后,hpa-nginx 发现 replicas 值为1,30秒的时候自动扩容到3个,因为我们设置的hpa的replicas最小值为3,并且采集到了cpu使用率
$ k get hpa -w
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-nginx Deployment/nginx cpu: <unknown>/50% 3 10 0 0s
hpa-nginx Deployment/nginx cpu: <unknown>/50% 3 10 1 15s
hpa-nginx Deployment/nginx cpu: 0%/50% 3 10 3 30s
  • ymal文件创建 HPA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: autoscaling/v2    # api版本,这里设置为 v2
kind: HorizontalPodAutoscaler # 资源类型
metadata:
name: hpa-nginx # hpa资源名称
namespace: default # 命名空间
spec:
maxReplicas: 10 # 最大副本数
minReplicas: 1 # 最小副本数
metrics: # 指标设定
- type: Resource # 资源类型,可以配置为 Resource/Object/Pods
resource: # 资源对象
name: cpu # 资源名称 cpu/memory
target: # 目标对象
type: Utilization # 目标类型,可以配置为 Utilization/AverageValue/Value
averageUtilization: 50 # 目标值,这里是平均使用率的百分比
scaleTargetRef: # 指定要自动伸缩的资源对象
apiVersion: apps/v1
kind: Deployment # 资源类型为 Deployment
name: nginx # 资源名称为 nginx

HPA 支持的监控指标类型

类型 字段名 说明 常见用途
Resource resource 基于 Pod 资源使用情况(CPU、内存),最常用 按 CPU 利用率自动扩缩容
Pods pods 基于每个 Pod 的自定义度量指标的平均值,需要第三方监控系统提供,比如 Prometheus 按业务指标(如请求数、队列长度)扩缩容
Object object 基于单个 Kubernetes 对象(如 Service、Ingress)的指标,,比如 Prometheus 按某个对象的指标扩缩容,例如队列长度

Resource 资源指标(CPU/内存)

  • Utilization: 利用率百分比

  • Utilization 不关心 limits,只看 requests。

  • 如果 resources.requests 没有设置,HPA Utilization 会报错(除非 AverageValue)。

1
2
3
4
5
6
7
metrics:
- type: Resource
resource: # 资源 指标
name: cpu # cpu
target:
type: Utilization # 利用率百分比
averageUtilization: 50 # 50%

当前有 3 个 Pod:

Pod CPU 实际使用 CPU requests
pod-1 150m 200m
pod-2 100m 200m
pod-3 50m 200m
合计 400m 600m
平均 400m ÷ 600m × 100% ≈ 66.7%

由于 66.7% > 50%,会扩容;如果小于 50%,HPA 会缩容。

  • AverageValue: 指定资源使用平均值

1
2
3
4
5
6
7
metrics:
- type: Resource
resource:
name: memory # 监控资源类型为 memory(内存)
target:
type: AverageValue # 指标类型为 AverageValue,表示每个 Pod 平均使用多少
averageValue: 400Mi # 目标值:平均每个 Pod 使用 400Mi 内存

假设当前部署了 3 个副本,内存使用情况如下:

Pod 当前内存使用
pod-1 450Mi
pod-2 400Mi
pod-3 300Mi
平均 (450+400+300)/3 = 383.3Mi

因为 383.3Mi < 400Mi,不触发扩容。如果将来平均使用超过 400Mi,HPA 就会扩容,反之缩容。

HPA 扩缩容速度配置

  • HPA 的扩缩容速度是可以控制的,通过 behavior 字段来自定义扩缩容的速度和策略。

  • 以下是默认策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
behavior:
scaleUp: # ⬆️ 扩容策略
tolerance: 0.1 # 容忍阈值,默认0.1(10%),表示每次扩容时,pod使用率超过1.1倍目标值时才会进行扩容
stabilizationWindowSeconds: 0 # 默认0,即增加或减少pod数量后保持不变的时间,单位为秒
policies:
- type: Percent # 扩容速度按百分比限制
value: 100 # 每 15 秒最多扩容 100% 的副本数
periodSeconds: 15 # 每 15 秒计算一次是否可以扩容,默认 15 秒
- type: Pods # 按固定 Pod 数量限制扩容速度
value: 4 # 每 15 秒最多扩容 4 个 Pod
periodSeconds: 15 # 同样 15 秒计算一次,默认 15 秒
selectPolicy: Max # 多个 policy 同时存在时,取 Percent 和 Pods 的最大值。Min 取最小值

scaleDown: # ⬇️ 缩容策略
# selectPolicy: Disabled # 禁用缩容
stabilizationWindowSeconds: 300 # 默认 300,即增加或减少pod数量后保持不变的时间,单位为秒,防止快速缩容(平滑策略)
policies:
- type: Percent # 缩容速度按百分比限制
value: 100 # 每 15 秒最多缩容 100% 的副本数
periodSeconds: 15 # 每 15 秒评估一次是否可以缩容
  • 完整的 HPA 配置文件示例(一般情况下,我们无需修改扩缩容速度的配置,默认策略满足大部分场景)

1
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
apiVersion: autoscaling/v2                    # api版本,使用 v2 版本支持 behavior 扩缩容策略
kind: HorizontalPodAutoscaler # 资源类型为 HPA
metadata:
name: hpa-nginx # HPA 资源的名称
namespace: default # 命名空间,设置为 default
spec:
maxReplicas: 10 # HPA 自动扩容的最大副本数
minReplicas: 1 # HPA 自动缩容的最小副本数
metrics: # 自动伸缩的指标配置
- type: Resource # 指标类型为 Resource(资源型)
resource: # 资源指标对象
name: cpu # 资源名称为 cpu,也可以设置为 memory
target: # 目标指标对象
type: Utilization # 指标类型为 Utilization(利用率百分比)
averageUtilization: 50 # CPU 平均使用率达到 50% 时进行扩缩容
scaleTargetRef: # HPA 绑定的目标资源对象
apiVersion: apps/v1
kind: Deployment # 目标类型为 Deployment
name: nginx # 目标 Deployment 名称为 nginx
behavior: # ⬇️ 扩缩容行为控制
scaleUp: # 扩容策略
stabilizationWindowSeconds: 30 # 扩容平滑窗口为 30 秒,防止短时间抖动频繁扩容
policies: # 扩容速度限制策略
- type: Percent # 按百分比计算
value: 100 # 每 15 秒内最多扩容 100%
periodSeconds: 15 # 每 15 秒评估一次扩容速率
- type: Pods # 按固定 Pod 数量计算
value: 4 # 每 15 秒最多扩容 4 个 Pod
periodSeconds: 15 # 每 15 秒评估一次扩容速率
selectPolicy: Max # 如果多个 policy 同时满足,取最大值(更激进的扩容)
scaleDown: # 缩容策略
stabilizationWindowSeconds: 300 # 缩容平滑窗口为 300 秒(5 分钟),避免短时流量降低快速缩容
policies: # 缩容速度限制策略
- type: Percent # 按百分比缩容
value: 30 # 每 60 秒最多缩容 30%
periodSeconds: 60 # 每 60 秒评估一次缩容速率