1 为什么需要HPA

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或者Node的资源使用率
  • 控制器定时检测Pod或者Node的平均资源使用率
  • 如果平均资源使用率超过目标资源使用率,则进行Pod的扩容,但是不会超过配置的最大副本数
  • 如果平均资源使用率低于目标资源使用率,则进行Pod的缩容,但是不会低于配置的最小副本数

2 HPA

2.1 metrics-server组件部署

由于要监控Pod的资源使用率,,就需要有组件可以采集Pod的资源使用率,最常见的是metrics-server。

按照metrics-servers官方文档,直接执行下面的语句就可以完成metrics-server的部署:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

但是在实际使用中会发现有三个问题:

  • 镜像下载问题:将metrics-server的镜像仓库换成registry.aliyuncs.com/google_containers
  • 探针检查失败:将存活探针和就绪探针从http改成socket方式
  • 证书过期问题:对metrics-server的参数进行调整
    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 nodekubectl top pod命令就可以看到按照资源使用率排序的node和pod,说明metrics-server正常工作了。

2.2 聚合API

有了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。

2.3 HPA扩缩容计算方式

现在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的变化,并且可以看到

请添加图片描述

请添加图片描述

2.4 快扩容,慢缩容

但是上面的判断逻辑还有两个问题:

  • 多久执行一次判断?
  • 当某次判断需要扩容或者缩容时,是否立即就去进行扩容或者缩容?

多久执行一次判断?这是通过kube-controller-manager的horizontal-pod-autoscaler-sync-period参数设置的,默认是15秒。

那么,当某次执行判断认为需要需要进行扩容或者缩容时,是否就要立即进行扩容或者缩容呢?如果完全根据上面的计算方法进行扩容或者缩容,由于流量可能是变化的,而且获取metrics-server的数据可能有延后或者不稳定,可能导致前一次判断需要扩容,后一次判断需要缩容,从而导致集群的抖动,这肯定是不被允许的。为了解决这种场景,k8s提供了两种机制来减少抖动造成的影响:

  • horizontal-pod-autoscaler-tolerance参数控制副本变化幅度的范围,默认为0.1,因此,Pod扩缩容的变化比例必须不在[0.9,1.1]之间,也就是说,不是只要计算出需要扩容的比例就进行扩容,当计算出需要扩容的比例时,这个比例需要大于1.1,当缩容时这个比例需要小雨小于0.9。
  • 如果根据指标计算得到的最终Pod数量比当前Pod数量大很多,HPA不是一下就将Pod扩容至最终的目标数,而是针对变化的比例有限制,必须小于2,因此,一次扩容最多增加一倍,特例是,当原副本数为1时,最大可以扩容到4。

以上主要针对的是扩容,当某个判断周期中计算得到的扩容比例位于[1.1,2]时,就可以进行扩容,此时可以快速解决业务负载高的问题,但是对于缩容则不能立即执行,因为缩容会带来业务上的不稳定,因此,HPA中会通过一个时间窗口进行若干次判断,如果这段时间的Pod变化比例都小于0.9就可以进行缩容,默认的时间窗口是5分钟。

3 总结

本文讲解了k8s中的HPA水平Pod伸缩机制,分析了扩缩容的整体流程,简单说明了扩缩容的计算方式,并对扩容和缩容中的工程实践中的一些机制进行了分析。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐