Horizontal Pod Autoscaling
在 Kubernetes 中,HorizontalPodAutoscaler 会自动更新工作负载资源(例如 Deployment 或 StatefulSet),旨在自动调整容量以匹配需求。
水平伸缩意味着响应增加的负载是部署更多的 Pod。这与垂直伸缩不同,对于 Kubernetes 而言,这意味着为已经运行的工作负载的 Pod 分配更多的资源(例如:内存或 CPU)。
如果负载降低,并且 Pod 的数量超过了配置的最小值,HorizontalPodAutoscaler 会指示工作负载资源(Deployment、StatefulSet 或其他类似资源)缩小规模。
水平 Pod 自动伸缩不适用于无法伸缩的对象(例如:DaemonSet)。
HorizontalPodAutoscaler 作为 Kubernetes API 资源和 控制器 实现。该资源决定了控制器的行为。运行在 Kubernetes 控制平面 中的水平 Pod 自动伸缩控制器,会定期调整其目标的期望规模(例如,Deployment)以匹配观察到的指标,例如平均 CPU 使用率、平均内存使用率或您指定的任何其他自定义指标。
有一个 演练示例,介绍了如何使用水平 Pod 自动伸缩。
HorizontalPodAutoscaler 是如何工作的?
图 1. HorizontalPodAutoscaler 控制 Deployment 及其 ReplicaSet 的规模
Kubernetes 将水平 Pod 自动伸缩实现为以间歇性方式运行的控制循环(它不是一个连续的过程)。间隔由传递给 kube-controller-manager 的 --horizontal-pod-autoscaler-sync-period 参数设置(默认间隔为 15 秒)。
在每个周期中,控制器管理器都会查询针对每个 HorizontalPodAutoscaler 定义中指定的指标的资源利用率。控制器管理器找到由 scaleTargetRef 定义的目标资源,然后根据目标资源的 .spec.selector 标签选择 Pod,并从资源指标 API(用于每个 Pod 的资源指标)或自定义指标 API(用于所有其他指标)获取指标。
对于每个 Pod 的资源指标(如 CPU),控制器会从 HorizontalPodAutoscaler 针对的目标 Pod 的资源指标 API 获取指标。然后,如果设置了目标利用率值,控制器会将利用率值计算为每个 Pod 中容器上的等效 资源请求 的百分比。如果设置了目标原始值,则直接使用原始指标值。然后,控制器计算所有目标 Pod 上利用率或原始值的平均值(具体取决于指定的目标类型),并生成用于调整所需副本数量的比率。
请注意,如果某些 Pod 的容器未设置相关的资源请求,则 Pod 的 CPU 利用率将未定义,并且自动伸缩器将不会对该指标采取任何操作。有关自动伸缩算法如何工作的更多信息,请参阅下面的 算法细节 部分。
对于每个 Pod 的自定义指标,控制器的工作方式与每个 Pod 的资源指标类似,只是它使用原始值,而不是利用率值。
对于对象指标和外部指标,会获取一个单一的指标,该指标描述了所涉及的对象。将此指标与目标值进行比较,以生成上述比率。在
autoscaling/v2API 版本中,此值可以选择在与 Pod 数量进行比较之前进行除法。
HorizontalPodAutoscaler 的常见用途是将其配置为从 聚合 API(metrics.k8s.io、custom.metrics.k8s.io 或 external.metrics.k8s.io)获取指标。metrics.k8s.io API 通常由名为 Metrics Server 的插件提供,需要单独启动。有关资源指标的更多信息,请参阅 Metrics Server。
对指标 API 的支持 解释了这些不同 API 的稳定性保证和支持状态。
HorizontalPodAutoscaler 控制器访问支持伸缩的相应工作负载资源(例如 Deployment 和 StatefulSet)。这些资源中的每个资源都有一个名为 scale 的子资源,该子资源是一个允许您动态设置副本数量并检查其当前状态的接口。有关 Kubernetes API 中子资源的常规信息,请参阅 Kubernetes API 概念。
算法细节
从最基本的角度来看,HorizontalPodAutoscaler 控制器运行于期望指标值与当前指标值之间的比率
例如,如果当前指标值为 200m,而期望值为 100m,则副本数量将翻倍,因为 \( { 200.0 \div 100.0 } = 2.0 \)。
如果当前值为 50m,则会将副本数量减半,因为 \( { 50.0 \div 100.0 } = 0.5 \)。如果比率足够接近 1.0(默认情况下,在 可配置的容忍度 0.1 内),则控制平面将跳过任何伸缩操作。
当指定 targetAverageValue 或 targetAverageUtilization 时,currentMetricValue 通过计算 HorizontalPodAutoscaler 的伸缩目标中所有 Pod 上的给定指标的平均值来计算。
在检查容忍度并确定最终值之前,控制平面还会考虑是否有任何指标缺失以及有多少 Pod Ready。所有设置了删除时间戳的 Pod(对象具有删除时间戳正在关闭/删除)将被忽略,并且所有失败的 Pod 将被丢弃。
如果某个 Pod 缺少指标,则将其留待以后使用;缺少指标的 Pod 将用于调整最终的伸缩量。
在伸缩 CPU 时,如果任何 Pod 尚未准备就绪(仍在初始化中,或者可能不正常),或者 Pod 的最新指标点是在其准备就绪之前,则该 Pod 也会被留待以后使用。
由于技术限制,HorizontalPodAutoscaler 控制器无法准确确定 Pod 首次准备就绪的时间,从而确定是否要留待某些 CPU 指标。相反,如果 Pod 未准备就绪并且自启动以来在较短的可配置时间内过渡到准备就绪状态,则认为该 Pod“尚未准备就绪”。此值使用 --horizontal-pod-autoscaler-initial-readiness-delay 命令行选项配置,其默认值为 30 秒。一旦 Pod 准备就绪,如果自启动以来在较长可配置时间内过渡到准备就绪状态,则认为任何过渡到准备就绪状态都是第一次。此值使用 --horizontal-pod-autoscaler-cpu-initialization-period 命令行选项配置,其默认值为 5 分钟。
然后计算 \( currentMetricValue \over desiredMetricValue \) 基本伸缩比率,使用剩余的未留待或丢弃的 Pod。
如果存在缺失的指标,控制平面会更保守地重新计算平均值,假设这些 Pod 在缩减规模时消耗了所需值的 100%,在扩容时消耗了 0%。 这会降低任何潜在缩放的幅度。
此外,如果存在尚未就绪的 Pod,并且在没有考虑缺失的指标或尚未就绪的 Pod 的情况下,工作负载本来会扩容,那么控制器会保守地假设这些尚未就绪的 Pod 消耗了所需指标的 0%,从而进一步降低扩容的幅度。
在考虑了尚未就绪的 Pod 和缺失的指标之后,控制器会重新计算使用率。 如果新的比例反转了缩放方向,或者在容差范围内,控制器将不会采取任何缩放操作。 在其他情况下,新的比例将用于决定 Pod 数量的任何变化。
请注意,即使在使用新的使用率时,原始平均利用率值也会通过 HorizontalPodAutoscaler 的状态报告回来,不考虑尚未就绪的 Pod 或缺失的指标。
如果 HorizontalPodAutoscaler 中指定了多个指标,则会对每个指标进行此计算,然后选择所需副本数的最大值。 如果这些指标中的任何一个无法转换为所需的副本数(例如,由于从指标 API 获取指标时出错),并且可以获取的指标建议缩减规模,则跳过缩放。 这意味着 HPA 仍然可以在一个或多个指标给出大于当前值的 desiredReplicas 时进行扩容。
最后,在 HPA 缩放目标之前,会记录缩放建议。 控制器会考虑配置窗口内的所有建议,并选择该窗口内的最高建议。 您可以使用 --horizontal-pod-autoscaler-downscale-stabilization 命令行选项配置此值,默认值为 5 分钟。 这意味着缩减规模将逐渐发生,从而平滑快速波动指标值的影响。
Pod 就绪状态和自动缩放指标
HorizontalPodAutoscaler (HPA) 控制器包含两个命令行选项,这些选项会影响在 Pod 启动期间从 Pod 收集 CPU 指标的方式
--horizontal-pod-autoscaler-cpu-initialization-period(默认值:5 分钟)
这定义了 Pod 启动后,除非满足以下条件,否则其 CPU 使用率将被忽略的时间窗口:- Pod 处于 Ready 状态 并且 - 指标样本是在其处于 Ready 状态期间完全采集的。
此命令行选项有助于 排除误导性的高 CPU 使用率,这些使用率来自正在初始化的 Pod(例如:Java 应用程序预热)在 HPA 缩放决策中。
--horizontal-pod-autoscaler-initial-readiness-delay(默认值:30 秒)
这定义了 Pod 启动后的一段短暂延迟期,在此期间,HPA 控制器会将当前 Unready 的 Pod 视为仍在初始化中,即使它们之前短暂地过渡到 Ready 状态。
它的设计目的是:- 避免包含在启动期间在 Ready 和 Unready 之间快速切换的 Pod。 - 确保在 HPA 认为其指标有效之前,初始就绪信号的稳定性。
您只能在集群范围内设置这些命令行选项。
Pod 就绪状态的关键行为
- 如果 Pod 处于
Ready状态并保持Ready状态,即使在延迟期间,也可以将其计为贡献指标。 - 如果 Pod 在
Ready和Unready之间快速切换,则会忽略指标,直到其被认为稳定地处于Ready状态。
Pod 就绪状态的最佳实践
- 配置一个
startupProbe,直到高 CPU 使用率消失后才通过,或者 - 确保您的
readinessProbe仅在 CPU 峰值消退之后才报告Ready,使用initialDelaySeconds。
并且理想情况下也设置 --horizontal-pod-autoscaler-cpu-initialization-period 以 覆盖启动持续时间。
API 对象
HorizontalPodAutoscaler 是 Kubernetes autoscaling API 组中的一种 API 类型。 当前稳定版本可以在 autoscaling/v2 API 版本中找到,该版本包括对基于内存和自定义指标进行缩放的支持。 在使用 autoscaling/v1 时,在 autoscaling/v2 中引入的新字段将作为注释保留。
当您创建一个 HorizontalPodAutoscaler API 对象时,请确保指定的名字是有效的 DNS 子域名。 可以在 HorizontalPodAutoscaler 对象 中找到有关 API 对象的更多详细信息。
工作负载缩放的稳定性
在使用 HorizontalPodAutoscaler 管理副本组的缩放时,由于评估的指标的动态性,副本数可能会频繁波动。 这有时被称为震荡或拍打。 它类似于控制论中的滞后性概念。
滚动更新期间的自动缩放
Kubernetes 允许您对 Deployment 执行滚动更新。 在这种情况下,Deployment 会为您管理底层的 ReplicaSet。 当您为 Deployment 配置自动缩放时,会将 HorizontalPodAutoscaler 绑定到单个 Deployment。 HorizontalPodAutoscaler 管理 Deployment 的 replicas 字段。 Deployment 控制器负责设置底层 ReplicaSet 的 replicas,以便在推出期间和之后将其加起来到一个合适的值。
如果您对具有自动缩放副本数的 StatefulSet 执行滚动更新,StatefulSet 会直接管理其 Pod 集合(没有类似于 ReplicaSet 的中间资源)。
对资源指标的支持
任何 HPA 目标都可以根据缩放目标中 Pod 的资源使用情况进行缩放。 在定义 Pod 规范时,应指定诸如 cpu 和 memory 之类的资源请求。 这用于确定资源利用率,并由 HPA 控制器用于缩放目标上下。 要使用基于资源利用率的缩放,请指定如下的指标源
type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
使用此指标,HPA 控制器会将缩放目标中 Pod 的平均利用率保持在 60%。 利用率是当前资源使用量与 Pod 请求的资源之间的比率。 有关如何计算和平均利用率的更多详细信息,请参见 算法。
说明
由于所有容器的资源使用量都加在一起,因此总 Pod 利用率可能无法准确表示单个容器的资源使用量。 这可能导致单个容器可能以高使用率运行,而 HPA 不会扩容,因为整体 Pod 使用率仍在可接受范围内。容器资源指标
Kubernetes v1.30 [稳定](默认启用)HorizontalPodAutoscaler API 还支持容器指标源,HPA 可以跟踪一组 Pod 中各个容器的资源使用情况,以便缩放目标资源。 这使您可以为特定 Pod 中最重要的容器配置缩放阈值。 例如,如果您有一个 Web 应用程序和一个提供日志记录的 sidecar 容器,您可以根据 Web 应用程序的资源使用情况进行缩放,忽略 sidecar 容器及其资源使用情况。
如果您修改目标资源以具有包含一组不同容器的新 Pod 规范,则如果新添加的容器也应用于缩放,则应修改 HPA 规范。 如果指标源中指定的容器不存在或仅存在于一部分 Pod 中,则会忽略这些 Pod,并重新计算建议。 有关计算的更多详细信息,请参见 算法。 要使用容器资源进行自动缩放,请定义如下的指标源
type: ContainerResource
containerResource:
name: cpu
container: application
target:
type: Utilization
averageUtilization: 60
在上面的示例中,HPA 控制器缩放目标,使得所有 Pod 的 application 容器中 CPU 的平均利用率为 60%。
说明
如果您更改 HorizontalPodAutoscaler 正在跟踪的容器的名称,您可以按照特定顺序进行更改,以确保缩放可用且有效,同时应用更改。 在更新定义容器的资源(例如 Deployment)之前,应更新关联的 HPA 以跟踪新旧容器名称。 这样,HPA 能够在更新过程中计算缩放建议。
在将容器名称更改推出到工作负载资源后,通过从 HPA 规范中删除旧容器名称来清理。
基于自定义指标的缩放
Kubernetes v1.23 [稳定](以前的 autoscaling/v2beta2 API 版本以 beta 功能提供了此功能)
前提是您使用 autoscaling/v2 API 版本,您可以配置 HorizontalPodAutoscaler 以基于自定义指标(即 Kubernetes 或任何 Kubernetes 组件中未内置的指标)进行缩放。 然后,HorizontalPodAutoscaler 控制器会从 Kubernetes API 查询这些自定义指标。
请参阅 对指标 API 的支持 以获取要求。
基于多个指标的缩放
Kubernetes v1.23 [稳定](以前的 autoscaling/v2beta2 API 版本以 beta 功能提供了此功能)
前提是您使用 autoscaling/v2 API 版本,您可以为 HorizontalPodAutoscaler 指定多个指标以进行缩放。 然后,HorizontalPodAutoscaler 控制器会评估每个指标,并根据该指标提出新的缩放建议。 HorizontalPodAutoscaler 采用每个指标建议的最大缩放,并将工作负载设置为该大小(前提是这不超过您配置的总体最大值)。
对指标 API 的支持
默认情况下,HorizontalPodAutoscaler 控制器从一系列 API 获取指标。 为了使其能够访问这些 API,集群管理员必须确保
启用了 API 聚合层。
已注册相应的 API
对于资源指标,这是
metrics.k8s.ioAPI,通常由 metrics-server 提供。 它可以作为集群插件启动。对于自定义指标,这是
custom.metrics.k8s.ioAPI。 它由指标解决方案供应商提供的“adapter”API 服务器提供。 请查看您的指标管道,以查看是否有可用的 Kubernetes 指标适配器。对于外部指标,这是
external.metrics.k8s.ioAPI。 它可能由上述自定义指标适配器提供。
有关这些不同指标路径以及它们之间的区别的更多信息,请参阅 HPA V2、custom.metrics.k8s.io 和 external.metrics.k8s.io 的相关设计提案。
有关如何使用它们的示例,请参阅 使用自定义指标的演练 和 使用外部指标的演练。
可配置的缩放行为
Kubernetes v1.23 [稳定](以前的 autoscaling/v2beta2 API 版本以 beta 功能提供了此功能)
如果您使用 v2 HorizontalPodAutoscaler API,可以使用 behavior 字段(请参阅 API 参考)来配置单独的扩容和缩减行为。 您可以通过在 behavior 字段下设置 scaleUp 和/或 scaleDown 来指定这些行为。
缩放策略使您可以控制副本变化的速度。 还可以使用两种设置来防止 拍打:您可以指定一个稳定窗口来平滑副本计数,以及一个忽略低于指定阈值的微小指标波动的容差。
伸缩策略
可以在规范的 behavior 部分指定一个或多个伸缩策略。当指定多个策略时,允许最大变化量的策略将默认被选中。以下示例展示了在缩减规模时的这种行为
behavior:
scaleDown:
policies:
- type: Pods
value: 4
periodSeconds: 60
- type: Percent
value: 10
periodSeconds: 60
periodSeconds 指示策略必须保持为真的过去时间长度。您可以为 periodSeconds 设置的最大值为 1800(半小时)。第一个策略 (Pods) 允许在一个分钟内最多缩减 4 个副本。第二个策略 (Percent) 允许在一个分钟内最多缩减当前副本的 10%。
由于默认情况下选择允许最大变化量的策略,因此只有在 Pod 副本数超过 40 时,第二个策略才会被使用。当副本数小于或等于 40 时,将应用第一个策略。例如,如果有 80 个副本,并且目标需要缩减到 10 个副本,那么在第一步中,将减少 8 个副本。在下一个迭代中,当副本数为 72 时,Pod 的 10% 是 7.2,但该数字会向上取整为 8。在自动伸缩器控制器的每个循环中,要更改的 Pod 数量会根据当前副本的数量重新计算。当副本数降至 40 以下时,将应用第一个策略 (Pods),一次减少 4 个副本。
可以通过为伸缩方向指定 selectPolicy 字段来更改策略选择。通过将值设置为 Min,可以选择允许副本计数变化最小的策略。将值设置为 Disabled 会完全禁用该方向上的伸缩。
稳定窗口
稳定窗口用于限制当用于伸缩的指标不断波动时,副本计数的 剧烈波动。自动伸缩算法使用此窗口来推断先前期望的状态,并避免对工作负载规模的不必要更改。
例如,在以下示例片段中,为 scaleDown 指定了稳定窗口。
behavior:
scaleDown:
stabilizationWindowSeconds: 300
当指标表明目标应该缩减时,算法会查看先前计算的期望状态,并使用指定间隔内的最高值。在上面的示例中,将考虑过去 5 分钟内的所有期望状态。
这近似于滚动最大值,并避免自动伸缩算法频繁删除 Pod,而仅仅在片刻之后又触发重新创建等效的 Pod。
容忍度
Kubernetes v1.35 [beta](默认启用)tolerance 字段配置了指标变化的阈值,防止自动伸缩器对低于该值的变化进行伸缩。
该容忍度定义为期望指标值周围的允许变化量,在此范围内不会发生任何伸缩。例如,考虑配置了目标内存消耗量为 100MiB 且缩放容忍度为 5% 的水平 Pod 自动伸缩器
behavior:
scaleUp:
tolerance: 0.05 # 5% tolerance for scale up
使用此配置,HPA 算法仅在内存消耗量高于 105MiB(即高于目标的 5%)时才考虑进行缩放。
如果您未设置此字段,HPA 将应用默认的集群范围容忍度 10%。可以使用 kube-controller-manager --horizontal-pod-autoscaler-tolerance 命令行参数更新缩放和缩减的默认值。(您无法使用 Kubernetes API 配置此默认值。)
默认行为
要使用自定义伸缩,不必指定所有字段。只需指定需要自定义的值即可。这些自定义值将与默认值合并。默认值与 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% 的副本,这意味着缩放目标可以缩减到允许的最小副本数。对于缩放,没有稳定窗口。当指标表明目标应该缩放时,目标会立即缩放。有 2 个策略,即每 15 秒最多可以添加 4 个 Pod 或当前正在运行的 100% 的副本,直到 HPA 达到其稳定状态。
示例:更改缩减规模稳定窗口
要提供 1 分钟的自定义缩减规模稳定窗口,可以将以下行为添加到 HPA
behavior:
scaleDown:
stabilizationWindowSeconds: 60
示例:限制缩减规模速率
要限制 HPA 每分钟删除 Pod 的速率为 10%,可以将以下行为添加到 HPA
behavior:
scaleDown:
policies:
- type: Percent
value: 10
periodSeconds: 60
为了确保每分钟删除的 Pod 不超过 5 个,您可以添加第二个缩减规模策略,该策略具有固定大小为 5,并将 selectPolicy 设置为最小值。将 selectPolicy 设置为 Min 意味着自动伸缩器选择影响 Pod 数量最少的策略
behavior:
scaleDown:
policies:
- type: Percent
value: 10
periodSeconds: 60
- type: Pods
value: 5
periodSeconds: 60
selectPolicy: Min
示例:禁用缩减规模
selectPolicy 值 Disabled 会关闭给定方向上的伸缩。因此,为了防止缩减规模,将使用以下策略
behavior:
scaleDown:
selectPolicy: Disabled
kubectl 中对 HorizontalPodAutoscaler 的支持
HorizontalPodAutoscaler 像每个 API 资源一样,以标准方式由 kubectl 支持。您可以使用 kubectl create 命令创建一个新的自动伸缩器。您可以使用 kubectl get hpa 列出自动伸缩器,或使用 kubectl describe hpa 获取详细描述。最后,您可以使用 kubectl delete hpa 删除自动伸缩器。
此外,还有一个特殊的 kubectl autoscale 命令,用于创建 HorizontalPodAutoscaler 对象。例如,执行 kubectl autoscale rs foo --min=2 --max=5 --cpu-percent=80 将为 ReplicaSet foo 创建一个自动伸缩器,目标 CPU 利用率设置为 80%,副本数量在 2 到 5 之间。
隐式维护模式停用
您可以在不更改 HPA 配置本身的情况下,隐式地为目标停用 HPA。如果目标的期望副本计数设置为 0,并且 HPA 的最小副本计数大于 0,则 HPA 会停止调整目标(并在自身上将 ScalingActive 条件设置为 false),直到您通过手动调整目标的期望副本计数或 HPA 的最小副本计数来重新激活它。
将 Deployments 和 StatefulSets 迁移到水平自动伸缩
启用 HPA 后,建议从 Deployment 和/或 StatefulSet 的 清单 中删除 spec.replicas 的值。如果未这样做,则每次应用对该对象的更改时,例如通过 kubectl apply -f deployment.yaml,这将指示 Kubernetes 将当前 Pod 数量缩放到 spec.replicas 键的值。这可能不是期望的行为,并且当 HPA 处于活动状态时,可能会导致争用或剧烈波动行为。
请记住,删除 spec.replicas 可能会导致 Pod 计数的一次性下降,因为此键的默认值为 1(参考 Deployment Replicas)。在更新后,除了 1 个 Pod 之外的所有 Pod 都将开始其终止过程。此后的任何部署应用程序都将按预期行为并尊重滚动更新配置。您可以通过以下两种方法来避免这种下降,具体取决于您修改部署的方式
kubectl apply edit-last-applied deployment/<deployment_name>- 在编辑器中,删除
spec.replicas。保存并退出编辑器后,kubectl将应用更新。此时不会发生 Pod 计数的变化。 - 现在您可以从清单中删除
spec.replicas。如果您使用源代码管理,请提交您的更改或采取任何其他适当的步骤来修订源代码。 - 从现在开始,您可以运行
kubectl apply -f deployment.yaml
接下来
如果您在集群中配置了自动伸缩,您可能还希望考虑使用 节点自动伸缩,以确保您正在运行正确的节点数量。您还可以阅读更多关于 垂直 Pod 自动伸缩 的信息。
有关 HorizontalPodAutoscaler 的更多信息
- 阅读水平 Pod 自动伸缩的 演练示例。
- 阅读
kubectl autoscale的文档。 - 如果您想编写自己的自定义指标适配器,请查看 样板 以开始使用。
- 阅读 HorizontalPodAutoscaler 的 API 参考。