基于应用负载动态管理CPU、内存等资源的使用是云原生架构的最佳实践之一,Kubernetes通过资源请求和限制以及HPA/VPA等机制,为云原生应用资源管理提供了很好的支持。原文: Infinite Scaling with Containers and Kubernetes[1]
如果没有足够资源让容器完成工作,那么即使把世界上最好的代码容器化也没有任何意义。
我在写关于容器状态探测的文章时产生了这篇文章的想法,很明显,对kubelet[2]查询返回准确响应只是解决方案的第一部分。
第二部分是本文的主题: 为容器分配资源。我们需要探索资源分配的所有领域,从Kubernetes pod中的单个容器直到云上近乎无限的容量。
Kubernetes资源管理: 概述
"如何确保容器始终拥有所需资源?"
本节只是为刚开始使用Kubernetes管理资源的人简单介绍一下资源管理,关于命名空间限制范围[3]和Kubernetes容器资源管理[4]的官方文档仍然是必读资料,如果你对这一主题比较熟悉,可以跳到下一部分。
Kubernetes资源管理主要包括CPU、内存和**本地临时存储**[5],容器规范可以为每种资源类型定义资源请求和资源限制[6]。
资源请求(resource request) 是容器运行时所需的最小资源量,节点调度器只有在节点满足pod中所有容器的最小资源请求时,才会将pod调度到该节点上。
资源限制(resource limit) 是kubelet在其生命周期内应该分配给容器的最大资源量。当容器设置了资源限制但没有资源请求时,相当于将资源请求设置为与资源限制相同的值。
容器可以设置资源请求和限制的任意组合,CPU和内存预留的不同组合定义了父pod的服务质量[7],如下所示:
-
pod中每个容器都有资源请求和限制,并且每个容器的请求和限制具有相同的值: Guaranteed。 -
pod中至少有一个容器没有设置资源请求或限制: BestEfford。 -
不属于前两种的pod: Burstable。
当kube-scheduler[8]决定在工作节点上调度某个pod时,了解这些类别的含义会很有帮助:
-
"Guaranteed"意味着调度程序只将pod分配给具有足够资源的工作节点,以满足pod中所有容器的请求。 -
"Burstable"意味着调度程序会寻找具有足够内存的工作节点来满足pod中所有容器的资源请求, 调度器并不关心工作节点是否能够满足pod中容器的资源限制或资源请求之上的任何限制。 -
"BestEffort"是指调度程序在调度"BestEffort" pod之前,将首先满足"Guaranteed"或"Burstable" pod的资源请求。调度器会不断重新评估,如果"Guaranteed"或"Burstable" pod需要资源,可能会 标记"BestEffort" pod以便驱逐。
决策时间:谁来定义容器资源?
首先需要理解,资源请求是用来保护容器的,而资源限制是用来保护集群的。
做出决策的主要参与方是产品开发人员和系统管理员,由于决策会随着时间的推移而改变,因此他们之间的"谈判"充满挑战:
-
产品开发人员将大部分时间用于建立与部署与生产环境隔离的资源边界。 -
系统管理员通常在处理 Kubernetes工作负载 [9]规范时缺乏足够的文档说明资源请求和限制的变化会如何影响工作负载。
产品开发人员可以更好的了解工作负载的需求及其在不同资源分配下的行为,一般来说,应该能够在发布前确定以下阈值:
-
绝对最小(Absolute minimum)资源请求。这个值是加载运行时所需的资源,并且能够轻松响应来自kubelet的探测请求。 -
最大可用(Maximum usable)资源限制。这个值是容器的性能开始遇到外部限制的点,比如磁盘I/O或调用第三方服务的瓶颈。
理想情况下,产品开发人员还应该建立并记录容器性能在边界内的变化情况,这些信息可以帮助系统架构师在部署之前规划初始容量,并使系统管理员能够调整生产环境中的资源请求。
配额、范围和公地悲剧
系统管理员可以查看集群容量及可用容量。作为偶尔兼顾SRE的人,我比较欣赏"Guaranteed"工作负载。对于组织来说,在pod中所有容器上设置固定的资源边界并信任pod可以在边界内可靠工作需要特殊的信心(以及大量测试)。
"Burstable"工作负载在信任范围内的排名略低。当我做SRE时,最大的希望是在burstable pod中的所有容器都设置了上一节提到的"绝对最小"资源请求。
关于burstable工作负载的资源限制,我的观点来自资源限制背后的开发理念:
-
(好的理念) 在工作负载内进行自动伸缩(例如,启动具有最小和最大内存限制的Java虚拟机)。这种安排(资源限制设置高于资源请求)意味着开发团队花时间确定了工作负载的可用操作范围,并能很好的控制和管理资源。 -
(不好的理念) 防止最终错误。这种设计理念让我想起了" 公地悲剧 [10]",即每个人都预期资源最终将被耗尽,因此需要占有更多共享资源,从而加速了共享资源的耗尽。当开发团队低估了资源需求时,这种理念尤其具有灾难性,在这种情况下,工作负载持续在其资源开销的突发区域内运行,使其容易受到CPU节流、内存耗尽和工作节点kubelet终止的影响。
最后留下来的是"BestEffort"工作负载,它们不受约束的耗尽集群资源的能力,以及面对其他请求相同资源的pod时脆弱的调度状态,让我们时刻担心集群的运行。
在处理这些类型的工作负载时,系统管理员必须花时间考虑以下选项:
-
用 限制范围(limit ranges) [11]为命名空间中的容器和pod设置默认资源请求和限制。我认为,对跨命名空间的工作负载分配相同的资源限制,再加上缺乏产品开发人员的投入,让这一主张并不现实。 -
用 资源配额(resource quotas) [12]限制命名空间中所有pod的组合资源。这种方法是更有前途的解决方案,减少了最终容器对该命名空间失控的"爆炸半径",优点是避免了资源安全缓冲区叠加命名空间中pod数量所造成的浪费。 -
深吸一口气,我们有望从下一节中介绍的pod自动缩放技术中解脱出来。
Pod自动缩放: 超越静态大小
许多读者可能已经准备好提出处理难以控制的工作负载的常用方法:
-
Horizontal pod autoscaling (HPA) [13] -
Vertical pod autoscaling (VPA) [14]
这两种流行的自动扩容技术可以根据资源指标在集群内动态分配容量。HPA可以更改部署pod副本的数量,而VPA可以更改pod的资源请求和限制。注意,在Kubernetes增强建议[15]添加到Kubernetes之前,VPA对pod的更改会导致pod重启。
虽然能够自动扩展容量,但这些组件并不能在缺乏对工作负载的操作范围的精确定义。例如,如果pod中的容器没有对该度量的资源请求,HPA将不会对该度量采取行动。HPA也不适合部署依赖于集群中pod的特定数量的副本的场景。
对于需要固定数量副本的工作负载或没有预设限制的工作负载,使用VPA效果更好。不过会有一个相对较长的限制列表[16]。此外,如果设置限制范围[17],则需要考虑的因素[18]可能会非常复杂。
这些工具在自动伸缩工作负载方面最终是否有效取决于开发期间经过充分研究的资源请求和限制的分析工作。在这方面我必须给予VPA额外的信任,因为它能够"学习"部署的资源范围。
自动化运维operator: 即开即用的SRE?
Kubernetes operator[19]引入了自定义资源的概念来管理应用程序和工作负载。从概念上讲,operator持续监控自定义资源并将其内容映射到工作负载资源[20]。
Operator成熟度模型[21]定义了五个不同级别的自动化,最高是5级,即"自动驾驶(Auto Pilot)"。处于该成熟度阶段的operator应该对其管理的工作负载自动应用包括水平或垂直扩展在内的自主行为。
开发operator自然会有额外成本,包括设计和记录自定义资源定义,operator pod还存在额外的运行时成本。这就是为什么我认为operator的价值只有在第4级或"深度洞察(Deep Insights)"时才能开始抵消成本,这一级别需要生成指标、发出警报、完成工作负载分析等。
在4级以下,operator对于帮助缓解产品开发人员对未来的预测以及帮助系统管理员将这些预测变为现实几乎没有任何帮助。
Knative: 零浪费的弹性容量
据其网站介绍,Knative[22]"是一个开源企业级解决方案,用于构建无服务器和事件驱动应用程序。"
从本文角度来看,我们感兴趣的是Knative的服务模块[23]。该模块包含Knative Pod Autoscaler (KPA),它模仿HPA改变Pod副本数量以满足需求的能力,具有两个关键区别:
-
根据外部请求的数量来缩放副本 -
当没有外部请求时,将副本缩小到零
与HPA不同的地方在于,HPA在超过预设边界时对资源利用率做出反应,KPA根据并发性[24]和[每秒请求数](https://knative.dev/docs/serving/autoscaling/rps-target "Knative Configuring the requests per second (RPS "每秒请求数") target")目标做出决策。
根据经验,HPA更适合资源需求不会突然改变的、长时间运行的、启动缓慢的进程。相反,KPA更适合能够快速启动以满足即时需求的瞬时进程。
最后,无论是否考虑"无服务器",KPA都要使用容器,因此资源限制[25]仍然很重要。
集群自动伸缩: 突破极限
一旦对工作负载资源和pod autoscaler进行了适当的调优,就可以深入了解工具箱并使用Kubernetes集群autoscaler[26]了。
集群自动缩放器可以根据资源需求添加(或删除)工作节点,自动缩放器尊重可配置的下限和上限,以保证pod的最小容量以及一定程度的成本控制,避免难以控制的工作负载使集群过度扩容。
缓慢扩容(Slow stretching) 。记住,与pod自动缩放器相比,集群自动缩放器的响应时间明显较慢,工作节点需要几分钟才能对kube-scheduler可用。
假设需要集群自动缩放器来处理峰值需求,在这种情况下,必须部署一个牺牲缓冲区的"暂停舱(pause pod)"(在集群自动缩放器文档[27]中有描述)。当新工作负载需要这些资源时,kube-scheduler会立即退出pause pod以释放资源,然后集群扩展节点数量以重新分配被驱逐的pod。
请注意,Kubernetes的污点(taints)和容忍(tolerations)[28]也在自动缩放器的工作中发挥作用,如果没有对污点的容忍,则设有污点的工作节点将被禁止放置pod。
无服务器: 无限容量……但有限制
对于最终级别的自动扩展,是时候放弃集群的边界了(即使Kubernetes也有限制[29]),并在云服务商级别考虑无服务器功能。
主要的云服务商可以根据多个事件(如web请求、数据库操作和计时器)按需启动容器镜像。与单个集群相比,云服务商的资源容量实际上是无限的,与在集群中运行相同的工作负载相比,只在容器中运行一会儿的成本几乎可以忽略不计。
具有讽刺意味的是,尽管每次调用的成本极低,但无服务器方法使我们从担心资源限制转向担心成本超支[30]:
-
容器限制仍然很重要: 云服务商要求为每个容器调用指明cpu和内存数量。 -
预算限制: 如果测试套件中的错误可能耗尽集群容量,那么在无服务器部署中,相同的错误可能会在一夜之间耗尽整个部门的预算。
无服务器产品还有几种变体,例如将一个小功能交给云引擎,而不是整个容器镜像,或者让云服务商使用托管集群来运行容器(以规模化换取安全性)。
为方便起见,这些产品都可以从包含Dockerfile的Git存储库开始,在调度该镜像的工作负载之前,用存储库构建和缓存容器镜像。
结论
我们涵盖了工作负载的整个资源分配范围,从调优单个容器到Kubernetes之外的超大规模工作负载。
本文首先概述了Kubernetes资源管理及其资源请求和限制的核心概念,然后在这些静态限制的基础上构建pod自动缩放器: HPA、VPA和KPA。
从pod自动缩放器有效利用集群最大容量的能力开始,继续使用集群自动缩放来增加容量。最后以跨云服务商的无服务器产品形式,用几乎无限的资源(和成本)完成了整个视角的闭环[31]。
除了VPA,其他技术仍然建立在开发时确定容器资源消耗的努力之上。例如,HPA不会根据没有指定限制的资源采取行动,而云服务商会直接请求分配给每个容器运行的CPU和内存资源。
参考文献
-
Practical Kubernetes — Top Ten Challenges — Part 6: Sizing and Footprint Optimization [32], by Andre Tost -
You can’t have both high utilization and high reliability [33], by Nathan Yellin -
Vertical Pod Autoscaler deep dive, limitations and real-world examples [34] -
Vertical Pod Autoscaling: The Definitive Guide [35] -
Kubernetes Workload Resources [36] -
IBM Code Engine [37] (Managed Knative) -
AWS Fargate [38]
你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。微信公众号:DeepNoMind
参考资料
Infinite Scaling with Containers and Kubernetes: https://dnastacio.medium.com/kubernetes-resources-1a1fa1e72dcf
[2]kubelet: https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet
[3]命名空间限制范围: https://kubernetes.io/docs/concepts/policy/limit-range
[4]Kubernetes容器资源管理: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers
[5]本地临时存储: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#local-ephemeral-storage
[6]资源请求和资源限制: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources
[7]服务质量: https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod
[8]kube-scheduler: https://kubernetes.io/docs/reference/command-line-tools-reference/kube-scheduler
[9]Kubernetes工作负载: https://kubernetes.io/docs/concepts/workloads
[10]公地悲剧: https://www.investopedia.com/terms/t/tragedy-of-the-commons.asp
[11]限制范围(limit ranges): https://kubernetes.io/docs/concepts/policy/limit-range
[12]资源配额(resource quotas): https://kubernetes.io/docs/concepts/policy/resource-quotas
[13]Horizontal pod autoscaling (HPA): https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale
[14]Vertical pod autoscaling (VPA): https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale
[15]In-place Update of Pod Resources: https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/1287-in-place-update-pod-resources/README.md
[16]Vertical Pod Autoscaler: Known limitations: https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler#known-limitations
[17]Limit Range: https://kubernetes.io/docs/concepts/policy/limit-range
[18]Vertical Pod Autoscaler: Limits Control: https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler#limits-control
[19]Kubernetes operator: https://kubernetes.io/docs/concepts/extend-kubernetes/operator
[20]工作负载资源: https://kubernetes.io/docs/concepts/workloads
[21]Operator Maturity Model: https://sdk.operatorframework.io/docs/overview/operator-capabilities
[22]Knative: https://knative.dev/docs
[23]Knative Serving: https://knative.dev/docs/serving
[24]Knative Configuring concurrency: https://knative.dev/docs/serving/autoscaling/concurrency
[25]Knative Configure requests limits services: https://knative.dev/docs/serving/services/configure-requests-limits-services/#additional-resources
[26]Kubernetes Autoscaler: https://github.com/kubernetes/autoscaler
[27]Kubernetes Cluster Autoscaler: https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler
[28]Kubernetes Taint and Toleration: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration
[29]Kubernetes Best Pratices: Considerations for large clusters: https://kubernetes.io/docs/setup/best-practices/cluster-large
[30]Is Serverless with Lambda, the silver bullet?: https://medium.com/immoweb-tech-blog/is-serverless-with-lambda-the-silver-bullet-80c48d38a572
[31]Total Perspective Vortex: https://hitchhikers.fandom.com/wiki/Total_Perspective_Vortex
[32]Practical Kubernetes — Top Ten Challenges — Part 6: Sizing and Footprint Optimization: https://practicalkubernetes.blogspot.com/2021/12/top-ten-challenges-part-6-sizing-and.html
[33]You can’t have both high utilization and high reliability: https://home.robusta.dev/blog/kubernetes-utilization-vs-reliability
[34]Vertical Pod Autoscaler deep dive, limitations and real-world examples: https://medium.com/infrastructure-adventures/vertical-pod-autoscaler-deep-dive-limitations-and-real-world-examples-9195f8422724
[35]Vertical Pod Autoscaling: The Definitive Guide: https://povilasv.me/vertical-pod-autoscaling-the-definitive-guide/#
[36]Kubernetes Workload Resources: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources
[37]IBM Code Engine: https://cloud.ibm.com/docs/codeengine?topic=codeengine-getting-started
[38]AWS Fargate: https://aws.amazon.com/fargate
本文由 mdnice 多平台发布