随着Kubernetes生态的不断壮大,一度被誉为新一代数据中心操作系统(DCOS),从资源角度来讲,K8S其核心工作也是管理整个集群的计算资源,并按需合理分配给系统里的程序(以Pod为基础的各种WorkLoad)。本质也是解决资源与业务负载之间供需平衡的问题。因此,了解Kubernetes自动扩展功能的相关原理,可以帮助我们在资源管理层面获得更多的价值,有利于提升运维效能。
伸缩对象?
在Kubernetes生态环境中,通常有两件关键事项需要进行弹性伸缩,以使得资源处于最优状态:
Pod:对于给定的应用程序,假设我们正在运行X副本,如果发出的请求超出X Pod池的处理能力,则为该应用程序扩展到多个X副本是一个好主意。为了使它无缝运行,我们的节点应具有足够的可用资源,以便可以成功调度和执行这些额外的Pod。这使我们进入了扩展规模的第二部分。
Node:所有节点的总容量代表集群的容量。如果工作负载需求超出了此容量,则必须将节点添加到集群中,并确保可以有效地计划和执行工作负载。如果Pod继续扩展,则有时节点可用的资源将耗尽,我们将不得不添加更多节点以增加集群级别的整体可用资源。
伸缩时机?
何时扩缩的决定包括两个部分:一个是连续测量某个指标,而该指标何时超过阈值,然后通过缩放特定资源对其进行操作。例如,我们可能想要测量我们的Pod的平均CPU消耗,然后在CPU消耗超过80%时触发定标操作。但是一个度量标准并不适合所有用例,对于不同类型的应用程序,该度量标准可能会有所不同。例如,对于消息队列,处于等待状态的消息数可能是适当的指标。对于内存密集型应用程序,内存消耗可能是该指标。如果我们有一个业务应用程序,对于给定的容量Pod,每秒处理大约1000个事务,那么我们可能要使用该指标并在Pod中的TPS达到850以上时进行扩展。
到目前为止,我们仅考虑了扩展部分,但是当工作负载使用率下降时,应该有一种方法可以适当地进行扩展,而不会引起正在处理的现有请求的中断。我们将在后面的部分中查看这些事情的实现细节。
如何伸缩?
对于Pod而言,只需在复制控制器中更改副本数即可。对于节点,若基于云平台,我们可以调用云提供商的API,创建一个新实例,并使它成为群集的一部分,只不过相对平常的操作,可能会花费更多时间。
Kubernetes弹性伸缩
了解了弹性伸缩后,让我们讨论Kubernetes弹性伸缩的特定实现和技术细节。
1、Cluster Autoscaler
集群自动扩缩器在Kubernetes中用于动态扩展集群(即节点)。它会持续监视Pod,如果发现Pod无法安排,则根据Pod Condition,选择扩展。这比查看节点的CPU总百分比要有效得多。由于创建节点最多可能需要一分钟或更长时间,具体取决于我们的云提供商和其他因素,因此可能需要一些时间才能安排Pod。在集群中,我们可能有多个节点池,例如,一个用于计费应用程序的节点池和另一个用于机器学习工作负载的节点池。此外,节点可以分布在区域中的各个可用区中,并且扩展方式可能会因拓扑而异。集群自动扩缩器提供各种标记和方法来调整节点扩缩行为。
为了缩减规模,需要查看该节点上的平均利用率,当然也可以依据其他因素。例如,如果具有节点中断预算的节点在无法重新调度的节点上运行,则无法从群集中剔除该节点。集群自动扩缩器提供了一种方法,可以优雅地终止节点,并且最多可以有10分钟的时间来移动Pod。
2、Horizontal Pod Autoscaler (HPA)
Pod水平自动扩缩器是一个控制回路,可监视和扩缩部署中的Pod。由控制器管理器的 --horizontal-pod-autoscaler-sync-period 参数指定周期(默认值为 15 秒)。每个周期内,控制管理器根据每个 HorizontalPodAutoscaler 定义中指定的指标查询资源利用率。控制管理器可以从资源度量指标 API(按 Pod 统计的资源用量)和自定义度量指标 API(其他指标)获取度量值。我们可以定义阈值以及部署应扩展到的最小和最大扩展。HPA的原始版本为GA(autoscaling / v1),仅支持将CPU作为可以监控的指标。Beta版的当前HPA版本(autoscaling / v2beta1)支持内存和其他自定义指标。我们来看下Pod 水平自动扩缩工作机制,如下图所示:
通常情况下,控制器将从一系列的聚合 API(metrics.k8s.io、custom.metrics.k8s.io 和 external.metrics.k8s.io)中获取度量值。创建HPA对象并能够查询该Pod的指标后,我们可以看到相关详细信息:
[administrator@JavaLangOutOfMemory k8s-master %]kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE helloetst-ownay28d Deployment/helloetst-ownay28d 8% / 60% 1 4 1 23h
我们可以通过向控制管理器添加标志来对水平pod自动缩放器的行为进行一些调整:
1、通过使用标志-horizontal-pod-autoscaler-sync-periodon控制管理器,确定hPa监视Pod池上给定指标的频率。默认同步周期为30秒。
2、两次操作之间的延迟默认为3分钟,可以使用标志-horizontal-pod-autoscaler-upscale-delay进行控制。
3、两次缩减操作之间的延迟默认情况下为5分钟,并且可以通过标志-horizontal-pod-autoscaler-downscale-delay进行调整。
关于指标, 在Kubernetes 1.9及更高版本中,API指标服务器是首选方法。
伸缩策略
从 v1.18 开始,v2beta2 API 允许通过 HPA 的 behavior 字段配置扩缩行为。在 behavior 字段中的 scaleUp 和 scaleDown 分别指定扩容和缩容行为。可以两个方向指定一个稳定窗口,以防止扩缩目标中副本数量的波动。类似地,指定扩缩策略可以控制扩缩时副本数的变化率。我们看一个关于默认的Demo行为,具体如下所示:
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 达到稳定状态。其他的策略可参考官网定义。
伸缩算法
从最基本的角度来看,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),将会放弃本次扩缩。
针对如下异常场景,算法策略如下:
1、如果 HorizontalPodAutoscaler 指定的是 targetAverageValue 或targetAverage
Utilization, 那么将会把指定 Pod 度量值的平均值做为 currentMetricValue。 然而,在检查容忍度和决定最终扩缩值前,我们仍然会把那些无法获取指标的 Pod 统计进去。
2、所有被标记了删除时间戳(Pod 正在关闭过程中)的 Pod 和失败的 Pod 都会被忽略。
3、如果某个 Pod 缺失度量值,它将会被搁置,只在最终确定扩缩数量时再考虑。
4、当使用 CPU 指标来扩缩时,任何还未就绪(例如还在初始化)状态的 Pod 或 最近的指标 度量值采集于就绪状态前的 Pod,该 Pod 也会被搁置。
5、由于受技术限制,Pod 水平扩缩控制器无法准确的知道 Pod 什么时候就绪, 也就无法决定是否暂时搁置该 Pod。--horizontal-pod-autoscaler-initial-readiness-delay 参数(默认为 30s)用于设置 Pod 准备时间, 在此时间内的 Pod 统统被认为未就绪。--horizontal-pod-autoscaler-cpu-initialization-period 参数(默认为5分钟) 用于设置 Pod 的初始化时间, 在此时间内的 Pod,CPU 资源度量值将不会被采纳。
最后,来一张简单的思维导图,可以帮助大家能够深入去了解、学习K8S的弹性伸缩机制以及其所涉及的关键要素。