节省 AWS 上的 Kubernetes 云成本

文章的翻译是在课程开始前夕准备的 《基于Kubernetes的基础设施平台》.

节省 AWS 上的 Kubernetes 云成本

使用 Kubernetes 时如何节省云成本? 没有单一正确的解决方案,但本文介绍了几种可以帮助您更有效地管理资源并降低云计算成本的工具。

我写这篇文章时考虑的是适用于 AWS 的 Kubernetes,但它将以(几乎)完全相同的方式应用于其他云提供商。 我假设您的集群已经配置了自动缩放(集群自动缩放器)。 删除资源和缩小部署规模只有在减少工作节点(EC2 实例)群的情况下才能节省资金。

本文将涵盖:

清理未使用的资源

在快节奏的环境中工作非常棒。 我们需要科技组织 加速的。 更快的软件交付还意味着更多的 PR 部署、预览环境、原型和分析解决方案。 一切都部署在 Kubernetes 上。 谁有时间手动清理测试部署? 人们很容易忘记删除一周前的实验。 由于我们忘记关闭某些事情,云账单最终会增加:

节省 AWS 上的 Kubernetes 云成本

(亨宁·雅各布斯:
智杂:
(引述)科里·奎因:
误区:您的 AWS 账户取决于您拥有的用户数量。
事实:您的 AWS 分数取决于您拥有的工程师数量。

伊万·库尔诺索夫(回应):
事实:您的 AWS 分数是您忘记禁用/删除的事物数量的函数。)

Kubernetes 看门人 (kube-janitor) 帮助清理你的集群。 看门人配置对于全局和本地使用都很灵活:

  • 集群范围的规则可以定义 PR/测试部署的最大生存时间 (TTL)。
  • 单个资源可以使用 janitor/ttl 进行注释,例如在 7 天后自动删除尖峰/原型。

一般规则在 YAML 文件中定义。 它的路径是通过参数传递的 --rules-file 在 kube-janitor 中。 以下是删除所有命名空间的示例规则 -pr- 两天后的名字:

- id: cleanup-resources-from-pull-requests
  resources:
    - namespaces
  jmespath: "contains(metadata.name, '-pr-')"
  ttl: 2d

以下示例规范了 2020 年所有新 Deployments/StatefulSet 在 Deployment 和 StatefulSet Pod 上应用程序标签的使用,但同时允许在一周内不使用此标签来执行测试:

- id: require-application-label
  # удалить deployments и statefulsets без метки "application"
  resources:
    - deployments
    - statefulsets
  # см. http://jmespath.org/specification.html
  jmespath: "!(spec.template.metadata.labels.application) && metadata.creationTimestamp > '2020-01-01'"
  ttl: 7d

在运行 kube-janitor 的集群上运行 30 分钟的限时演示:

kubectl run nginx-demo --image=nginx
kubectl annotate deploy nginx-demo janitor/ttl=30m

成本增加的另一个来源是持久卷 (AWS EBS)。 删除 Kubernetes StatefulSet 不会删除其持久卷 (PVC - PersistentVolumeClaim)。 未使用的 EBS 卷很容易导致每月数百美元的成本。 Kubernetes Janitor 有一个功能可以清理未使用的 PVC。 例如,此规则将删除所有未由模块安装且未由 StatefulSet 或 CronJob 引用的 PVC:

# удалить все PVC, которые не смонтированы и на которые не ссылаются StatefulSets
- id: remove-unused-pvcs
  resources:
  - persistentvolumeclaims
  jmespath: "_context.pvc_is_not_mounted && _context.pvc_is_not_referenced"
  ttl: 24h

Kubernetes Janitor 可以帮助您保持集群清洁并防止云计算成本慢慢堆积。 有关部署和配置说明,请遵循 自述文件 kube-janitor.

减少非工作时间结垢

测试和登台系统通常只需要在工作时间内运行。 某些生产应用程序(例如后台/管理工具)也只需要有限的可用性,并且可能会在一夜之间被禁用。

Kubernetes 缩减器 (kube-downscaler) 允许用户和操作员在非工作时间缩小系统规模。 部署和 StatefulSet 可以扩展到零副本。 CronJobs 可能会被暂停。 Kubernetes Downscaler 是针对整个集群、一个或多个命名空间或单个资源进行配置的。 您可以设置“空闲时间”,也可以设置“工作时间”。 例如,要在夜间和周末尽可能减少缩放:

image: hjacobs/kube-downscaler:20.4.3
args:
  - --interval=30
  # не отключать компоненты инфраструктуры
  - --exclude-namespaces=kube-system,infra
  # не отключать kube-downscaler, а также оставить Postgres Operator, чтобы исключенными БД можно было управлять
  - --exclude-deployments=kube-downscaler,postgres-operator
  - --default-uptime=Mon-Fri 08:00-20:00 Europe/Berlin
  - --include-resources=deployments,statefulsets,stacks,cronjobs
  - --deployment-time-annotation=deployment-time

下面是周末扩展集群工作节点的图表:

节省 AWS 上的 Kubernetes 云成本

从大约 13 个工作节点缩减到 4 个工作节点肯定会对您的 AWS 账单产生显着影响。

但是如果我需要在集群“停机”期间工作怎么办? 通过添加 downscaler/exclude: true 注释,可以永久排除某些部署的扩展。 可以使用 downscaler/exclude-until 注释和格式为 YYYY-MM-DD HH:MM (UTC) 的绝对时间戳暂时排除部署。 如果有必要,可以通过部署带有注解的 pod 来缩减整个集群 downscaler/force-uptime,例如,通过启动 nginx 空白:

kubectl run scale-up --image=nginx
kubectl annotate deploy scale-up janitor/ttl=1h # удалить развертывание через час
kubectl annotate pod $(kubectl get pod -l run=scale-up -o jsonpath="{.items[0].metadata.name}") downscaler/force-uptime=true

自述文件 kube-downscaler,如果您对部署说明和其他选项感兴趣。

使用水平自动缩放

许多应用程序/服务处理动态加载模式:有时它们的模块空闲,有时它们满负荷工作。 运营永久的 Pod 队列来应对最大峰值负载并不经济。 Kubernetes 支持跨资源的水平自动缩放 水平Pod自动缩放器 (HPA)。 CPU 使用率通常是缩放的一个很好的指标:

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        averageUtilization: 100
        type: Utilization

Zalando 创建了一个组件来轻松连接自定义指标以进行扩展: Kube 指标适配器 (kube-metrics-adapter) 是 Kubernetes 的通用指标适配器,可以收集和提供用于 pod 水平自动缩放的自定义和外部指标。 它支持基于 Prometheus 指标、SQS 队列和其他设置的扩展。 例如,要将部署扩展到由应用程序本身表示为 /metrics 中的 JSON 的自定义指标,请使用:

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
  annotations:
    # metric-config.<metricType>.<metricName>.<collectorName>/<configKey>
    metric-config.pods.requests-per-second.json-path/json-key: "$.http_server.rps"
    metric-config.pods.requests-per-second.json-path/path: /metrics
    metric-config.pods.requests-per-second.json-path/port: "9090"
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Pods
    pods:
      metric:
        name: requests-per-second
      target:
        averageValue: 1k
        type: AverageValue

使用 HPA 配置水平自动缩放应该是提高无状态服务效率的默认操作之一。 Spotify 有一个演示,介绍了他们对 HPA 的经验和建议: 扩展您的部署,而不是您的钱包.

减少资源超额预订

Kubernetes 工作负载通过“资源请求”确定其 CPU/内存需求。 CPU 资源以虚拟核心或更常见的“毫核心”来衡量,例如 500m 意味着 50% vCPU。 内存资源以字节为单位,可以使用常见的后缀,如500Mi,表示500兆字节。 资源请求“锁定”工作节点上的容量,这意味着在具有 1000 个 vCPU 的节点上具有 4m CPU 请求的 Pod 将仅留下 3 个 vCPU 可供其他 Pod 使用。 [1]

松弛(超额储备) 是请求的资源和实际使用之间的差异。 例如,请求 2 GiB 内存但仅使用 200 MiB 的 Pod 具有约 1,8 GiB 的“过剩”内存。 过量需要花钱。 人们可以粗略估计 1 GiB 冗余内存的成本约为每月 10 美元。 [2]

Kubernetes 资源报告 (kube-resource-report) 显示超额储备并可以帮助您确定节省潜力:

节省 AWS 上的 Kubernetes 云成本

Kubernetes 资源报告 显示按应用程序和命令聚合的多余部分。 这使您可以找到可以减少资源需求的地方。 生成的 HTML 报告仅提供资源使用情况的快照。 您应该查看一段时间内的 CPU/内存使用情况,以确定足够的资源请求。 以下是“典型”CPU 密集型服务的 Grafana 图表:所有 Pod 所使用的 CPU 核心均明显少于 3 个请求的 CPU 核心:

节省 AWS 上的 Kubernetes 云成本

将 CPU 请求从 3000m 减少到约 400m 可以为其他工作负载释放资源,并允许集群更小。

“EC2 实例的平均 CPU 使用率通常徘徊在个位数百分比范围内,” 科里·奎因写道。 而对于 EC2 估计正确的尺寸可能是一个错误的决定在 YAML 文件中更改一些 Kubernetes 资源查询非常简单,并且可以带来巨大的节省。

但我们真的希望人们更改 YAML 文件中的值吗? 不,机器可以做得更好! 库伯内斯 垂直 Pod 自动缩放器 (VPA) 就是这样做的:根据工作负载调整资源请求和约束。 以下是 VPA 随着时间推移调整的 Prometheus CPU 请求(细蓝线)的示例图:

节省 AWS 上的 Kubernetes 云成本

Zalando 在其所有集群中都使用 VPA 用于基础设施组件。 非关键应用程序也可以使用 VPA。

金发 来自 Fairwind 的工具可以为命名空间中的每个部署创建 VPA,然后在其仪表板上显示 VPA 建议。 它可以帮助开发人员为其应用程序设置正确的 CPU/内存请求:

节省 AWS 上的 Kubernetes 云成本

我写了一个小 关于 VPA 的博文 2019 年以及最近 CNCF 最终用户社区讨论了 VPA 问题.

使用 EC2 Spot 实例

最后但并非最不重要的一点是,通过使用 Spot 实例作为 Kubernetes 工作节点可以降低 AWS EC2 成本 [3]。 与按需价格相比,现货实例的折扣高达 90%。 在 EC2 Spot 上运行 Kubernetes 是一个很好的组合:您需要指定几种不同的实例类型以获得更高的可用性,这意味着您可以以相同或更低的价格获得更大的节点,并且增加的容量可以由容器化 Kubernetes 工作负载使用。

如何在 EC2 Spot 上运行 Kubernetes? 有多种选择:使用 SpotInst 等第三方服务(现在称为“Spot”,不要问我为什么),或者只是将 Spot AutoScalingGroup (ASG) 添加到您的集群。 例如,以下是具有多种实例类型的“容量优化”Spot ASG 的 CloudFormation 代码段:

MySpotAutoScalingGroup:
 Properties:
   HealthCheckGracePeriod: 300
   HealthCheckType: EC2
   MixedInstancesPolicy:
     InstancesDistribution:
       OnDemandPercentageAboveBaseCapacity: 0
       SpotAllocationStrategy: capacity-optimized
     LaunchTemplate:
       LaunchTemplateSpecification:
         LaunchTemplateId: !Ref LaunchTemplate
         Version: !GetAtt LaunchTemplate.LatestVersionNumber
       Overrides:
         - InstanceType: "m4.2xlarge"
         - InstanceType: "m4.4xlarge"
         - InstanceType: "m5.2xlarge"
         - InstanceType: "m5.4xlarge"
         - InstanceType: "r4.2xlarge"
         - InstanceType: "r4.4xlarge"
   LaunchTemplate:
     LaunchTemplateId: !Ref LaunchTemplate
     Version: !GetAtt LaunchTemplate.LatestVersionNumber
   MinSize: 0
   MaxSize: 100
   Tags:
   - Key: k8s.io/cluster-autoscaler/node-template/label/aws.amazon.com/spot
     PropagateAtLaunch: true
     Value: "true"

有关将 Spot 与 Kubernetes 结合使用的一些注意事项:

  • 您需要处理 Spot 终止,例如通过在实例停止时合并节点
  • 扎兰多用途 具有节点池优先级的官方集群自动缩放
  • 现货节点 可以被强迫 接受在 Spot 中运行的工作负载的“注册”

总结

我希望您发现所提供的一些工具对于减少云费用很有用。 您还可以在以下位置找到本文的大部分内容 我在 2019 年 DevOps Gathering 上的 YouTube 和幻灯片演讲.

在 Kubernetes 上节省云成本的最佳实践是什么? 请让我知道 推特(@try_except_).

[1] 事实上,由于保留的系统资源减少了节点的吞吐量,因此仍然可用的 vCPU 数量将少于 3 个。 Kubernetes 区分物理节点容量和“预配置”资源(可分配节点).

[2] 计算示例:一个具有 5 GiB 内存的 m8.large 实例每月约 84 美元(eu-central-1,按需),即阻塞 1/8 节点的费用约为每月约 10 美元。

[3] 还有很多方法可以减少您的 EC2 账单,例如预留实例、储蓄计划等 - 我不会在这里讨论这些主题,但您绝对应该研究它们!

了解有关课程的更多信息。

来源: habr.com

添加评论