跳转至

弹性伸缩灵活调节 HPA 扩缩容速率

HPA v2beta2 版本开始支持调节扩缩容速率

K8S 1.18 之前,HPA 扩容是无法调整速率的:

  • 缩容速率,由 kube-controller-manager 的 --horizontal-pod-autoscaler-downscale-stabilization-window 参数控制缩容时间窗口,默认 5 分钟,即负载减小后至少需要等 5 分钟才会缩容。

  • 扩容速率,由 hpa controller 固定的算法、硬编码的常量因子来控制扩容速度,无法自定义。这样的设计逻辑导致用户无法自定义 HPA 的扩缩容速率,而不同的业务场景对于扩容容灵敏度要求可能是不一样的,

对于有流量突发的关键业务,在需要的时候应该快速扩容 (即便可能不需要,以防万一),但缩容要慢 (防止另一个流量高峰)。处理关键数据的应用,数据量飙升时它们应该尽快扩容以减少数据处理时间,数据量降低时应尽快缩小规模以降低成本,数据量的短暂抖动导致不必要的频繁扩缩是可以接受的。处理常规数据/网络流量的业务,不是很重要,它们可能会以一般的方式扩大和缩小规模,以减少抖动。

HPA 在 K8S 1.18 迎来了一次更新,在之前 v2beta2 版本上新增了扩缩容灵敏度的控制,不过版本号依然保持 v2beta2 不 变。

计算原理

  • HPA 在进行扩缩容时,先是由固定的算法计算出期望副本数:

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

其中 当前指标 / 期望指标 的比例如果接近 1 (在容忍度范围内,默认为 0.1,即比例在 0.9~1.1 之间),则不进行伸缩,避免抖动导致频繁扩缩容。

容忍度是由 kube-controller-manager 参数 --horizontal-pod-autoscaler-tolerance 决定,默认是 0.1,即 10%。

介绍的扩缩容速率调节,不是指要调整期望副本数的算法,它并不会加大或缩小扩缩容比例或数量,仅仅是控制扩缩容的速率,实现的效果是: 控制 HPA 在 XX 时间内最大允许扩容/缩容 XX 比例/数量的 Pod。

HPA 查看多个 apiVersion 版本:

[root@k8smaster002 ~]# kubectl api-versions | grep autoscaling
autoscaling/v1
autoscaling/v2

使用 scaleUp 和 scaleDown 调整扩缩容速率

随着 Kubernetes 1.18 的发布,你现在有能力来定义 scaleUp 和 scaleDown 方案 官方链接。这有助于防止在副本数量持续波动时出现所谓的 "抖动"(或不必要的缩放)。

1. 如下所示,有两个部分。第一个 ( scaleDown) 定义缩小参数,而第二个 ( scaleUp) 用于放大。每个部分都有 stabilizationWindowSeconds 这个参数本质上是作为副本数量改变后的超时时间。
behavior:
scaleDown:
  stabilizationWindowSeconds: 60
  policies:
  - type: Percent
    value: 5    # 确保每分钟删除的 Pod 数不超过 5 个
    periodSeconds: 20
  - type: Pods
    value: 5    # 大小固定为 5                      
    periodSeconds: 60
  selectPolicy: Min #  将 selectPolicy 设置为 Min 意味着 autoscaler 会选择影响 Pod 数量最小的策略
scaleUp:
  stabilizationWindowSeconds: 0
  policies:
  - type: Percent
    value: 100
    periodSeconds: 10

scaleDown 策略

  • 允许您指定在 type: Percent 特定时间段内缩减的 Pod 百分比 。如果负载具有周期性现象,您必须做的是降低百分比并增加持续时间。在这种情况下,随着负载的减少,HPA 不会立即杀死大量 Pod(根据其公式),而是会逐渐杀死对应的 Pod。此外,您可以设置 type: Pods 在指定时间段内允许 HPA 杀死的最大 Pod 数量 。

  • 注意 selectPolicy: Min 参数。这意味着 HPA 使用影响最小 Pod 数量的策略。因此,如果百分比值(上例中的 5%)小于数字替代值(上例中的 5 个 Pod),HPA 将选择百分比值。相反,selectPolicy: Max 策略会产生相反的效果。

scaleUp 策略

  • scaleUp 部分中使用了类似的参数。请注意,在大多数情况下,集群必须(几乎)立即扩容,因为即使是轻微的延迟也会影响用户及其体验。因此,在本节中StabilizationWindowsSeconds 设置为 0 。如果负载具有循环模式,HPA可以在必要时将副本计数增加到 maxReplicas(如HPA清单中定义的)。我们的策略允许 HPA 每10秒(periodSeconds:10)向当前运行的副本添加多达 100% 的副本。

总结:完整的 HPA 清单

编写 HPA 清单。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 10
  - type: External
    external:
      metric:
        name: queue_messages
      target:
        type: AverageValue
        averageValue: 15
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 100 # 指定了在缩减 Pod 副本数之前的稳定时间窗口为 100 秒。这意味着在检测到缩减条件后 100 秒开始缩减操作
      policies:     # 定义了缩减 Pod 副本数的策略列表。
      - type: Percent   # 指定了使用百分比作为缩减策略。
        value: 5    # 指定了缩减的百分比为 5%。这意味着在每个缩减周期中,将缩减当前副本数的 5%。
        periodSeconds: 20   # 指定了计算缩减的周期为 20 秒。这意味着每隔 20 秒计算一次是否需要进行缩减操作。
      - type: Pods  # 指定了使用 Pod 数量作为缩减策略
        value: 5    # 指定了缩减的 Pod 数量为 5 个。这意味着在每个缩减周期中,将缩减当前副本数的 5 个 Pod。
        periodSeconds: 60   # 指定了计算缩减的周期为 60 秒。这意味着每隔 60 秒计算一次是否需要进行缩减操作。
      selectPolicy: Min # 指定了在使用多个缩减策略时选择最小值作为最终策略。这意味着在每个缩减周期中,将根据百分比缩减和固定 Pod 数量缩减这两个策略计算出的缩减值中选择较小的一个作为最终的缩减值。
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:    # 指定了扩容策略的列表。
      - type: Percent   # 指定了扩容策略的类型。在这个配置中,使用的是 "Percent" 类型的策略,即基于百分比的扩容。
        value: 100      # 指定了扩容的百分比值。在这个配置中,设置为 100,表示在每次计算扩容操作时,将当前副本数的 100% 作为目标副本数。
        periodSeconds: 10 # periodSeconds: 指定了扩容策略计算的时间间隔(秒)。在这个配置中,设置为 10 秒,表示每隔 10 秒计算一次扩容操作。

Horizontal Pod Autoscaler 简介: HPA 非常适合生产环境。但是,在为 HPA 选择指标时,您必须谨慎并尽量多的考虑现状。错误的度量标准或错误的阈值将导致资源浪费(来自不必要的副本)或服务降级(如果副本数量不够)。密切监视应用程序的行为并对其进行测试,直到达到正确的平衡。

一些扩缩容的案例

快速扩容

当你的应用需要快速扩容时,可以使用类似如下的 HPA 配置:

1. 表示扩容时立即新增当前 9 倍数量的副本数,即立即扩容到当前 10 倍的 Pod 数量,当然也不能超过 maxReplicas 的限制。
behavior:
  scaleUp:
    policies:
    - type: Percent
      value: 900
      periodSeconds: 15 # 每 15s 最多允许扩容 9 倍于当前副本数

假如一开始只有 1 个 Pod,如果遭遇流量突发,且指标持续超阈值 9 倍以上,它将以飞快的速度进行扩容,扩容时 Pod 数量变化趋势如下:

1 -> 10 -> 100 -> 1000

没有配置缩容策略,将等待全局默认的缩容时间窗口 (默认5分钟) 后开始缩容。

缓慢扩容

如果你的应用不太关键,希望扩容时不要太快,可以让它扩容平稳缓慢一点,为 HPA 加入下面的 behavior

1. 增加 scaleUp 配置,指定扩容时每 5 分钟才扩容 1 个 Pod,大大降低了扩容速度
behavior:
  scaleUp:
    policies:
    - type: Pods
      value: 1 
      periodSeconds: 300 # 每 5 分钟最多只允许扩容 1 个 Pod

假如一开始只有 1 个 Pod,指标一直持续超阈值,扩容时它的 Pod 数量变化趋势如下:

1 -> 2 -> 3 -> 4

快速扩容,缓慢缩容

如果流量高峰过了,并发量骤降,如果用默认的缩容策略,等几分钟后 Pod 数量也会随之骤降,如果 Pod 缩容后突然又来一个流量高峰,虽然可以快速扩容,但扩容的过程毕竟还是需要一定时间的,如果流量高峰足够高,在这段时间内还是可能造成后端处理能力跟不上,导致部分请求失败。这时候我们可以为 HPA 加上缩容策略,HPA behavior 配置示例如下:

1. 增加 scaleDown 配置,指定缩容时每 10 分钟才缩掉 1 个 Pod,大大降低了缩容速度
behavior:
  scaleUp:
    policies:
    - type: Percent
      value: 900
      periodSeconds: 15 # 每 15s 最多允许扩容 9 倍于当前副本数
  scaleDown:
    policies:
    - type: Pods
      value: 1
      periodSeconds: 600 # 每 10 分钟最多只允许缩掉 1 个 Pod

上面示例中缩容时的 Pod 数量变化趋势如下:

1000 -> … (10 min later) -> 999

这个可以让关键业务在可能有流量突发的情况下保持处理能力,避免流量高峰导致部分请求失败。

延长扩容时间窗口

有些应用经常会有数据毛刺导致频繁扩容,而扩容出来的 Pod 其实没太大必要,反而浪费资源。比如数据处理管道的场景,需要的副本数取决于队列中的事件数量,当队列中堆积了大量事件时,我们希望可以快速扩容,但又不希望太灵敏,因为可能只是短时间内的事件堆积,即使不扩容也可以很快处理掉。

1. 可以给扩容增加一个时间窗口以避免毛刺导致扩容带来的资源浪费
behavior:
  scaleUp:
    stabilizationWindowSeconds: 300 # 扩容前等待 5 分钟的时间窗口
    policies:
    - type: Pods
      value: 20 
      periodSeconds: 60 # 每分钟最多只允许扩容 20 个 Pod

需要先等待 5 分钟的时间窗口,如果在这段时间内指标又降下来了就不再扩容,如果一直持续超过阈值才扩容,并且每分钟最多只允许扩容 20 个 Pod。

禁止自动缩容

如果应用非常关键,希望扩容后不自动缩容,需要人工干预或其它自己开发的 controller 来判断缩容条件,可以使用类型如下的 behavior 配置来禁止自动缩容:

1. selectPolicy: Disabled 关闭缩容
behavior:
  scaleDown:
    selectPolicy: Disabled

延长缩容时间窗口

缩容默认时间窗口是 5 分钟,如果我们需要延长时间窗口以避免一些流量毛刺造成的异常,可以指定下缩容的时间窗口,behavior 配置示例如下:

1. 增加 scaleDown 配置,当负载降下来时,会等待 600s (10 分钟) 再缩容,每 10 分钟最多只允许缩掉 5 个 Pod。
behavior:
  scaleDown:
    stabilizationWindowSeconds: 600 # 等待 10 分钟再开始缩容
    policies:
    - type: Pods
      value: 5 
      periodSeconds: 600 # 每 10 分钟最多只允许缩掉 5 个 Pod

配置快速扩容,为什么快不起来?

比如这个配置:

允许每 10 秒最大允许扩出 9 倍于当前数量的 Pod,实测中可能发现压力已经很大了,但扩容却并不快。
behavior:
  scaleUp:
    policies:
    - type: Percent
      value: 900
      periodSeconds: 10

通常原因是计算周期与指标延时:

  • 期望副本数的计算有个计算周期,默认是 15 秒 (由 kube-controller-manager 的 --horizontal-pod-autoscaler-sync-period 参数决定)。

  • 每次计算时,都会通过相应的 metrics API 去获取当前监控指标的值,这个返回的值通常不是实时的,对于腾讯云容器服务而言,监控数据是每分钟上报一次;对于自建的 prometheus + prometheus-adapter 而言,监控数据的更新取决于监控数据抓取间隔,prometheus-adapter 的 --metrics-relist-interval 参数决定监控指标刷新周期(从 prometheus 中查询),这两部分时长之和为监控数据更新的最长时间。

通常都不需要 HPA 极度的灵敏,有一定的延时一般都是可以接受的。如果实在有对灵敏度特别敏感的场景,可以考虑使用 prometheus,缩小监控指标抓取间隔和 prometheus-adapter 的 --metrics-relist-interval。

总结

本文介绍了如何利用 HPA 的新特性来控制扩缩容的速率,以更好的满足各种不同场景对扩容速度的需求,也提供了常见的几种场景与配置示例,可自行根据需求修改配置。