造成数千美元损失的错误(Kubernetes、GKE)重大危险信号🚩

2025-06-05

代价数千美元的错误(Kubernetes、GKE)

重大危险信号🚩

很遗憾,这并非夸张。需要声明的是,这是一个非常愚蠢的错误,暴露了我管理自动伸缩部署的经验不足。然而,这一切都源于一个没有答案的问题,我感到有义务分享我的经验,帮助其他人避免类似的陷阱。

n1-standard-1Kubernetes 集群使用 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) 使得成本管理变得非常简单:

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



Enter fullscreen mode Exit fullscreen mode

平均长度为 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



Enter fullscreen mode Exit fullscreen mode

综上所述,我认为进行如下配置是有意义的:



resources:
  requests:
    memory: '150Mi'
    cpu: '20m'
  limits:
    memory: '250Mi'
    cpu: '200m'



Enter fullscreen mode Exit fullscreen mode

这意味着:

  • 闲置的 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



Enter fullscreen mode Exit fullscreen mode

每当有新节点加入或退出集群时,就会发生这种情况(由于自动缩放,这种情况经常发生)。

因此,我不断增加请求的 pod 资源,直到最终得到此部署的以下配置:



resources:
  requests:
    memory: '150Mi'
    cpu: '100m'
  limits:
    memory: '250Mi'
    cpu: '500m'



Enter fullscreen mode Exit fullscreen mode

在这种配置下,集群运行顺畅,但这意味着即使是空闲的 Pod 也会被预先分配比其实际需要更多的 CPU 时间。这就是每个节点平均 CPU 使用率较低的原因。然而,我不知道该如何解决(减少资源请求会导致集群状态混乱/中断),因此我为所有部署都采用了一种慷慨的资源分配方案。

回答

回到我的问题:

n1-standard-1Kubernetes 集群使用 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-c​​ost-thousands-kubernetes-gke-46ij
PREV
函数式编程简介
NEXT
使用 Node.js 动态生成 SQL 查询