横向伸缩

Pod 的横向伸缩是指由 Horizontal 控制器 管理的 Pod 副本数量 的自动伸缩。

可以通过向 Horizontal 控制器 提交一个 HorizontalPodAutoscaler (HPA) 资源来将需要自动横向伸缩的 Deployment 等资源对象托付给 HPA 资源对象,由 HPA 管理目标对象的 Pod 副本数量 (replicas 字段)

🚦 提交 HPA 对象后,Horizontal 控制器 会周期性检查 Pod 度量(关注的是 Pod 的哪一项特性),计算满足 HPA 配置的目标数值所需的 副本数量,进而调整目标资源(如 Deployment、ReplicaSet、ReplicationController、StatefulSet 等)的 replicas 字段。

创建 HPA

最简单的创建方式是使用 kubectl autoscale 命令构建 HPA 资源对象。创建案例如下,将 deployment/kubia 作为伸缩对象,以该目标对象内部所有 Pod 的 CPU 平均使用率 为度量,持续调整目标对象的 replicas ,直至 Pod 的 CPU 实际使用量 接近 申请量30%,Pod 的最小数量为 1,最大数量为 5

kubectl autoscale deployment kubia --cpu-percent=30 --min=1 --max=5

🚀 目前 HPA 资源已经有多个版本,每个版本使用的 YAML 配置清单会有区别,这里直接介绍 --api-version=autoscaling/v2beta2 版本的配置清单。

查看某类资源指定版本的 YAML 配置清单格式可用如下命令,案例中的资源为 hpa

kubectl explain hpa --api-version=autoscaling/v2beta2

查看某资源指定版本的 YAML 配置清单可用如下命令,案例中的资源对象名为 kubia

kubectl get hpa.v2beta2.autoscaling kubia -o yaml

下面使用 YAML 配置清单创建 HPA 资源,配置实例中展示了基于目标 CPU 使用率、目标对象 deployment/kubia 中 Pod 的平均 QPS、度量对象 ingress/main-route 中所有 Pod 的总 QPS 三种度量的配置方式(实际使用中可以根据需求,只配置一种即可):

apiVersion: autoscaling/v2beta2    # 注意版本
kind: HorizontalPodAutoscaler    # 资源类型
metadata:
  name: kubia    # HPA 资源对象名
  namespace: default    # 所在命名空间
spec:
  maxReplicas: 5    # 目标资源对象中 replicas 可调成的最大值
  minReplicas: 1    # 目标资源对象中 replicas 可调成的最小值
  scaleTargetRef:    # 需要调整的目标资源对象(!)
    apiVersion: apps/v1    # 目标资源所在 API 版本
    kind: Deployment    # 目标资源类型
    name: kubia    # 目标资源对象名
  metrics:    # 度量(重点!!决定了以什么为标准决定是否伸缩、如何伸缩)
  - resource:    # Resource 类型度量对应的就是该字段
      name: cpu    # 参考的资源类型(如果是内存,就是 memory)
      target:
        averageUtilization: 30    # 调整目标对象的 Pod 副本数直至其中所有 Pod 的 CPU 实际平均利用率应该接近申请量的 30%
        type: Utilization    # 根据利用率伸缩
    type: Resource    # Resource 类型度量(还有 Pod 和 Object 两种)
  - pods:    # Pods 类型度量对应的就是该字段
      metric:
        name: packets-per-second    # 参考的资源类型,这里是包的 QPS,该字段是通过其他手段添加至 API 服务器提供的 /metrics 下的度量类型
      target:
        averageValue: 1k    # 调整目标对象的 Pod 副本数直至其中所有 Pod 的平均 QPS 接近 1K
    type: Pods    # Pods 类型度量
  - object:    # Object 类型度量对应的就是该字段
      metric:
        name: requests-per-second    # 参考的资源类型,这里是请求 main-route 这个 Ingress 中的 Pod 的 QPS,该字段是通过其他手段添加至 API 服务器提供的 /metrics 下的度量类型
      describedObject:    # 度量对象
        apiVersion: networking.k8s.io/v1beta1
        kind: Ingress
        name: main-route
      target:
        type: Value
        value: 2k    # 调整目标对象的 Pod 副本数直至度量对象(main-route)中所有 Pod 的总 QPS 接近 2K
    type: Object    # Object 类型度量

hpa.spec.metrics.type 除了可以是 Resource 外,还可以是 PodsObject 两种,下面对三种应用场景分别进行解释:

  • Resource:针对 Pod 中资源申请量作为度量,例如 Pod 的 CPU 和 内存等的实际使用量与申请量的比值;

  • Pods:针对其他种类的(包括自定义的)与 Pod 直接相关的度量,也就是说度量来自 目标对象 中的 Pod 自身,例如 QPS、消息队列中的消息数量(消息队列服务运行于 Pod 中)等;

  • Object:针对与 Pod 没有直接关系的度量,也就是说度量来自 非目标对象,而是来自另外指定的度量对象,例如给予另一个集群对象,比如 Ingress 对象等;

🍓 伸缩操作的最大速率(避免抖动):扩缩容单次操作时 最多使副本数翻倍;两次操作之间的时间间隔也有限制,两次 扩容 操作需间隔 3 分钟,两次 缩容 操作需间隔 5 分钟。

🥑 在对目标对象的副本数伸缩后,该对象的 replicas 会被调整为对应的值,即使此时删除 HPA 资源,该目标对象的 replicas 仍然是被固定为 HPA 删除时的值。

🗣 一定要把度量对象中的度量值设置远低于 100%,最好不要超过 90%,预留充分空间给资源请求洪峰。

🗣 并不是所有资源都可以作为度量的,如果增加副本数不能导致被观测的度量平均值的线形下降,那么 HPA 就不能正常工作。

🗣 hpa.spec.minReplicas 设为 0 时,即为 空载,在新的请求来时,该请求会被阻塞,直到有 Pod 被启动。(使用该特性需要满足一些要求 TODO)

伸缩过程

自动伸缩过程可以分为三步:

  1. 获取被伸缩资源对象(目标对象)所管理的所有 Pod 度量(CPU 利用率等),这个拉取动作是 周期性

  2. 计算使度量数值到达(或接近)所指定目标数值所需的 Pod 副本数;

  3. 更新被伸缩资源对象的 replicas 字段;

伸缩机制

获取度量

在 1.8 及之后的 Kubernetes 版本中,提供了 /metrics API 接口供 HPA 获取度量数据。用户可以通过向 API 服务器提交请求,添加新的度量参数,实现自定义度量。

在 minikube 中,可以通过 minikube addons enable metrics-server 开启该特性

计算所需 Pod 数量

HPA 需要计算出一个合适的副本数 replicas,使所有副本 Pod 上度量的平均值尽量接近配置的目标值。该计算的输入是一组 Pod 的度量(每个 Pod 可能有多个),输出则是一个整数(Pod 副本数)。

期望副本数的公式如下:

期望副本数 = ceil[当前副本数 * ( 当前指标 / 期望指标 )]

ceil 为向上取整

例如,当前度量为 200m,目标设定值为 100m,那么由于 200.0 / 100.0 == 2.0, 副本数量将会翻倍。 如果当前指标为 50m,副本数量将会减半,因为 50.0 / 100.0 == 0.5

多个度量时

当 Pod 存在多个度量时,首先根据每种度量计算出各自的期望副本数,然后取所有期望副本数中的 最大值 即可。

更新目标对象的副本数

HPA 会通过 Scale 子资源 修改目标资源对象的 replicas 字段,如下图。

通过 Scale 资源伸缩目标对象

这意味着只要 API 服务器为目标资源暴露了 Scale 子资源,HPA 就可以通过该资源间接操作目标资源的 replicas 字段。目前暴露了 Scale 子资源的资源有:

  • Deployment;

  • ReplicaSet;

  • ReplicationController;

  • StatefulSet;

Last updated

Was this helpful?