代价数千美元的错误(Kubernetes、GKE)
重大危险信号🚩
很遗憾,这并非夸张。需要声明的是,这是一个非常愚蠢的错误,暴露了我管理自动伸缩部署的经验不足。然而,这一切都源于一个没有答案的问题,我感到有义务分享我的经验,帮助其他人避免类似的陷阱。
n1-standard-1
Kubernetes 集群使用 100 个(1 个 vCPU)虚拟机与使用 1 个n1-standard-96
(vCPU 96)或 6 个n1-standard-16
虚拟机(vCPU 16)之间有什么区别?
我在 Kubernetes 社区多次问过这个问题,但没有人给出答案。如果你不确定答案,可以从我的经验中学习一些东西(或者,如果你没有耐心,可以直接跳到答案部分)。答案如下:
前提
我半夜醒来,决心要降低我们的基础设施成本。
我们正在运行一个大型 Kubernetes 集群。当然,“大型”是相对的。在我们的案例中,正常工作时间内有 600 个 vCPU。高峰时段,这个数字会翻倍,夜间某些时段则接近 0。
上个月的发票金额为 3,500 美元。
考虑到我们所获得的计算能力,这已经相当不错了,而且 Google Kubernetes Engine (GKE) 使得成本管理变得非常简单:
- 我们使用最便宜的数据中心(
europe-west2
(伦敦)比europe-west4
(荷兰)贵约 15%) - 我们针对不同的部署使用不同的机器类型(内存密集型 vs CPU 密集型)
- 我们使用水平 Pod 自动缩放器 (HPA) 和自定义指标来扩展部署
- 我们使用集群自动缩放器(https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-autoscaler)来扩展节点池
- 我们使用可抢占虚拟机
n1-standard-1
使用专用抢占式虚拟机使我们能够降低成本。为了说明节省成本,以托管在 的机器类型为例europe-west4
,专用虚拟机和抢占式虚拟机之间的差异为每月 26.73 美元 vs 每月 8.03 美元。这意味着成本降低了 3.25 倍。当然,抢占式虚拟机有其局限性,您需要熟悉并克服这些局限性,但这是另一个话题。
以上所有措施都到位后,我们感觉所有措施都正确,可以有效降低成本。然而,我总感觉哪里不对劲。
重大危险信号🚩
关于那种挥之不去的感觉:
每个节点的平均 CPU 使用率较低(10%-20%)。这似乎不太对劲。
我首先想到的是我错误地配置了计算资源。所需的资源完全取决于你正在运行的程序。因此,最好的做法是在没有资源限制的情况下部署你的程序,观察程序在空闲/常规负载和峰值负载下的表现,并根据观察到的值设置请求/限制资源。
我将通过单个部署“admdesl”的示例来说明我的错误。
在我们的案例中,资源需求是零星的:
NAME CPU(cores) MEMORY(bytes)
admdesl-5fcfbb5544-lq7wc 3m 112Mi
admdesl-5fcfbb5544-mfsvf 3m 118Mi
admdesl-5fcfbb5544-nj49v 4m 107Mi
admdesl-5fcfbb5544-nkvk9 3m 103Mi
admdesl-5fcfbb5544-nxbrd 3m 117Mi
admdesl-5fcfbb5544-pb726 3m 98Mi
admdesl-5fcfbb5544-rhhgn 83m 119Mi
admdesl-5fcfbb5544-rhp76 2m 105Mi
admdesl-5fcfbb5544-scqgq 4m 117Mi
admdesl-5fcfbb5544-tn556 49m 101Mi
admdesl-5fcfbb5544-tngv4 2m 135Mi
admdesl-5fcfbb5544-vcmjm 22m 106Mi
admdesl-5fcfbb5544-w9dsv 180m 100Mi
admdesl-5fcfbb5544-whwtk 3m 103Mi
admdesl-5fcfbb5544-wjnnk 132m 110Mi
admdesl-5fcfbb5544-xrrvt 4m 124Mi
admdesl-5fcfbb5544-zhbqw 4m 112Mi
admdesl-5fcfbb5544-zs75s 144m 103Mi
平均长度为 5 米的 Pod 处于“空闲”状态:队列中有一个任务需要它们处理,但我们正在等待某些(外部)条件清除后再继续处理。在这种特定的部署中,这些 Pod 每分钟会在空闲/活动状态之间切换多次,并且 70% 以上的时间处于空闲状态。
一分钟后,同一组豆荚看起来会有所不同:
NAME CPU(cores) MEMORY(bytes)
admdesl-5fcfbb5544-lq7wc 152m 107Mi
admdesl-5fcfbb5544-mfsvf 49m 102Mi
admdesl-5fcfbb5544-nj49v 151m 116Mi
admdesl-5fcfbb5544-nkvk9 105m 100Mi
admdesl-5fcfbb5544-nxbrd 160m 119Mi
admdesl-5fcfbb5544-pb726 6m 103Mi
admdesl-5fcfbb5544-rhhgn 20m 109Mi
admdesl-5fcfbb5544-rhp76 110m 103Mi
admdesl-5fcfbb5544-scqgq 13m 120Mi
admdesl-5fcfbb5544-tn556 131m 115Mi
admdesl-5fcfbb5544-tngv4 52m 113Mi
admdesl-5fcfbb5544-vcmjm 102m 104Mi
admdesl-5fcfbb5544-w9dsv 18m 125Mi
admdesl-5fcfbb5544-whwtk 173m 122Mi
admdesl-5fcfbb5544-wjnnk 31m 110Mi
admdesl-5fcfbb5544-xrrvt 91m 126Mi
admdesl-5fcfbb5544-zhbqw 49m 107Mi
admdesl-5fcfbb5544-zs75s 87m 148Mi
综上所述,我认为进行如下配置是有意义的:
resources:
requests:
memory: '150Mi'
cpu: '20m'
limits:
memory: '250Mi'
cpu: '200m'
这意味着:
- 闲置的 Pod 消耗的内存不超过 20 兆
- 活跃(健康)的豆荚峰值位于 200 米处
然而,当我使用这种配置时,它使部署变得忙乱。
admdesl-78fc6f5fc9-xftgr 0/1 Terminating 3 21m
admdesl-78fc6f5fc9-xgbcq 0/1 Init:CreateContainerError 0 10m
admdesl-78fc6f5fc9-xhfmh 0/1 Init:CreateContainerError 1 9m44s
admdesl-78fc6f5fc9-xjf4r 0/1 Init:CreateContainerError 0 10m
admdesl-78fc6f5fc9-xkcfw 0/1 Terminating 0 20m
admdesl-78fc6f5fc9-xksc9 0/1 Init:0/1 0 10m
admdesl-78fc6f5fc9-xktzq 1/1 Running 0 10m
admdesl-78fc6f5fc9-xkwmw 0/1 Init:CreateContainerError 0 9m43s
admdesl-78fc6f5fc9-xm8pt 0/1 Init:0/1 0 10m
admdesl-78fc6f5fc9-xmhpn 0/1 CreateContainerError 0 8m56s
admdesl-78fc6f5fc9-xn25n 0/1 Init:0/1 0 9m6s
admdesl-78fc6f5fc9-xnv4c 0/1 Terminating 0 20m
admdesl-78fc6f5fc9-xp8tf 0/1 Init:0/1 0 10m
admdesl-78fc6f5fc9-xpc2h 0/1 Init:0/1 0 10m
admdesl-78fc6f5fc9-xpdhr 0/1 Terminating 0 131m
admdesl-78fc6f5fc9-xqflf 0/1 CreateContainerError 0 10m
admdesl-78fc6f5fc9-xrqjv 1/1 Running 0 10m
admdesl-78fc6f5fc9-xrrwx 0/1 Terminating 0 21m
admdesl-78fc6f5fc9-xs79k 0/1 Terminating 0 21m
每当有新节点加入或退出集群时,就会发生这种情况(由于自动缩放,这种情况经常发生)。
因此,我不断增加请求的 pod 资源,直到最终得到此部署的以下配置:
resources:
requests:
memory: '150Mi'
cpu: '100m'
limits:
memory: '250Mi'
cpu: '500m'
在这种配置下,集群运行顺畅,但这意味着即使是空闲的 Pod 也会被预先分配比其实际需要更多的 CPU 时间。这就是每个节点平均 CPU 使用率较低的原因。然而,我不知道该如何解决(减少资源请求会导致集群状态混乱/中断),因此我为所有部署都采用了一种慷慨的资源分配方案。
回答
回到我的问题:
n1-standard-1
Kubernetes 集群使用 100 个(1 个 vCPU)虚拟机与使用 1 个n1-standard-96
(vCPU 96)或 6 个n1-standard-16
虚拟机(vCPU 16)之间有什么区别?
n1-standard-1
首先,和之间没有每 vCPU 价格差异n1-standard-96
。因此,我认为使用 vCPU 数量较少的机器可以让我更精细地控制价格。
我考虑的另一个问题是集群的自动扩展速度,也就是说,如果出现突然激增的情况,集群自动扩展器能多快为未调度的 Pod 提供新的节点。不过这倒不是问题——我们的资源需求是逐渐增长和减少的。
因此我主要采用 1 个 vCPU 节点,其结果我在前提中已经描述过。
回想起来,这是一个明显的错误:将 Pod 分布在单个 vCPU 的节点上,无法实现高效的资源利用率,因为单个部署会在空闲和活动状态之间切换。换句话说,同一台机器上的 vCPU 越多,Pod 就能越紧密地打包,因为当一部分 Pod 超出其所需配额时,就会有可用的资源可供使用。
有效的方法:
- 我切换到 16 个 vCPU 机器,因为它们在自动扩展集群时的精细资源控制和每台机器的充足资源之间提供了平衡的解决方案,从而可以对处于空闲/活动状态的 pod 进行紧密调度。
- 我使用的资源配置请求的资源量仅略高于空闲状态下所需的资源量,但限制也较为宽松。当大多数 Pod 处于空闲状态时,它允许在同一台机器上调度多个 Pod,但仍然允许资源密集型的突发情况。
- 我切换到了n2机器类型:n2 机器价格更高,但它们的基本频率为 2.8 GHz(而
n1-*
其他机器的可用频率约为 2.2 GHz)。我们利用更高的时钟频率来尽快处理资源密集型任务,并尽快将 Pod 置于前面描述的空闲状态。
当前 Node vCPU 的平均利用率高达 60%。这听起来还不错。要确定节省了多少资源,还需要一些时间。然而,今天我们使用的 vCPU 数量已经不到昨天同期的一半了。
文章来源:https://dev.to/gajus/mistake-that-cost-thousands-kubernetes-gke-46ij