k8s作为云操作系统
,跟主机操作系统一样,核心功能就是管理云中的资源,例如,CPU、内存等。
从业务角度上看,单个Pod能够提供的服务资源肯定是有限的,因此,在使用Deployment进行部署时可能会需要配置多个副本,那么,配置多少个合适呢?如果配置的副本数少,单个Pod的资源使用率会比较高,业务逻辑可能会执行异常;如果配置的副本数多,单个Pod的资源使用率会比较低,造成资源的浪费。有没有一种办法可以让Pod的副本数能够随业务的流量动态调整,当Pod资源使用率升高,就增加副本数,当Pod资源使用率降低,就减少副本数。
也就是需要一种机制:它能够实时监控Pod的资源使用率,然后给Pod的资源使用率设置一个范围,当Deployment的Pod的平均资源使用率超过这个范围,则增加Pod的副本数;当Deployment的Pod的平均资源使用率低于这个范围,则减少Pod副本数。
这就是k8s中的Horizontal Pod Autoscaler
,简称HPA,翻译为水平Pod自动伸缩
,水平的意思表明它是调整Pod的数量。既然有水平,那肯定有垂直,也就是调整Pod的参数配置,Vertical Pod Autoscaler
,简称VPA,翻译为垂直Pod自动伸缩
。无论是HPA还是VPA,都是在集群中自动进行Pod的伸缩,那如果平均资源使用率超过一定范围,而且HPA和VPA都不能扩容时,应该怎么办呢?此时,所有的节点的资源使用率都比较高,那就只能增加节点了,这就是Cluster Autoscaler
,简称CA
,翻译为集群自动伸缩
。
它们实现的基本思路是:
由于要监控Pod的资源使用率,,就需要有组件可以采集Pod的资源使用率,最常见的是metrics-server。
按照metrics-servers官方文档,直接执行下面的语句就可以完成metrics-server的部署:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
但是在实际使用中会发现有三个问题:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --requestheader-username-headers=X-Remote-User
- --requestheader-group-headers=X-Remote-Group
- --requestheader-extra-headers-prefix=X-Remote-Extra-
image: registry.aliyuncs.com/google_containers/metrics-server:v0.6.4
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
tcpSocket:
port: 4443
periodSeconds: 10
name: metrics-server
ports:
- containerPort: 4443
name: https
protocol: TCP
readinessProbe:
failureThreshold: 3
tcpSocket:
port: 4443
initialDelaySeconds: 20
periodSeconds: 10
部署完成后用kubectl top node
和kubectl top pod
命令就可以看到按照资源使用率排序的node和pod,说明metrics-server正常工作了。
有了metrics-server,那怎么提供给HPA Controller使用呢?或者说怎么能够让HPA Controller从metrics-server获取到指标数据呢?如果HPA Controller直接调用metrics-server的服务,那么两者就有依赖关系,不便于扩展。
这里使用了Aggregate API
的方式:在k8s中注册一个APIService的资源,APIService中指定了API的group、version和service的名称,当某个组件访问apiserver的接口时,会根据接口中的group和version,将调用转发给后端的service,这种方式就实现了对k8s api的一种扩展。
HPA Controller在获取资源使用率时,group会设置为metrics.k8s.io,而metrics.k8s.io的常用的后端就是metrics-server。
现在HPA Controller可以通过调用Aggregate API的方式获取Pod的资源使用率,下一步就是决策是否需要进行扩容和缩容。
获取到Pod的资源使用率后,需要与定义的资源使用率限制进行对比,如果超过定义的资源使用率,则需要进行扩容,否则,进行缩容。
这里以CPU使用率说明计算方式:
当从metrics-server获取到nginx的Deployment的所有Pod中的容器的CPU使用量,然后计算出每个Pod的CPU使用量,再获取出Pod的CPU的requests使用量,然后将当前的CPU使用量/requests使用量,就得到单个Pod的CPU使用率,最终可以计算出Deployment的平均CPU使用率,再与预先定义的资源使用率限制进行对比。例如,当Deployment的Pod的平均CPU使用率是60,当前Pod数是3,用户设置目标CPU使用率是20,那么HPA就会认为Pod需要扩容至9,如果maxReplicas小于9,那就只会扩容到maxReplicas。
下面看下HPA资源的定义和使用:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: nginx
spec:
maxReplicas: 10 # 最大副本数
minReplicas: 1 # 最小副本数
scaleTargetRef: # 进行自动伸缩的目标资源
apiVersion: apps/v1
kind: Deployment
name: nginx
targetCPUUtilizationPercentage: 2 # 目标CPU使用率
这里需要注意的是:如果使用HPA对Pod进行管理,Pod就需要配置requests:pod.spec.containers.resources.requests
。
# kubectl get hpa -o wide
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx Deployment/nginx 0%/2% 1 10 1 23h
在TARGETS列展示的就是当前平均CPU使用率/目标CPU使用率
。
当对该nginx的Deployment的Service增加负载:
kubectl run test --image nginx --rm -i -- /bin/bash -c "while true; do curl service_ip:service_port;done"
然后用kubectl get pods --watch
命令查看Pod的变化,并且可以看到
但是上面的判断逻辑还有两个问题:
多久执行一次判断?这是通过kube-controller-manager的horizontal-pod-autoscaler-sync-period
参数设置的,默认是15秒。
那么,当某次执行判断认为需要需要进行扩容或者缩容时,是否就要立即进行扩容或者缩容呢?如果完全根据上面的计算方法进行扩容或者缩容,由于流量可能是变化的,而且获取metrics-server的数据可能有延后或者不稳定,可能导致前一次判断需要扩容,后一次判断需要缩容,从而导致集群的抖动,这肯定是不被允许的。为了解决这种场景,k8s提供了两种机制来减少抖动造成的影响:
以上主要针对的是扩容,当某个判断周期中计算得到的扩容比例位于[1.1,2]时,就可以进行扩容,此时可以快速解决业务负载高的问题,但是对于缩容则不能立即执行,因为缩容会带来业务上的不稳定,因此,HPA中会通过一个时间窗口进行若干次判断,如果这段时间的Pod变化比例都小于0.9就可以进行缩容,默认的时间窗口是5分钟。
本文讲解了k8s中的HPA水平Pod伸缩机制,分析了扩缩容的整体流程,简单说明了扩缩容的计算方式,并对扩容和缩容中的工程实践中的一些机制进行了分析。