一个损失巨大的错误(关于Kubernetes和GKE)

【编者的话】云计算时代,对于Kubernetes来说,规模化的扩展带来了方便,但是规模化带来的成本也是不容小觑的,如何更低成本更高效的使用Kubernetes集群,本文将揭晓答案。

不幸的是,标题并没有夸张。作为免责声明,我首先要补充一点,这是一个非常愚蠢的错误,显示了我在管理自动伸缩部署方面缺乏经验。然而,这一切都始于一个没有答案的问题,我觉得有义务分享我的经验来帮助其他人避免类似的陷阱。

使用100x n1-standard-1 (1 vCPU) VMs的Kubernetes集群与使用1x n1-standard-96 (vCPU 96)或使用6x n1-standard-16 VMs (vCPU 16)的Kubernetes集群有什么区别?

这个问题我在Kubernetes社区问过很多次,没有人给出建设性的答案。如果你对答案不确定,那么你可以从我的经验中学到一些东西(如果没有耐心可以直接跳到答案部分)。

前提(序)

某一天,我半夜醒来,决定降低我们的基础设施成本。

我们正在运行一个大型Kubernetes集群。当然,“大”是相对的。在我们的例子中,正常工作时间是600个vcpu。这个数字在高峰时间翻倍,在晚上的某些时候接近于0。

上个月的发票是3500美元。

考虑到我们所获得的计算能力,这已经相当不错了,但是谷歌Kubernetes引擎(GKE)使成本管理变得非常简单:

  • 我们使用最便宜的数据中心(europe-west2 (London)就比europe-west4 (Netherlands)贵15%)
  • 我们为不同的部署使用不同的机器类型(偏重内存vs偏重CPU)
  • 我们使用水平Pod自动调度器(HPA)和自定义指标来扩展部署
  • 我们使用cluster-autoscaler来扩展节点池
  • 我们使用可抢占的vm

使用专门可抢占的vm使我们能够保持低成本。为了说明节省的费用,对于在europe-west4中托管的n1-standard-1机器类型,专属VM和可抢占VM之间的差异是26.73美元/月和8.03美元/月,这样成本就降低了3.25倍。当然,可抢占的vm有其局限性,你需要熟悉和克服这些局限性,但这是一个完全不同的主题。

有了以上所有的条件,我觉得我们正在做所有正确的事情来保持低成本。然而,我总是有一种挥之不去的感觉,觉得事情不对劲,不吐不快。

主要的红色标志

关于那种烦人的情况:

每个节点的平均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

其中,平均5m的pod是“空闲”的:队列中有一个任务等待它们处理,但是我们正在等待某些(外部)条件清除后再继续。在这种特殊的部署情况下,这些pods每分钟将在空闲/活动状态之间多次改变,并在空闲状态中花费70%以上的时间。

一分钟后,相同的pods看起来就不一样了:

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'

这意味着:

  • 闲置的pods消耗不超过20m
  • 活跃(健康)的pods在200m时达到峰值

然而,当我使用这个配置时,它使部署变得繁忙了。

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使用量较低的原因。但是,我不知道解决方案是什么(减少请求的资源会导致繁忙的集群状态/中断),因此我为所有部署提供了一种慷慨的资源分配变体。

答案

回到我的问题:

使用100x n1-standard-1 (1 vCPU) VMs的Kubernetes集群与使用1x n1-standard-96 (vCPU 96)或使用6x n1-standard-16 VMs (vCPU 16)的Kubernetes集群有什么区别?

对于初学者来说,n1-standard-1和n1-standard-96之间没有单个vcpu的价格差异。因此,我认为使用具有较少vcpu的机器将使我能够更好地控制价格。

我考虑的另一个问题是集群自动伸缩的速度有多快,也就是说,如果突然出现激增,集群自动伸缩器为计划外的pod提供新节点的速度有多快。不过,这并不是我们所关心的问题- -我们对资源的需求是逐渐增加和减少的。

所以我用了1个vCPU节点,结果我在前提中已经描述过了。

回顾过去,这是一个明显的错误:使用单个vCPU在节点上分布pod,在空闲和活动状态之间的单个部署更改时,不允许有效地利用资源。换句话说,在同一台机器上的vcpu越多,就可以将许多pod打包得越紧,因为当一部分pod超出了它们所需的配额时,就有现成的资源可供使用。

有效的工作是:

  • 我切换到16-vCPU机器,因为它们提供了一种平衡的解决方案,可以在自动扩展集群时进行良好的资源控制,也可以在每台机器上提供足够的资源来实现对处于空闲/活动状态的pod的严格调度。
  • 我使用的资源配置请求仅比空闲状态所需的资源多一点点,但是有很大的限制。当大多数pods处于空闲状态时,它允许在同一台机器上调度多个pods,但仍然允许资源密集型突发事件。
  • 我切换到n2机器类型:n2机器更贵,但是它们有2.8 GHz的基频(与n1-*机器可用的~2.2 GHz相比)。我们正在利用更高的时钟频率来尽可能快地处理资源密集型任务,并将pod尽可能快地置于前面描述的空闲状态。

当前节点vCPU的平均使用率高达60%。这听起来是对的,需要一些时间才能得出节省了多少。然而,惊喜的是,今天我们使用的vcpu还不到昨天的一半。

作者:Gajus Kuizinas。

原文链接: Mistake that cost thousands (Kubernetes, GKE)

译者:Mr.lzc,软件研发工程师、DevOpsDays深圳组织者&志愿者,目前供职于华为,从事云存储工作,以Cloud Native方式构建云文件系统服务,专注于K8s、微服务领域。