k8s中的自动扩容Horizontal Pod Autoscaling(HPA)
分布式系统要能够根据当前负载的变化情况自动触发水平扩展或缩容的行为,因为这一过程可能是频繁发生的、不可预料的,所以手动控制的方式是不现实的。HPA全称是Horizontal Pod Autoscaler,翻译成中文是POD水平自动伸缩,以下都会用HPA代替Horizontal Pod Autoscaler,HPA可以基于CPU利用率、内存利用率对replication controller、deployment和replicaset中的pod数量进行自动扩缩容(除了CPU利用率也可以基于其他应程序提供的度量指标custom metrics进行自动扩缩容)
在Kubernetes1.1版本中首次发布了这一重量级新特性—–Horizontal Pod Autoscaler。
HPA的实现是一个控制循环,由controller manager的–horizontal-pod-autoscaler-sync-period参数指定周期(默认值为15秒)。每个周期内,controller manager根据每个HorizontalPodAutoscaler定义中指定的指标查询资源利用率。controller manager可以从resource metrics API(pod 资源指标)和custom metrics API(自定义指标)获取指标。依赖metric-server服务,需要自行单独部署,过程中可以随时查看该pod的日志来排查问题,部署方式参考https://github.com/kubernetes-sigs/metrics-server
目前,可以有以下两种方式作为Pod负载的度量指标:
1、CPU/内存 utilization percentage
2、应用程序自定义的度量指标,比如服务在每秒内的相应的请求数(TPS或QPS)
CPU/内存 utilization percentage是一个算术平均值,即目标pod所有副本自身的CPU/内存利用率的平均值。拿cpu来说,一个Pod自身的CPU利用率是该Pod当前CPU使用量除以它的Pod request的值。比如当我们定义一个Pod的pod request为0.6,而当前pod的cpu使用量为0.3,则使用率为50%。如此可以得出一个平均值,如果某一个时刻CPU utilization percentage超过80%,则意味着当前Pod副本不足以支撑接下来更多的请求,需要进行动态扩容。而当请求高峰时段过去后,Pod的CPU利用率又会降下来,此时对应的Pod副本数应该自动减少到一个合理的水平。
CPU/内存 utilization percentage计算过程使用到的Pod的CPU使用量通常是1分钟的平均值。
条件
HPA通过定期(定期轮询的时间通过–horizontal-pod-autoscaler-sync-period选项来设置,默认的时间为30秒)通过Status.PodSelector来查询pods的状态,获得pod的CPU使用率。然后,通过现有pods的CPU使用率的平均值(计算方式是最近的pod使用量(最近一分钟的平均值,从heapster中获得)除以设定的每个Pod的CPU使用率限额)跟目标使用率进行比较,并且在扩容时,还要遵循预先设定的副本数限制:MinReplicas <= Replicas <= MaxReplicas。
计算扩容后Pod的个数:sum(最近一分钟内某个Pod的CPU使用率的平均值)/CPU使用上限的整数+1
过程
1、创建HPA资源,设定目标CPU或者内存使用率限额,以及最大、最小实例数
2、收集一组中(PodSelector)每个Pod最近一分钟内的CPU或者内存使用率,并计算平均值
3、读取HPA中设定的CPU或者内存使用限额
4、计算:平均值之和/限额,求出目标调整的实例个数
5、目标调整的实例数不能超过1中设定的最大、最小实例数,如果没有超过,则扩容;超过,则扩容至最大的实例个数
例外
考虑到自动扩展的决策可能需要一段时间才会生效,例如当pod所需要的CPU/内存负荷过大,从而运行一个新的pod进行分流,在创建过程中,系统的CPU/内存使用量可能会有一个攀升的过程。所以,在每一次作出决策后的一段时间内,将不再进行扩展决策。对于ScaleUp而言,这个时间段为3分钟,Scaledown为5分钟。
HPA允许一定范围内的CPU/内存使用量的不稳定,只有avg(CurrentPodsConsumption) / Target**小于90%或者大于110%**时才会触发扩容或缩容,避免频繁扩容、缩容造成颠簸。如下,内存使用率已经超过85%还是没有自动扩展。
[root@prod-com-k8master1 prod_yaml]# kubectl get hpa -A
NAMESPACE NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
com-prod aad-server-mem Deployment/aad-server 123%/85% 2 4 4 223d
com-prod ac-server-mem Deployment/ac-server 92%/85% 2 3 2 223d
com-prod adb-service-mem Deployment/adb-service 90%/85% 1 2 1 223d
com-prod adc-service-mem Deployment/adc-service 62%/85% 2 3 2 223d
com-prod ade-service-mem Deployment/ade-service 54%/85% 6 9 6 226d
为什么选择相对使用率
为了简便,选用了相对使用率(90%的CPU资源)而不是0.6个CPU core来描述扩容、缩容条件。如果选择使用绝对度量,用户需要保证目标(限额)要比请求使用的低,否则,过载的Pod未必能够消耗那么多,从而自动扩容永远不会被触发:假设设置CPU为1个核,那么这个pod只能使用1个核,可能Pod在过载的情况下也不能完全利用这个核,所以扩容不会发生。在修改申请资源时,还有同时调整扩容的条件,比如将1个core变为1.2core,那么扩容条件应该同步改为1.2core,真是太麻烦了,与自动扩容的目标相悖。
实例一
1、基于CPU自动扩容
[root@k8s-m1 hpa]# cat cpu-hpa.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-cpu-hpa
namespace: test1
spec:
replicas: 1
selector:
matchLabels:
app: nginx-cpu-hpa
template:
metadata:
labels:
app: nginx-cpu-hpa
spec:
containers:
- name: nginx-hpa
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
resources:
limits:
cpu: 200m
requests:
cpu: 100m
---
apiVersion: v1
kind: Service
metadata:
name: nginx-cpu-hpa-svc
namespace: test1
spec:
ports:
- port: 80
protocol: TCP
selector:
app: nginx-cpu-hpa
type: ClusterIP
kubectl apply -f cpu-hpa.yaml
2、创建HPA
nginx-cpu-hpa服务正在运行,使用kubectl autoscale创建自动缩放器,实现对nginx-cpu-hpa这个deployment创建的pod自动扩缩容,下面的命令将会创建一个HPA,HPA将会根据CPU,内存等资源指标增加或减少副本数,创建一个可以实现如下目的的hpa:
1)让副本数维持在1-10个之间(这里副本数指的是通过deployment部署的pod的副本数)
2)将所有Pod的平均CPU使用率维持在20%(通过kubectl run运行的每个pod如果是100毫核,这意味着平均CPU利用率为20毫核
给上面nginx-cpu-hpa这个deployment创建HPA
[root@k8s-m1 hpa]#kubectl autoscale deployment nginx-cpu-hpa -n test1 --cpu-percent=20 --min=1 --max=10
解释:
kubectl autoscale deployment nginx-cpu-hpa (nginx-cpu-hpa表示deployment的名字) --cpu-percent=20(表示cpu使用率不超过50%) --min=1(最少一个pod) --max=10(最多10个pod)
kubectl delete hpa tomcat-hpa -n app (命令行创建的hpa修改需要先删除)
3、验证HPA
[root@k8s-m1 hpa]# kubectl get hpa -n test1
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-cpu-hpa Deployment/nginx-cpu-hpa 0%/20% 1 10 1 14m
4、进行压测查看pod的数量变化
查看svc地址为:
[root@k8s-m1 hpa]# kubectl get svc -n test1
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-cpu-hpa-svc ClusterIP 10.109.69.60 <none> 80/TCP 49m
进行压力测试:
为满足大并发,通过安装webbench软件进行测试,安装过程如下:
yum install make cmake ctags -y
mkdir /usr/local/man
wget http://home.tiscali.cz/~cz210552/distfiles/webbench-1.5.tar.gz
tar zxvf webbench-1.5.tar.gz
cd webbench-1.5
make && make install
while true; do webbench -c 200 -t 60 http://10.109.69.60:80/index.html; done #一分钟内200个并发
5、查看hpa的负载变化
[root@k8s-m1 hpa]# kubectl get hpa -n test1 nginx-cpu-hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-cpu-hpa Deployment/nginx-cpu-hpa 115%/20% 1 10 6 4h4m
6、查看pod数量变化
[root@k8s-m1 hpa]# kubectl get pods -n test1 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-cpu-hpa-7b7545fcc-4dxjt 1/1 Running 0 37s 10.244.11.0 k8s-m3 <none> <none>
nginx-cpu-hpa-7b7545fcc-cjcgc 1/1 Running 0 37s 10.244.11.27 k8s-m3 <none> <none>
nginx-cpu-hpa-7b7545fcc-fpf8b 1/1 Running 0 21s 10.244.11.13 k8s-m3 <none> <none>
nginx-cpu-hpa-7b7545fcc-wkjk9 1/1 Running 0 4h37m 10.244.11.38 k8s-m3 <none> <none>
nginx-cpu-hpa-7b7545fcc-z2wkc 1/1 Running 0 37s 10.244.11.62 k8s-m3 <none> <none>
nginx-cpu-hpa-7b7545fcc-zsgnl 1/1 Running 0 21s 10.244.11.3 k8s-m3 <none> <none>
7、测试停止后,pod数量减少为1
[root@k8s-m1 hpa]# kubectl get pods -n test1 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-cpu-hpa-7b7545fcc-wkjk9 1/1 Running 0 51m 10.244.11.38 k8s-m3 <none> <none>
实例二
1、基于内存自动扩容
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-mem-hpa
namespace: test1
spec:
replicas: 1
selector:
matchLabels:
app: nginx-mem-hpa
template:
metadata:
labels:
app: nginx-mem-hpa
spec:
containers:
- name: nginx-hpa
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
resources:
limits:
memory: 100Mi
requests:
memory: 50Mi
---
apiVersion: v1
kind: Service
metadata:
name: nginx-mem-hpa-svc
namespace: test1
spec:
ports:
- port: 80
protocol: TCP
selector:
app: nginx-mem-hpa
type: ClusterIP
---
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa
namespace: test1
spec:
maxReplicas: 10
minReplicas: 1
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx-mem-hpa
metrics:
- type: Resource
resource:
name: memory
targetAverageUtilization: 50
2、验证HPA
[root@k8s-m1 hpa]# kubectl get hpa -n test1
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-hpa Deployment/nginx-mem-hpa 6%/50% 1 10 1 87s
3、进行压测查看pod的数量变化
查看svc地址为:
[root@k8s-m1 webbench-1.5]# kubectl get svc -n test1
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-mem-hpa-svc ClusterIP 10.108.189.64 <none> 80/TCP 12m
进行压力测试,通过webbench软件进行大并发测试,安装过程如下:
[root@k8s-m1 webbench-1.5]# while true; do webbench -c 20000 -t 10 http://10.108.189.64:80/index.html; done #10s内20000w个并发
4、动态查看hpa的负载变化
[root@k8s-m1 hpa]# kubectl get hpa -n test1 -w
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-hpa Deployment/nginx-mem-hpa 20%/50% 1 10 1 1m
......
......
nginx-hpa Deployment/nginx-mem-hpa 63%/50% 1 10 6 14m
5、查看pod数量变化,
[root@k8s-m1 hpa]# kubectl get pod -n test1
NAME READY STATUS RESTARTS AGE
nginx-mem-hpa-855fb5bbb4-6xbtk 1/1 Running 0 7m48s
nginx-mem-hpa-855fb5bbb4-9lklv 1/1 Running 0 6m46s
nginx-mem-hpa-855fb5bbb4-ffdlc 1/1 Running 0 7m48s
nginx-mem-hpa-855fb5bbb4-k8bpt 1/1 Running 0 6m46s
nginx-mem-hpa-855fb5bbb4-m9sgb 1/1 Running 0 6m46s
nginx-mem-hpa-855fb5bbb4-nqmh7 1/1 Running 0 10m
6、测试停止后,pod数量减少为1,注意这个过程变化效果可能比较缓慢
[root@k8s-m1 hpa]# kubectl get pod -n test1
NAME READY STATUS RESTARTS AGE
nginx-mem-hpa-855fb5bbb4-nqmh7 1/1 Running 0 17m