水平 Pod 自动扩缩
在 Kubernetes 中,_HorizontalPodAutoscaler_ 会自动更新工作负载资源(例如 Deployment 或 StatefulSet),旨在根据需求自动扩缩工作负载。
水平扩缩意味着对增加的负载的响应是部署更多的 Pod。这与_垂直_扩缩不同,垂直扩缩对 Kubernetes 而言意味着为工作负载中已在运行的 Pod 分配更多资源(例如:内存或 CPU)。
如果负载减少,且 Pod 数量高于配置的最小值,HorizontalPodAutoscaler 会指示工作负载资源(Deployment、StatefulSet 或其他类似资源)缩减。
水平 Pod 自动扩缩不适用于无法扩缩的对象(例如:DaemonSet)。
HorizontalPodAutoscaler 以 Kubernetes API 资源和控制器的形式实现。该资源决定了控制器的行为。水平 Pod 自动扩缩控制器在 Kubernetes 控制平面中运行,定期调整其目标(例如 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),控制器从资源指标 API 获取 HorizontalPodAutoscaler 目标中每个 Pod 的指标。然后,如果设置了目标利用率值,控制器会将利用率值计算为每个 Pod 中容器上等效资源请求的百分比。如果设置了目标原始值,则直接使用原始指标值。然后,控制器会计算所有目标 Pod 的利用率或原始值(取决于指定的目标类型)的平均值,并生成一个用于扩缩所需副本数量的比率。
请注意,如果某些 Pod 的容器没有设置相关的资源请求,则 Pod 的 CPU 利用率将无法定义,并且自动扩缩器将不会对该指标采取任何操作。有关自动扩缩算法工作原理的更多信息,请参阅下面的算法详情部分。
对于每个 Pod 的自定义指标,控制器的工作方式与每个 Pod 的资源指标类似,只是它使用原始值,而不是利用率值。
对于对象指标和外部指标,会获取单个指标,该指标描述了相关对象。此指标与目标值进行比较,以产生如上所述的比率。在 `autoscaling/v2` API 版本中,此值在进行比较之前可以选择除以 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 控制器访问支持扩缩的相应工作负载资源(例如 Deployments 和 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 控制器在确定是否要搁置某些 CPU 指标时,无法精确地确定 Pod 第一次准备就绪的时间。相反,如果一个 Pod 处于未就绪状态,并且在启动后很短的可配置时间窗口内转换为就绪状态,则该 Pod 被视为“尚未准备就绪”。此值通过 `--horizontal-pod-autoscaler-initial-readiness-delay` 标志配置,默认值为 30 秒。一旦 Pod 准备就绪,如果它在启动后较长的可配置时间内转换为就绪状态,则任何转换为就绪状态都被视为第一次。此值通过 `--horizontal-pod-autoscaler-cpu-initialization-period` 标志配置,默认值为 5 分钟。
然后,使用上述未搁置或丢弃的剩余 Pod 计算 \( currentMetricValue \over desiredMetricValue \) 的基本扩缩比率。
如果存在任何缺失指标,控制平面会更保守地重新计算平均值,假设在缩减的情况下,这些 Pod 消耗了期望值的 100%,而在扩容的情况下消耗了 0%。这会抑制任何潜在扩缩的幅度。
此外,如果存在任何尚未准备就绪的 Pod,并且工作负载在不考虑缺失指标或尚未准备就绪的 Pod 的情况下会进行扩容,则控制器会保守地假设尚未准备就绪的 Pod 消耗了期望指标的 0%,从而进一步抑制扩容的幅度。
在考虑了尚未准备就绪的 Pod 和缺失指标之后,控制器会重新计算使用率。如果新比率反转了扩缩方向,或者在容差范围内,控制器将不采取任何扩缩操作。在其他情况下,新比率将用于决定 Pod 数量的任何更改。
请注意,平均利用率的_原始_值通过 HorizontalPodAutoscaler 状态报告,不考虑尚未准备就绪的 Pod 或缺失指标,即使使用了新的利用率比率。
如果在 HorizontalPodAutoscaler 中指定了多个指标,则对每个指标执行此计算,然后选择最大的所需副本计数。如果这些指标中的任何一个无法转换为所需副本计数(例如,由于从指标 API 获取指标时出错),并且可获取的指标建议缩减,则跳过扩缩。这意味着如果一个或多个指标的 `desiredReplicas` 大于当前值,HPA 仍然能够进行扩容。
最后,在 HPA 扩缩目标之前,会记录扩缩建议。控制器会考虑可配置窗口内的所有建议,选择该窗口内的最高建议。此值可以通过 `--horizontal-pod-autoscaler-downscale-stabilization` 标志配置,默认值为 5 分钟。这意味着缩减将逐步进行,从而平滑快速波动的指标值的影响。
Pod 就绪性和自动扩缩指标
HorizontalPodAutoscaler (HPA) 控制器包含两个标志,它们会影响启动期间从 Pod 收集 CPU 指标的方式
- `—horizontal-pod-autoscaler-cpu-initialization-period`(默认值:5 分钟)
这定义了 Pod 启动后的一段时间窗口,在此期间其**CPU 使用率将被忽略**,除非:- Pod 处于 `Ready` 状态**并且**- 指标样本完全是在 Pod 处于 `Ready` 状态期间采集的。
此标志有助于 HPA 扩缩决策中**排除初始化 Pod(例如,Java 应用程序预热)中误导性的高 CPU 使用率**。
- `—horizontal-pod-autoscaler-initial-readiness-delay`(默认值:30 秒)
这定义了 Pod 启动后的一个短延迟期,在此期间,HPA 控制器会将当前处于 `Unready` 状态的 Pod 视为仍在初始化,**即使它们之前曾短暂转换为 `Ready` 状态**。
其目的是:- 避免在启动期间包含在 `Ready` 和 `Unready` 之间快速切换的 Pod。- 确保初始就绪信号的稳定性,然后 HPA 才认为其指标有效。
关键行为
- 如果 Pod 处于 `Ready` 状态并保持 `Ready` 状态,即使在延迟期内,它也可以被计入指标贡献。
- 如果 Pod 在 `Ready` 和 `Unready` 之间快速切换,则在它被认为稳定 `Ready` 之前,指标将被忽略。
最佳实践
如果你的 Pod 在启动阶段有较高的 CPU 使用率
- 配置一个 `startupProbe`,只有在 CPU 使用率高峰过去后才通过,或者
- 使用 `initialDelaySeconds` 确保你的 `readinessProbe` 仅在 CPU 峰值消退**后**报告 `Ready`。
理想情况下,还要将 `—horizontal-pod-autoscaler-cpu-initialization-period` 设置为**覆盖启动持续时间**。
API 对象
Horizontal Pod Autoscaler 是 Kubernetes `autoscaling` API 组中的一个 API 资源。当前稳定版本可以在 `autoscaling/v2` API 版本中找到,该版本支持基于内存和自定义指标的扩缩。在处理 `autoscaling/v1` 时,`autoscaling/v2` 中引入的新字段将作为注解保留。
创建 HorizontalPodAutoscaler API 对象时,请确保指定的名称是有效的DNS 子域名。有关 API 对象的更多详细信息,请参阅HorizontalPodAutoscaler 对象。
工作负载扩缩的稳定性
当使用 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 [stable]
(默认启用:true)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 [stable]
(`autoscaling/v2beta2` API 版本以前将此功能作为 Beta 功能提供)
假设你使用 `autoscaling/v2` API 版本,你可以配置 HorizontalPodAutoscaler 以基于自定义指标(即未内置到 Kubernetes 或任何 Kubernetes 组件中的指标)进行扩缩。HorizontalPodAutoscaler 控制器然后从 Kubernetes API 查询这些自定义指标。
有关要求,请参阅对指标 API 的支持。
基于多个指标进行扩缩
Kubernetes v1.23 [stable]
(`autoscaling/v2beta2` API 版本以前将此功能作为 Beta 功能提供)
如果你使用 `autoscaling/v2` API 版本,你可以为 HorizontalPodAutoscaler 指定多个指标以进行扩缩。然后,HorizontalPodAutoscaler 控制器会评估每个指标,并根据该指标提出新的扩缩建议。HorizontalPodAutoscaler 会取每个指标推荐的最大扩缩量,并将工作负载设置为该大小(前提是这不大于你配置的总体最大值)。
对指标 API 的支持
默认情况下,HorizontalPodAutoscaler 控制器从一系列 API 检索指标。为了使其能够访问这些 API,集群管理员必须确保:
API 聚合层已启用。
相应的 API 已注册
对于资源指标,这是 `metrics.k8s.io` API,通常由 metrics-server 提供。它可以作为集群附加组件启动。
对于自定义指标,这是 `custom.metrics.k8s.io` API。它由指标解决方案供应商提供的“适配器”API 服务器提供。请咨询你的指标管道,看看是否有可用的 Kubernetes 指标适配器。
对于外部指标,这是 `external.metrics.k8s.io` API。它可能由上述提供的自定义指标适配器提供。
有关这些不同指标路径及其区别的更多信息,请参阅 HPA V2、custom.metrics.k8s.io 和 external.metrics.k8s.io 的相关设计提案。
有关如何使用它们的示例,请参阅使用自定义指标的演练和使用外部指标的演练。
可配置的扩缩行为
Kubernetes v1.23 [stable]
(`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)允许在 1 分钟内最多缩减 4 个副本。第二个策略(Percent)允许在 1 分钟内最多缩减当前副本的 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.33 [alpha]
(默认启用:false)`tolerance` 字段配置了指标变化的阈值,防止自动扩缩器对低于该值的变化进行扩缩。
此容差定义为期望指标值周围的变化量,在该变化量以下不会发生扩缩。例如,考虑一个配置了目标内存消耗为 100MiB 和扩容容差为 5% 的 HorizontalPodAutoscaler。
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 副本)。更新后,除 1 个 Pod 外,所有 Pod 都将开始其终止过程。此后的任何部署应用程序都将正常运行并遵循所需的滚动更新配置。你可以根据修改部署的方式,通过以下两种方法之一避免这种降级:
kubectl apply edit-last-applied deployment/<deployment_name>
- 在编辑器中,删除 `spec.replicas`。当你保存并退出编辑器时,`kubectl` 会应用更新。此步骤不会发生 Pod 计数的更改。
- 现在你可以从清单中删除 `spec.replicas`。如果你使用源代码管理,还要提交你的更改,或根据你跟踪更新的方式采取任何其他修订源代码的适当步骤。
- 从现在开始,你可以运行 `kubectl apply -f deployment.yaml`
下一步
如果你在集群中配置自动扩缩,你可能还需要考虑使用节点自动扩缩,以确保你运行了正确数量的节点。
有关 HorizontalPodAutoscaler 的更多信息
- 阅读水平 Pod 自动扩缩的演练示例。
- 阅读 `kubectl autoscale` 的文档。
- 如果你想编写自己的自定义指标适配器,请查看样板以开始使用。
- 阅读 HorizontalPodAutoscaler 的API 参考。