kubernetes自动扩容之Horizontal Pod Autoscaling(HPA)

原创
半兽人 发表于: 2018-10-10   最后更新时间: 2021-11-05 17:07:44  
{{totalSubscript}} 订阅, 9,835 游览

我们通过手动执行kubectl scale命令,可以实现Pod扩容。但是,分布式系统要能够根据当前负载的变化情况自动触发水平扩展或缩容的行为,因为这一过程可能是频繁发生的、不可预料的,所以手动控制的方式是不现实的。

因此,在Kubernetes1.1版本中首次发布了这一重量级新特性-----Horizontal Pod Autoscaler

Pod水平自动扩缩(Horizontal Pod Autoscaler),简称HPA,意思是Pod横向水平自动扩容,与之前的RC、Deployment一样,也属于一种Kubernetes资源对象。可以基于 CPU 利用率自动扩缩 ReplicationController、Deployment、ReplicaSet 和 StatefulSet 中的 Pod 数量。如果Pod负载超过预定值,就开始增加Pod的个数,如果低于某个值,就自动减少Pod的个数。

Pod自动扩缩不适用于无法扩缩的对象,比如 DaemonSet。

水平扩容

常见的两种方式作为Pod负载的度量指标:

  • CPU 利用率。
  • 自定义的度量指标(比如服务在每秒内的相应的请求数,如:TPS或QPS)。

CPU利用率是一个算术平均值,即目标pod所有副本自身的CPU利用率的平均值。一个Pod自身的CPU利用率是该Pod当前CPU使用量除以它的Pod limit的值。比如当我们定义一个Pod的pod limit为0.4,而当前pod的cpu使用量为0.2,则使用率为50%。如果某一个时刻CPU利用率超过了设置的扩展阈值,则意味着当前Pod副本不足以支撑接下来更多的请求,需要进行动态扩容。而当请求高峰时段过去后,Pod CPU利用率又会降下来,此时对应的Pod副本数应该自动减少到一个合理的水平。

条件

HPA通过定期(定期轮询的时间通过--horizontal-pod-autoscaler-sync-period选项来设置,默认的时间为15秒)通过Status.PodSelector来查询pods的状态,获得pod的CPU使用率。然后,通过现有pods的CPU使用率的平均值(计算方式是最近的pod使用量(最近一分钟的平均值,从heapster中获得)除以设定的每个Pod的CPU使用率限额)跟目标使用率进行比较,并且在扩容时,还要遵循预先设定的副本数限制:MinReplicas <= Replicas <= MaxReplicas。

计算扩容后Pod的个数:sum(最近一分钟内某个Pod的CPU使用率的平均值) / CPU使用上限的整数 + 1

流程

1、创建HPA资源,设定目标CPU使用率限额,以及最大、最小实例数
2、收集一组中(PodSelector)每个Pod最近一分钟内的CPU使用率,并计算平均值
3、读取HPA中设定的CPU使用限额
4、计算:平均值之和/限额,求出目标调整的实例个数
5、目标调整的实例数不能超过1中设定的最大、最小实例数,如果没有超过,则扩容;超过,则扩容至最大的实例个数

例外

考虑到自动扩展的决策可能需要一段时间才会生效,甚至在短时间内会引入一些噪声。例如当pod所需要的CPU负荷过大,从而运行一个新的pod进行分流,在创建过程中,系统的CPU使用量可能会有一个攀升的过程。所以,在每一次作出决策后的一段时间内,将不再进行扩展决策,默认为5分钟。

HPA允许一定范围内的CPU使用量的不稳定,只有小于90%或者大于110%时才会触发扩容或缩容,避免频繁扩容、缩容造成抖动(Thrashing)

根据--horizontal-pod-autoscaler-tolerance参数全局配置的容忍值,默认为 0.1)

为什么选择相对比率

为了简便,选用了相对比率(90%的CPU资源)而不是0.6CPU core来描述扩容、缩容条件。如果选择使用绝对度量,用户需要保证目标(限额)要比请求使用的低,否则,过载的Pod未必能够消耗那么多,从而自动扩容永远不会被触发:假设设置CPU为1个核,那么这个pod只能使用1个核,可能Pod在过载的情况下也不能完全利用这个核,所以扩容不会发生。在修改申请资源时,还有同时调整扩容的条件,比如将1个core变为1.2core,那么扩容条件应该同步改为1.2core,真是太麻烦了,与自动扩容的目标相悖。

下面是HPA定义的一个具体例子:

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 90

根据上面的定义,我们可以知道,这个HPA控制的目标对象为一个名叫php-apacheDeployment里的Pod副本,当这些Pod副本的CPUUtilizationPercentage的值超过90%时会触发自动动态扩容行为,扩容或缩容时必须满足的一个约束条件是Pod的副本数要介于110之间。

通过命令创建上面的例子:

kubectl autoscale deployment php-apache --cpu-percent=90 --min=1 --max=10

算法细节

从最基本的角度来看,Pod 水平自动扩缩控制器根据当前指标和期望指标来计算扩缩比例。

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

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

如果计算出的扩缩比例接近 1.0(根据--horizontal-pod-autoscaler-tolerance参数全局配置的容忍值,默认为 0.1),将会放弃本次扩缩。

所有被标记了删除时间戳(Pod 正在关闭过程中)的 Pod 和失败的 Pod 都会被忽略。

如果某个 Pod 缺失度量值,它将会被搁置,只在最终确定扩缩数量时再考虑。

当使用 CPU 指标来扩缩时,任何还未就绪(例如还在初始化)状态的 Pod 或 最近的指标 度量值采集于就绪状态前的 Pod,该 Pod 也会被搁置。

由于受技术限制,Pod 水平扩缩控制器无法准确的知道 Pod 什么时候就绪,也就无法决定是否暂时搁置该 Pod。--horizontal-pod-autoscaler-initial-readiness-delay 参数(默认为 30s)用于设置 Pod 准备时间,在此时间内的 Pod 统统被认为未就绪。--horizontal-pod-autoscaler-cpu-initialization-period参数(默认为5分钟)用于设置 Pod 的初始化时间,在此时间内的 Pod,CPU 资源度量值将不会被采纳。

在排除掉被搁置的 Pod 后,扩缩比例就会根据currentMetricValue/desiredMetricValue计算出来。

如果缺失任何的度量值,我们会更保守地重新计算平均值,在需要缩小时假设这些 Pod 消耗了目标值的 100%, 在需要放大时假设这些 Pod 消耗了 0% 目标值。这可以在一定程度上抑制扩缩的幅度。

此外,如果存在任何尚未就绪的 Pod,我们可以在不考虑遗漏指标或尚未就绪的 Pod 的情况下进行扩缩, 我们保守地假设尚未就绪的 Pod 消耗了期望指标的 0%,从而进一步降低了扩缩的幅度。

在扩缩方向(缩小或放大)确定后,我们会把未就绪的 Pod 和缺少指标的 Pod 考虑进来再次计算使用率。 如果新的比率与扩缩方向相反,或者在容忍范围内,则跳过扩缩。 否则,我们使用新的扩缩比例。

注意,平均利用率的原始值会通过 HorizontalPodAutoscaler 的状态体现( 即使使用了新的使用率,也不考虑未就绪 Pod 和 缺少指标的 Pod)。

如果创建 HorizontalPodAutoscaler 时指定了多个指标, 那么会按照每个指标分别计算扩缩副本数,取最大值进行扩缩。 如果任何一个指标无法顺利地计算出扩缩副本数(比如,通过 API 获取指标时出错), 并且可获取的指标建议缩容,那么本次扩缩会被跳过。 这表示,如果一个或多个指标给出的 desiredReplicas 值大于当前值,HPA 仍然能实现扩容。

最后,在 HPA 控制器执行扩缩操作之前,会记录扩缩建议信息。 控制器会在操作时间窗口中考虑所有的建议信息,并从中选择得分最高的建议。 这个值可通过 kube-controller-manager 服务的启动参数 --horizontal-pod-autoscaler-downscale-stabilization 进行配置, 默认值为 5 分钟。 这个配置可以让系统更为平滑地进行缩容操作,从而消除短时间内指标值快速波动产生的影响。

支持可配置的扩缩 {#support-for-configurable-scaling-behaviour}

v1.18开始,v2beta2 API 允许通过 HPA 的 behavior 字段配置扩缩行为。在 behavior 字段中的 scaleUpscaleDown 分别指定扩容和缩容行为。可以两个方向指定一个稳定窗口,以防止扩缩目标中副本数量的波动。类似地,指定扩缩策略可以控制扩缩时副本数的变化率。

扩缩策略 {#scaling-policies}

在 spec 字段的 behavior 部分可以指定一个或多个扩缩策略。当指定多个策略时,默认选择允许更改最多的策略。下面的例子展示了缩容时的行为:

behavior:
  scaleDown:
    policies:
    - type: Pods
      value: 4
      periodSeconds: 60
    - type: Percent
      value: 10
      periodSeconds: 60

periodSeconds 表示在过去的多长时间内要求策略值为真。第一个策略(Pods)允许在一分钟内最多缩容 4 个副本。第二个策略(Percent)允许在一分钟内最多缩容当前副本个数的百分之十。

由于默认情况下会选择容许更大程度作出变更的策略,只有 Pod 副本数大于 40 时,第二个策略才会被采用。如果副本数为 40 或者更少,则应用第一个策略。例如,如果有 80 个副本,并且目标必须缩小到 10 个副本,那么在第一步中将减少 8 个副本。在下一轮迭代中,当副本的数量为 72 时,10% 的 Pod 数为 7.2,但是这个数字向上取整为 8。在 autoscaler 控制器的每个循环中,将根据当前副本的数量重新计算要更改的 Pod 数量。当副本数量低于 40 时,应用第一个策略(Pods),一次减少 4 个副本。

可以指定扩缩方向的 selectPolicy 字段来更改策略选择。通过设置 Min 的值,它将选择副本数变化最小的策略。将该值设置为 Disabled 将完全禁用该方向的缩放。

稳定窗口 {#stabilization-window}

当用于扩缩的指标持续抖动时,使用稳定窗口来限制副本数上下振动。自动扩缩算法使用稳定窗口来考虑过去计算的期望状态,以防止扩缩。在下面的例子中,稳定化窗口被指定为 scaleDown

scaleDown:
  stabilizationWindowSeconds: 300

当指标显示目标应该缩容时,自动扩缩算法查看之前计算的期望状态,并使用指定时间间隔内的最大值。
在上面的例子中,过去 5 分钟的所有期望状态都会被考虑。

默认行为 {#default-behavior}

要使用自定义扩缩,不必指定所有字段。只有需要自定义的字段才需要指定。这些自定义值与默认值合并。默认值与 HPA 算法中的现有行为匹配。

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
  scaleUp:
    stabilizationWindowSeconds: 0
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
    - type: Pods
      value: 4
      periodSeconds: 15
    selectPolicy: Max

用于缩小稳定窗口的时间为 300 秒(或是 --horizontal-pod-autoscaler-downscale-stabilization
参数设定值)。只有一种缩容的策略,允许 100% 删除当前运行的副本,这意味着扩缩目标可以缩小到允许的最小副本数。对于扩容,没有稳定窗口。当指标显示目标应该扩容时,目标会立即扩容。这里有两种策略,每 15 秒添加 4 个 Pod 或 100% 当前运行的副本数,直到 HPA 达到稳定状态。

示例:更改缩容稳定窗口

将下面的 behavior 配置添加到 HPA 中,可提供一个 1 分钟的自定义缩容稳定窗口:

behavior:
  scaleDown:
    stabilizationWindowSeconds: 60

示例:限制缩容速率

将下面的 behavior 配置添加到 HPA 中,可限制 Pod 被 HPA 删除速率为每分钟 10%:

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60

为了确保每分钟删除的 Pod 数不超过 5 个,可以添加第二个缩容策略,大小固定为 5,并将 selectPolicy 设置为最小值。将 selectPolicy 设置为 Min 意味着 autoscaler 会选择影响 Pod 数量最小的策略:

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60
    - type: Pods
      value: 5
      periodSeconds: 60
    selectPolicy: Min

示例:禁用缩容

selectPolicy 的值 Disabled 会关闭对给定方向的缩容。因此使用以下策略,将会阻止缩容:

behavior:
  scaleDown:
    selectPolicy: Disabled

隐式维护状态禁用

你可以在不必更改 HPA 配置的情况下隐式地为某个目标禁用 HPA。如果此目标的期望副本个数被设置为 0,而 HPA 的最小副本个数大于 0,则 HPA 会停止调整目标(并将其自身的 ScalingActive 状况设置为 false),直到你通过手动调整目标的期望副本个数或 HPA 的最小副本个数来重新激活。

更新于 2021-11-05
在线,5小时前登录

Gorgeous low-key 10月前

HPA 也是存在于Master节点上吗?

只是个规则,存储在etcd中。

规则存储在ETCD中,那规则运算处理是不是由Master节点做运算,然后将结果分发到个节点Node来处理Pod扩容或缩容?

是的,kube-scheduler组件。

枫叶痕 1年前

targetCPUUtilizationPercentage--cpu-percent 具体是什么意思呢?这篇里的很多名词(计算使用率流程中的)感觉一头雾水啊

Lance.Wu -> 枫叶痕 1年前

targetCPUUtilizationPercentage--cpu-percent都是表示容器资源的CPU使用率。

targetCPUUtilizationPercentage:表示Kubernetes内部对象数据展示的内容字段信息。

--cpu-percent:kubernetes客户端命令指定CPU使用率的参数字段,但最终还是转换成targetCPUUtilizationPercentage

如果Pod设置了资源大小则以资源大小为基准进行伸缩,如果当前Pod未设置资源大小,则以当前所在节点的整体资源大小按使用比率进行伸缩。

半兽人 -> 枫叶痕 1年前

--cpu-percenttargetCPUUtilizationPercentage命令形式的简写,它俩是一个东西。

枫叶痕 -> 半兽人 1年前

"比如当我们定义一个Pod的pod request为0.4,而当前pod的cpu使用量为0.2,则使用率为50%"
这里说的 pod request 是如何定义的? 当前pod的cpu使用量 是怎么获得的?
谢谢!

Lance.Wu -> 枫叶痕 1年前

kubectl explain pod.spec.containers.resources使用该命令可以查询到Pod设置资源的参数,里面有一个是requests和一个是limits。requests是指你向宿主机占用的资源大小,limits是Pod的最大使用资源,实际是以limits下面的CPU为准。

kubectl top pod podName该命令可以查询Pod的CPU使用量。

要使用HPA功能,kubernetes集群需要安装 metrics-server插件,具体安装插件的内容你可以参考 https://www.orchome.com/1203 文章。

枫叶痕 -> Lance.Wu 1年前

感谢!

陈健飞 1年前

HAP 应该是HPA

半兽人 -> 陈健飞 1年前

感谢提醒,已更正。

点点滴滴ゝ 1年前

php-apache写成了java-apache

感谢提示,已更新。

vitsippa -> 半兽人 1年前

还是 java-apache,没有改呢,
kubectl autoscale deployment java-apache --cpu-percent=90 --min=1 --max=10

半兽人 -> vitsippa 1年前

感谢,中秋节快乐。
只改了yaml的,autoscale漏掉了,已更正。

我想问 rc定义的时候有一个replactset 和这个HAP Replicas会有冲突吗?例如 rc定义副本数为1 这个最小1最大10 那这个时候rc 的作用在哪里?

已hpa的为准。

明白。多谢

查看kubernetes更多相关的文章或提一个关于kubernetes的问题,也可以与我们一起分享文章
提问