关于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美元。
1.png

考虑到我们所获得的计算能力,这已经相当不错了,但是谷歌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是“空闲”的:队列中有一个任务等待它们处理,但是我们正在等待某些(外部)条件清除后再继续。在这种特殊的部署情况下,这些Pod每分钟将在空闲/活动状态之间多次改变,并在空闲状态中花费70%以上的时间。

一分钟后,相同的Pod看起来就不一样了:
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消耗不超过20m
  • 活跃(健康)的Pod在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的严格调度。
  • 我使用的资源配置请求仅比空闲状态所需的资源多一点点,但是有很大的限制。当大多数Pod处于空闲状态时,它允许在同一台机器上调度多个Pod,但仍然允许资源密集型突发事件。
  • 我切换到n2机器类型:n2机器更贵,但是它们有2.8 GHz的基频(与n1-*机器可用的~2.2 GHz相比)。我们正在利用更高的时钟频率来尽可能快地处理资源密集型任务,并将Pod尽可能快地置于前面描述的空闲状态。


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

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

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

0 个评论

要回复文章请先登录注册