本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
计算和自动缩放
作为开发人员,您需要估算应用程序的资源需求,例如CPU和内存,但是如果您不不断调整它们,它们可能会过时,这可能会增加您的成本并降低性能和可靠性。持续调整应用程序的资源需求比第一次就正确调整它们更为重要。
下面提到的最佳实践将帮助您构建和运营成本感知型工作负载,从而实现业务成果,同时最大限度地降低成本,并使您的组织能够最大限度地提高投资回报。优化集群计算成本的高级重要性顺序是:
-
适当调整工作负载规模
-
减少未使用的容量
-
优化计算容量类型(例如 Spot)和加速器(例如 GPUs)
正确调整工作负载规模
在大多数 EKS 集群中,大部分成本来自用于运行容器化工作负载的 EC2 实例。如果不了解自己的工作负载需求,您将无法调整计算资源的大小。因此,您必须使用适当的请求和限制,并根据需要对这些设置进行调整。此外,实例大小和存储选择等依赖关系可能会影响工作负载性能,从而对成本和可靠性产生各种意想不到的后果。
请求应与实际利用率保持一致。如果容器的请求过高,就会出现未使用的容量,这是集群总成本的一个重要因素。Pod 中的每个容器(例如应用程序和 sidecar)都应设置自己的请求和限制,以确保集合 pod 限制尽可能准确。
使用 Goldilocks
我们建议使用水平容器自动扩缩器 (HPA) 来控制应运行应用程序的副本数量,使用垂直容器自动扩缩器 (VPA) 来调整应用程序每个副本所需的请求数量和限制,使用像 Karpent
Vertical Pod Autoscaler 可以调整分配给容器的请求和限制,从而使工作负载以最佳方式运行。你应该在审计模式下运行 VPA,这样它就不会自动进行更改和重启你的 pod。它将根据观察到的指标提出更改建议。对于影响生产工作负载的任何更改,您都应先在非生产环境中查看和测试这些更改,因为这些更改可能会影响应用程序的可靠性和性能。
减少消费
节省资金的最佳方法是配置更少的资源。实现这一目标的一种方法是根据工作负载的当前需求调整工作负载。在开始任何成本优化工作时,您都应确保您的工作负载定义其要求并动态扩展。这将需要从您的应用程序中获取指标并设置配置(例如PodDisruptionBudgets
Horistant Pod Autoscaler 是一款灵活的工作负载自动扩缩器,可以调整需要多少副本来满足应用程序的性能和可靠性要求。它有一个灵活的模型,可以根据各种指标(例如 CPU、内存或自定义指标(例如队列深度、与 Pod 的连接数等)来定义何时向上和向下扩展。
Kubernetes Metrics Server 支持根据内置指标(例如 CPU 和内存使用情况)进行扩展,但是如果您想根据其他指标(例如 Amazon CloudWatch 或 SQS 队列深度)进行扩展,则应考虑事件驱动的自动扩展项目,例如 KEDA。
减少工作负载消耗会在集群中造成容量过剩,通过适当的自动扩展配置,您可以自动缩减节点并减少总支出。我们建议您不要尝试手动优化计算容量。Kubernetes 调度器和节点自动扩缩器旨在为您处理此过程。
减少未使用的容量
在确定了应用程序的正确大小并减少了多余的请求之后,就可以开始减少预配置的计算容量了。如果您已经花时间根据上面的部分正确调整工作负载的大小,则应该能够动态地执行此操作。AWS 中有两个主节点自动扩缩器与 Kubernetes 配合使用。
Karpenter 和 Cluster 自动扩缩器
随着 Pod 的创建或移除以及计算要求的变化,Karpenter 和 Kubernetes 集群自动扩缩器都会扩展集群中的节点数量。两者的主要目标是一样的,但是 Karpenter 在节点管理配置和取消配置方面采用了不同的方法,这有助于降低成本并优化集群范围的使用率。
随着集群规模的扩大和工作负载种类的增加,预配置节点组和实例变得越来越困难。就像处理工作负载请求一样,设定初始基准并根据需要不断调整非常重要。
如果您使用的是集群自动扩缩器,它将尊重每个 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 在被移除之前会阻止终止。无论哪种情况,都将封锁该节点,以防止在该节点上安排其他工作。以下是显示如何设置注释的示例:
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 示例:
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
使用集群自动扩缩器和 Karpenter 标记节点
AWS 资源标签用于组织您的资源,并详细跟踪您的 AWS 成本。它们不与 Kubernetes 标签直接关联以进行成本跟踪。建议从 Kubernetes 资源标签开始,然后使用 Kubecos t 之类的工具根据容器、命名空间等上的 K
工作节点需要有标签才能在 AWS Cost Explorer 中显示账单信息。使用 Cluster Autoscaler,使用启动模板在托管节点组中标记您的工作节点。对于自我管理的节点组,请使用 a EC2 uto Scaling 组标记您的实例。对于 Karpenter 配置的实例,请在节点模板中使用 spec.tags对其进行标记。
多租户集群
在处理由不同团队共享的集群时,您可能无法看到在同一节点上运行的其他工作负载。虽然资源请求可以帮助隔离一些 “邻居噪音大” 的问题,例如 CPU 共享,但它们可能无法隔离所有资源边界,例如磁盘 I/O 耗尽。并非每个工作负载消耗的资源都可以被隔离或限制。应通过节点污点和容忍
在节点级别隔离工作负载可能会更昂贵,但可以通过使用预留实例
共享集群还可能存在集群级别的资源限制,例如 IP 耗尽、Kubernetes 服务限制或 API 扩展请求。您应查看可扩展性最佳实践指南,确保您的集群规避这些限制。
您可以在命名空间或 Karpenter 配置器级别隔离资源。资源配额
Karpenter Provisioners 可以对集群中的某些消耗资源(例如 CPU、GPU)设置限制
定时自动缩放
您可能需要在周末和非工作时间缩小集群。这对于测试和非生产集群尤其重要,在这些集群中,您希望在不使用时将其缩小到零。诸如集群关闭之类
优化计算容量类型
在优化了集群中的总计算容量并利用垃圾箱打包后,您应该查看在集群中预配置了哪种类型的计算以及如何为这些资源付费。AWS 的计算节省计划
-
Spot
-
节省计划
-
按需型
-
Fargate
每种容量类型在管理开销、可用性和长期承诺方面都有不同的权衡,您需要决定哪种容量类型适合您的环境。任何环境都不应依赖单一容量类型,您可以在单个集群中混合使用多种运行类型,以优化特定的工作负载要求和成本。
Spot
Spot
竞价计算应使用各种各样的实例类型,以降低竞价容量不可用的可能性。需要处理实例中断才能安全关闭节点。使用 Karpenter 或托管节点组的一部分配置的节点会自动支持实例中断通知。如果您使用的是自管理节点,则需要单独运行节点终止处理程序
可以在单个集群中平衡竞价实例和按需实例。借助 Karpenter,您可以创建加权配置器
以下是使用 Karpenter 将竞价型实例优先于按需实例的优先级的示例。创建置备程序时,您可以指定竞价型、按需型或两者兼而有之(如下所示)。当您同时指定两者时,如果 pod 没有明确指定需要使用 Spot 还是按需,则在使用分配策略配置节点时,Karpenter 会优先考虑 Spot。price-capacity-optimization
apiVersion: karpenter.sh/v1
kind: Provisioner
metadata:
name: spot-prioritized
spec:
requirements:
- key: "karpenter.sh/capacity-type"
operator: In
values: ["spot", "on-demand"]
Savings Plans、预留实例和 AWS EDP
您可以使用计算节省计划来减少计算
计算节省计划可以将 EC2 成本降低多达 66%,而无需承诺要使用的实例类型、系列或区域。在您使用实例时,节省的费用会自动应用于实例。
EC2 Instance Savings Plans 承诺使用特定地区和 EC2 系列(例如 C 系列的实例),可节省高达 72% 的计算费用。您可以将使用量转移到该区域内的任何可用区,使用任何一代的实例系列,例如 c5 或 c6,并使用该系列中任意大小的实例。折扣将自动应用于您账户中符合储蓄计划标准的任何实例。
预留实例
客户还可以选择与 AWS 签订企业协议。企业协议让客户可以选择定制最适合其需求的协议。客户可以享受基于 AWS EDP(企业折扣计划)的定价折扣。有关企业协议的更多信息,请联系您的 AWS 销售代表。
按需型
与现货相比,按需 EC2 实例具有无中断可用性的优势,而且与储蓄计划相比,无需长期承诺。如果您想降低集群成本,则应减少按需 EC2 实例的使用量。
优化工作负载要求后,您应该能够计算出集群的最小和最大容量。这个数字可能会随着时间的推移而变化,但很少会下降。考虑对低于最低限额的所有内容使用 Savings Plan,并选择不会影响应用程序可用性的容量。任何其他可能无法持续使用或可用性所必需的东西都可以按需使用。
如本节所述,减少使用量的最佳方法是消耗更少的资源,并最大限度地利用您配置的资源。使用集群自动扩缩器,您可以使用该设置删除未充分利用的节点。scale-down-utilization-threshold使用 Karpenter 时,建议启用整合。
要手动识别可用于您的工作负载的 EC2 实例类型,您应该使用 ec2-instance-selec
ec2-instance-selector --memory 4 --vcpus 2 --cpu-architecture x86_64 \ -r us-east-1 --service eks c5.large c5a.large c5ad.large c5d.large c6a.large c6i.large t2.medium t3.medium t3a.medium
对于非生产环境,您可以在夜间和周末等未使用的时间自动缩小集群。kubecost 项目集群关闭就是
Fargate 计算
Fargate 计算是 EKS 集群的完全托管计算选项。它通过在 Kubernetes 集群中为每个节点调度一个容器来提供容器隔离。它允许您根据工作负载的 CPU 和 RAM 要求调整计算节点的大小,从而严格控制集群中的工作负载使用情况。
Fargate 可以将工作负载扩展到 0.25 vCPU,内存为 0.5 GB,内存为 120 GB,内存可扩展至 16 个 vCPU。容器大小可用的变体数量有限制,您需要了解您的工作负载如何最适合 Fargate 配置。例如,如果您的工作负载需要 1 个 vCPU 和 0.5 GB 内存,那么最小的 Fargate 容器将是 1 个 vCPU 和 2 GB 内存。
尽管 Fargate 有许多好处,例如无需 EC2 实例或操作系统管理,但由于每个部署的 Pod 都作为集群中的单独节点隔离,因此它可能需要比传统 EC2 实例更多的计算容量。这需要对诸如 Kubelet、日志代理以及通常 DaemonSets 要部署到节点的任何东西进行更多的复制。 DaemonSets 在 Fargate 中不支持它们,需要将其转换为 pod “`sidecars” 并与应用程序一起运行。
Fargate 无法从 bin 打包或 CPU 过度配置中受益,因为工作负载的边界是一个不可突发的节点,也不能在工作负载之间共享。Fargate 将为您节省 EC2 实例管理时间,而这本身就是有成本的,但是 CPU 和内存成本可能比其他 EC2 容量类型更昂贵。Fargate pod 可以利用计算节省计划来降低按需成本。
优化计算使用率
在计算基础设施上节省资金的另一种方法是为工作负载使用更高效的计算。这可能来自性能更高的通用计算,例如 Graviton处理器
EKS 能够运行混合架构的集群(例如 amd64 和 arm64),如果您的容器是针对多个架构编译的,则可以通过在配置器中允许这两种架构来利用带有 Karpenter 的 Graviton 处理器。但是,为了保持稳定的性能,建议您将每个工作负载保持在单个计算架构上,并且仅在没有额外容量可用时才使用不同的架构。
Provisioner 可以配置多个架构,工作负载也可以在其工作负载规格中请求特定的架构。
apiVersion: karpenter.sh/v1 kind: Provisioner metadata: name: default spec: requirements: - key: "kubernetes.io/arch" operator: In values: ["arm64", "amd64"]
使用 Cluster Autoscaler,你需要为 Graviton 实例创建一个节点组,并对工作负载设置节点容忍度才能利用新的容量
GPUs 并 FPGAs 可以大大提高工作负载的性能,但是需要优化工作负载才能使用加速器。机器学习和人工智能的许多工作负载类型 GPUs 可用于计算,并且可以使用资源请求将实例添加到集群并安装到工作负载中。
spec: template: spec: - containers: ... resources: limits: nvidia.com/gpu: "1"
某些 GPU 硬件可以在多个工作负载之间共享,因此可以配置和使用单个 GPU。要了解如何配置工作负载 GPU 共享,请参阅虚拟 GPU 设备插件