# 自动伸缩

  • Pod 的业务负载可能有时大、有时小,因此占用的 CPU、内存等资源量也会变化。
    • 如果一直给 Pod 分配较多资源,则负载较低时, Pod 会浪费资源。
    • 如果一直给 Pod 分配较少资源,则负载较高时, Pod 会资源不足,导致处理速度慢,甚至服务中断。
    • 为了自动调整 Pod 数量、资源配额,k8s 提供了 HPA、VPA、CA 等功能。
  • 在离线混合部署:在线业务一般运行时间长,比如需要 24 小时供用户使用。但是存在低峰期,比如晚上的负载较低。此时可在同一服务器上运行一些耗时短的离线业务,比如 k8s Job ,从而提高服务器的 CPU、内存等资源的利用率。

# HPA

:横向的 Pod 自动伸缩(Horizontal Pod Autoscaling),即自动增减 Pod 数量。

  • 用于控制 Deployment、StatefulSet 类型的 Pod 数量,相当于自动执行 kubectl scale 命令。例如当全部 Pod 的平均 CPU 使用量高于期望值时,自动增加 Pod 数量,从而降低平均 CPU 使用量。

    • DaemonSet 不支持改变 Pod 数量,因此不支持 HPA 。
  • 原理:

    1. 用户创建一个 HPA 对象,监控 Pod 的一些 metrics 指标。
    2. kube-controller-manager 执行 HPA 伸缩规则,自动调整 Pod 的 replicas 数量,使得 metrics 指标接近期望值。算法为:
      rate = metrics_当前值 / metrics_期望值
      replicas_期望值 = ceil(replicas_当前值 * rate )   # ceil 即向上取整
      
      • kube-controller-manager 默认每隔 --horizontal-pod-autoscaler-sync-period=15 秒执行一次 HPA 。
      • 如果 rate 减去 1 的差值小于 --horizo​​ntal-pod-autoscaler-tolerance=0.1 ,则不会向上取整到 2 ,避免 replicas 抖动。
  • 一个 HPA 对象的配置示例:

    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: test-hpa
      namespace: default
    spec:
      maxReplicas: 10           # Pod 自动伸缩的最大数量
      minReplicas: 1            # Pod 自动伸缩的最小数量
      metrics:                  # 选择监控指标
      - resource:
          name: cpu
          target:               # 期望值,这里为全部 Pod 的平均 cpu 使用量
            averageValue: 100m
            type: AverageValue
      - resource:
          name: memory
          target:               # 期望值,这里为全部 Pod 的平均 memory 使用率,即实际使用量占 requests 的百分比
            averageUtilization: 80
            type: Utilization
          type: Resource
      # - containerResource:    # 只监控某个容器的资源使用率,而不考虑 Pod 中其它容器
      #     name: cpu
      #     container: container-0
      #     target:
      #       type: Utilization
      #       averageUtilization: 80
      scaleTargetRef:           # 选择要控制的 Pod
        apiVersion: apps/v1
        kind: Deployment
        name: nginx
      # behavior:               # 配置 HPA 的行为策略,主要用于避免 replicas 抖动
      #   scaleDown:            # 减少 replicas 时的策略
      #     stabilizationWindowSeconds: 300 # 如果连续 n 秒都想减少 replicas ,才允许减少。实际算法:统计最近 n 秒内 desiredReplicas 的最大值,作为 scaleDown 的依据
      #     policies:
      #     - type: Percent
      #       value: 100
      #       periodSeconds: 15 # 每次减少 replicas 时,限制在 15s 内最多减少 100% 的幅度
      #     selectPolicy: Max   # Max 表示,存在多个策略时,会采用使得 replicas 更大的那个策略。如果改为 Min ,则会采用使得 replicas 更小的那个策略。如果改为 Disabled ,则禁止 scaleUp
      #   scaleUp:              # 增加 replicas 时的策略
      #     stabilizationWindowSeconds: 0   # 如果连续 n 秒都想增加 replicas ,才允许增加。实际算法:统计最近 n 秒内 desiredReplicas 的最小值,作为 scaleUp 的依据
      #     policies:
      #     - type: Percent
      #       value: 100
      #       periodSeconds: 15 # 每次增加 replicas 时,限制在 15s 内最多增加 100% 的幅度
      #     - type: Pods
      #       value: 4
      #       periodSeconds: 15 # 每次增加 replicas 时,限制在 15s 内最多增加 4
      #     selectPolicy: Max
    status:
      currentMetrics:           # 记录 metrics 的当前值
      - resource:
          current:
            averageUtilization: 50
            averageValue: "52618582"
          name: memory
        type: Resource
      - resource:
          current:
            averageValue: 1m
          name: cpu
        type: Resource
      currentReplicas: 1        # 当前的 replicas
      desiredReplicas: 1        # 期望的 replicas
    
  • 关于监控指标。

    • HPA 原生的 metrics 种类少,只监控了 Pod 的 cpu、memory 负载。而且当 Pod 没有运行时就不能获取 metrics ,因此不能缩减到 minReplicas=0 。
      • 计算全部 Pod 的平均指标时,只会统计 Ready 状态的 Pod ,排除不健康的 Pod 、正在被删除的 Pod 。
      • 计算全部 Pod 的平均指标时,不能考虑到负载特别大的个别 Pod 。比如某个 Pod 内存不足,但全部 Pod 的平均内存负载不大,则不会增加 replicas 。
    • 可选在 k8s 安装 prometheus-adapter (opens new window) ,将 Prometheus 存储的 metrics 传给 apiserver ,从而允许 HPA 读取多种多样的 metrics ,而且能缩减到 minReplicas=0 。
    • 可选在 k8s 安装 keda (opens new window) ,从 Prometheus、Kafka 等数据源监听 event 来触发 HPA 。相当于 prometheus-adapter 的超集,支持更多数据源,而且能更及时地自动伸缩,甚至在负载升高之前就增加 replicas 。
  • 关于 replicas 字段的修改。

    • 用 HPA 管理 Deployment 或 DaemonSet 时,如果手动修改 replicas 的值,而且与 HPA 的 desiredReplicas 值不同,则等到 kube-controller-manager 下一次执行 HPA 规则时,会自动将 replicas 赋值为 desiredReplicas 。这会导致一些 Pod 在短时间内启动、停止,增加开销。
    • 同理,用 kubectl apply -f xx.yml 命令修改被 HPA 管理的 Deployment 或 DaemonSet 的配置文件时,应该省略 replicas 字段。
    • 滚动更新 Deployment 时,会创建新旧两个 ReplicaSet ,分别配置一个 replicas 字段。而 HPA 修改的是 Deployment 中的 replicas 字段,也就是新的 ReplicaSet 中的 replicas 字段。
  • 关于 replicas 字段的抖动。

    • metrics 的值可能每分钟变化两三次,导致 HPA 经常修改 replicas 。为了避免 replicas 抖动,建议配置 HPA behavior 。
    • 配置了 HPA behavior 时,HPA 每次修改 replicas (包括首次修改),都需要遵守限制条件。
    • 对于重要的业务程序 Pod ,优先保障 Pod 服务质量。当负载升高时,迅速增加 replicas 。当负载降低时,缓慢减少 replicas 。
    • 对于不重要的业务程序 Pod ,优先节省 CPU、内存资源。当负载升高时,缓慢增加 replicas 。当负载降低时,迅速减少 replicas 。
    • 为了避免 replicas 取值过大,耗尽 k8s 资源,建议设置合适的 maxReplicas ,还可设置整个 namespace 的 ResourceQuota 。

# VPA

:纵向的 Pod 自动伸缩(Vertical Pod Autoscaler),即自动增减 Pod 的 requests、limits 资源配额。

  • 目前 Pod 的资源配额是静态配置,想修改的话只能创建新 Pod ,导致 Pod 内应用程序重启。有的公司修改了 k8s 源代码,能在 Pod 运行时动态修改资源配额。

# CA

:k8s 集群的自动伸缩(cluster-autoscaler),即自动增减集群的 node 数量,从而增减整个集群的 CPU、内存等资源。