计算和自动缩放 - Amazon EKS

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

计算和自动缩放

作为开发人员,您需要估算应用程序的资源需求,例如CPU和内存,但是如果您不不断调整它们,它们可能会过时,这可能会增加您的成本并降低性能和可靠性。持续调整应用程序的资源需求比第一次就正确调整它们更为重要。

下面提到的最佳实践将帮助您构建和运营成本感知型工作负载,从而实现业务成果,同时最大限度地降低成本,并使您的组织能够最大限度地提高投资回报。优化集群计算成本的高级重要性顺序是:

  1. 适当调整工作负载规模

  2. 减少未使用的容量

  3. 优化计算容量类型(例如 Spot)和加速器(例如 GPUs)

正确调整工作负载规模

在大多数 EKS 集群中,大部分成本来自用于运行容器化工作负载的 EC2 实例。如果不了解自己的工作负载需求,您将无法调整计算资源的大小。因此,您必须使用适当的请求和限制,并根据需要对这些设置进行调整。此外,实例大小和存储选择等依赖关系可能会影响工作负载性能,从而对成本和可靠性产生各种意想不到的后果。

请求应与实际利用率保持一致。如果容器的请求过高,就会出现未使用的容量,这是集群总成本的一个重要因素。Pod 中的每个容器(例如应用程序和 sidecar)都应设置自己的请求和限制,以确保集合 pod 限制尽可能准确。

使用 GoldilocksKRR 和 K ubecost 等工具来估算容器的资源请求和限制。根据应用程序的性质、性能/成本要求和复杂性,您需要评估哪些指标最适合扩展,应用程序性能在什么时候会下降(饱和点),以及如何相应地调整请求和限制。有关此主题的进一步指导,请参阅应用程序的正确尺寸

我们建议使用水平容器自动扩缩器 (HPA) 来控制应运行应用程序的副本数量,使用垂直容器自动扩缩器 (VPA) 来调整应用程序每个副本所需的请求数量和限制,使用像 Karpent er 或 Cluster Autoscaler 这样的节点自动扩缩器来持续调整集群中的节点总数。本文档的后面部分将记录使用 Karpenter 和 Cluster Autoscaler 的成本优化技术。

Vertical Pod Autoscaler 可以调整分配给容器的请求和限制,从而使工作负载以最佳方式运行。你应该在审计模式下运行 VPA,这样它就不会自动进行更改和重启你的 pod。它将根据观察到的指标提出更改建议。对于任何影响生产工作负载的更改,您都应先在非生产环境中查看和测试这些更改,因为这些更改可能会影响应用程序的可靠性和性能。

减少消费

节省资金的最佳方法是配置更少的资源。实现这一目标的一种方法是根据工作负载的当前需求调整工作负载。在开始任何成本优化工作时,您都应确保您的工作负载定义其要求并动态扩展。这将需要从您的应用程序中获取指标并设置配置(例如PodDisruptionBudgetsPod Readiness Gates),以确保您的应用程序可以安全地动态地向上和向下扩展。重要的是要考虑到,限制性 PodDisruptionBudgets 会阻止 Cluster Autoscaler 和 Karpenter 缩小节点规模,因为集群自动扩缩器和 Karpenter 都尊重 Karpenter。 PodDisruptionBudgets中的 “minAvailable” 值 PodDisruptionBudget 应始终低于部署中的容器数量,并且应在两者之间保持良好的缓冲区。例如,在 6 个 pod 的部署中,您希望始终至少运行 4 个 pod,请将您 PodDisruptionBidget 的 “minAvailable” 设置为 4。这将允许 Cluster Autoscaler 和 Karpenter 在节点缩减事件期间安全地从未充分利用的节点中排出 Pod 并将其驱逐出去。请参阅集群自动扩缩器常见问题解答文档

Horistant Pod Autoscaler 是一款灵活的工作负载自动扩缩器,可以调整需要多少副本来满足应用程序的性能和可靠性要求。它有一个灵活的模型,可以根据各种指标(例如 CPU、内存或自定义指标(例如队列深度、与 Pod 的连接数等)来定义何时向上和向下扩展。

Kubernetes Metrics Server 支持根据内置指标(例如 CPU 和内存使用情况)进行扩展,但是如果您想根据其他指标(例如 Amazon CloudWatch 或 SQS 队列深度)进行扩展,则应考虑事件驱动的自动扩展项目,例如 KEDA。请参阅此博客文章,了解如何将 KEDA 与 CloudWatch 指标配合使用。如果您不确定要根据哪些指标进行监控和扩展,请查看有关重要监控指标的最佳实践

减少工作负载消耗会在集群中造成容量过剩,通过适当的自动扩展配置,您可以自动缩减节点并减少总支出。我们建议您不要尝试手动优化计算容量。Kubernetes 调度器和节点自动扩缩器旨在为您处理此过程。

减少未使用的容量

在确定了应用程序的正确大小并减少了多余的请求之后,就可以开始减少预配置的计算容量了。如果您已经花时间根据上面的部分正确调整工作负载的大小,则应该能够动态地执行此操作。AWS 中有两个主节点自动扩缩器与 Kubernetes 配合使用。

Karpenter 和 Cluster 自动扩缩器

随着 Pod 的创建或移除,以及计算要求的变化,Karpenter 和 Kubernetes 集群自动扩缩器都会扩展集群中的节点数量。两者的主要目标是一样的,但是 Karpenter 在节点管理配置和取消配置方面采用了不同的方法,这有助于降低成本并优化集群范围的使用率。

随着集群规模的扩大和工作负载种类的增加,预配置节点组和实例变得越来越困难。就像处理工作负载请求一样,设定初始基准并根据需要不断调整非常重要。

如果您使用的是 Cluster Autoscaler,它将尊重每个 Auto Scaling 组 (ASG) 的 “最小” 和 “最大” 值,并且只调整 “所需值”。在为底层 ASG 设置这些值时务必注意,因为 Cluster Autoscaler 无法将超出 “最小” 数量的 ASG 缩小规模。将 “期望” 计数设置为正常工作时间所需的节点数,将 “最小” 设置为非工作时间所需的节点数。请参阅集群自动扩缩器常见问题解答文档

集群自动扩缩器优先级扩展器

Kubernetes Cluster Autoscaler 的工作原理是在应用程序向上和向下扩展时向上和向下扩展节点组(称为节点组)。如果您没有动态扩展工作负载,那么集群自动扩缩器将无法帮助您节省资金。Cluster Autoscaler 要求集群管理员提前创建节点组以供工作负载使用。节点组需要配置为使用具有相同 “配置文件” 的实例,即大致相同的 CPU 和内存量。

您可以有多个节点组,并且可以将 Cluster Autoscaler 配置为设置优先级扩展级别,并且每个节点组可以包含不同大小的节点。节点组可以有不同的容量类型,优先级扩展器可以先扩展成本较低的组。

以下是集群配置片段的示例,该片段在使用按需实例之前使用ConfigMap`来确定预留容量的优先级。您可以使用相同的方法将 Graviton 或 Spot 实例优先于其他类型。

apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: my-cluster managedNodeGroups: - name: managed-ondemand minSize: 1 maxSize: 7 instanceType: m5.xlarge - name: managed-reserved minSize: 2 maxSize: 10 instanceType: c5.2xlarge
apiVersion: v1 kind: ConfigMap metadata: name: cluster-autoscaler-priority-expander namespace: kube-system data: priorities: |- 10: - .*ondemand.* 50: - .*reserved.*

默认情况下,使用节点组可以帮助底层计算资源完成预期的事情,例如将节点分布在各处 AZs,但并非所有工作负载都有相同的要求或期望,最好让应用程序明确声明其需求。有关集群自动扩缩器的更多信息,请参阅最佳实践部分

Descheduler

Cluster Autoscaler 可以根据需要调度的新 Pod 或未充分利用的节点来增加和移除集群中的节点容量。在将 pod 调度到节点后,它不会全面了解 pod 的放置情况。如果您使用的是集群自动扩缩程序,则还应查看 Kubernetes 解调器,以避免浪费集群中的容量。

如果您的集群中有 10 个节点,并且每个节点的利用率为 60%,则您使用的容量不会占集群中预置容量的 40%。使用 Cluster Autoscaler,您可以将每个节点的利用率阈值设置为 60%,但只有在利用率降至 60% 以下之后,才会尝试缩小单个节点。

使用解调器,它可以在调度 pod 或将节点添加到集群后查看集群容量和利用率。它会尝试将集群的总容量保持在指定的阈值以上。它还可以根据节点污点或加入集群的新节点移除 Pod,以确保 Pod 在其最佳计算环境中运行。请注意,descheduler 不会安排更换已驱逐的 pod,而是依赖默认的调度器来完成此操作。

Karpenter 整合

Karpenter 采用 “无组” 方法进行节点管理。这种方法对于不同的工作负载类型更加灵活,并且需要更少的集群管理员预先配置。Karpenter 没有预先定义组并根据工作负载的需要扩展每个组,而是使用配置器和节点模板来广泛定义可以创建的 EC2 实例类型以及创建实例时的设置。

Bin packing 是一种通过将更多工作负载打包到更少、大小最优的实例上来利用更多实例资源的做法。虽然这仅通过配置工作负载使用的资源来帮助降低计算成本,但它需要权衡取舍。启动新工作负载可能需要更长时间,因为必须向集群添加容量,尤其是在大规模扩展活动期间。设置垃圾箱包装时,请考虑成本优化、性能和可用性之间的平衡。

Karpenter 可以持续监控和装箱,以提高实例资源利用率并降低计算成本。Karpenter 还可以为您的工作负载选择更具成本效益的工作节点。这可以通过在配置器中将 “合并” 标志设置为 true 来实现(下面的示例代码片段)。以下示例显示了一个支持整合的置备程序示例。在撰写本指南时,Karpenter 不会将正在运行的竞价型实例替换为更便宜的竞价型实例。有关 Karpenter 整合的更多详细信息,请参阅博客。

apiVersion: karpenter.sh/v1 kind: Provisioner metadata: name: enable-binpacking spec: consolidation: enabled: true

对于可能无法中断的工作负载,例如没有检查点的长时间运行的批处理作业,可以考虑使用注释为 pod 添加注释。do-not-evict通过选择 pod 退出驱逐,你是在告诉 Karpenter 它不应该自愿移除包含这个 pod 的节点。但是,如果在节点耗尽时将 do-not-evict Pod 添加到该节点,则其余的 pod 仍会被驱逐,但是该 Pod 在被移除之前会阻止终止。无论哪种情况,都将封锁该节点,以防止在该节点上安排其他工作。以下是显示如何设置注释的示例:

8"" linenumbering="unnumbered">apiVersion: v1 kind: Pod metadata: name: label-demo labels: environment: production annotations: + "karpenter.sh/do-not-evict": "true" spec: containers: * name: nginx image: nginx ports: ** containerPort: 80

通过调整集群自动扩缩程序参数来移除未充分利用的节点

节点利用率定义为请求的资源总和除以容量。默认情况下scale-down-utilization-threshold,设置为 50%。此参数可以与和一起使用scale-down-unneeded-time,后者决定了节点在有资格缩减之前应该不需要多长时间,默认值为 10 分钟。Kube-scheduler 会将仍在缩减的节点上运行的 Pod 调度到其他节点上。调整这些设置可以帮助移除未充分利用的节点,但请务必先测试这些值,这样就不会强迫集群过早缩小规模。

您可以确保驱逐成本高昂的 Pod 受到集群自动扩缩器识别的标签的保护,从而防止缩小规模的发生。为此,请确保驱逐成本高昂的 pod 具有注释cluster-autoscaler.kubernetes.io/safe-to-evict=false。以下是设置注释的 yaml 示例:

8"" linenumbering="unnumbered">apiVersion: v1 kind: Pod metadata: name: label-demo labels: environment: production annotations: + "cluster-autoscaler.kubernetes.io/safe-to-evict": "false" spec: containers: * name: nginx image: nginx ports: ** containerPort: 80