任何领域,革命性的碾压式推陈出新并不是没有,但是概率极低,人们普遍的狂妄在于,总是认为自己所置身的环境正在发生着某种碾压式的变革,但其实,最终大概率不过是一场平庸。
作者 | dog250
责编 | 刘静
出品 | CSDN博客
但凡懂Linux内核的,都知道Linux内核的CFS进程调度算法,无论是从2.6.23将其初引入时的论文,还是各类源码分析,文章,以及Linux内核专门的图书,都给人这样一种感觉,即 CFS调度器是革命性的,它将彻底改变进程调度算法。 预期中,人们期待它会带来令人惊艳的效果。
然而这是错觉。
人们希望CFS速胜,但是分析来分析去, 却只是在某些方面比O(1)调度器稍微好一点点 。甚至在某些方面比不上古老的4.4BSD调度器。可是人们却依然对其趋之若鹜,特别是源码分析,汗牛塞屋!
为什么CFS对别的调度算法没有带来碾压的效果呢?
首先,在真实世界,碾压是不存在的,人与人,事与事既然被放在了同一个重量级梯队比较,其之间的差别没有想象的那么大,根本就不在谁碾压谁。不能被小说电视剧电影蒙蔽了,此外,徐晓冬大摆拳暴打雷雷也不算数,因为他们本就不是一个梯队。
任何领域,革命性的碾压式推陈出新并不是没有,但是概率极低,人们普遍的狂妄在于,总是认为自己所置身的环境正在发生着某种碾压式的变革,但其实,最终大概率不过是一场平庸。
最终就出现了角力,僵持。
其次,我们应该看到,CFS调度器声称它会给交互式进程带来福音,在这方面CFS确实比O(1)做得好,但是惊艳的效果来自于粉丝的认同。Linux系统交互进程本来就不多,Linux更多地被装在服务器,而在服务器看来,吞吐是要比交互响应更加重要的。
那么以交互为主的Android系统呢?我们知道,Android也是采用了CFS调度器,也有一些事BFS,为什么同样没有带来惊艳的效果呢?
我承认,2008年前后出现CFS时还没有Android,等到Android出现时,其采用的Linux内核已经默认了CFS调度器,我们看下Android版本,Linux内核版本以及发行时间的关系:
Linux内核在2.6.23就采用了CFS调度器。所以一个原因就是没有比较。Android系统上,CFS没有机会和O(1)做比较。
另外,即便回移一个O(1)调度器到Android系统去和CFS做AB,在我看来,CFS同样不会惊艳,原因很简单,Android系统几乎都是交互进程,却前台进程永远只有一个,你几乎感受不到进程的切换卡顿,换句话说,即便CFS对待交互式进程比O(1)好太多,你也感受不到,因为对于手机,平板而言,你切换 APP 的时间远远大于进程切换的时间粒度。
那么,CFS到底好在哪里?
简单点说,CFS的意义在于, 在一个混杂着大量计算型进程和IO交互进程的系统中,CFS调度器对待IO交互进程要比O(1)调度器更加友善和公平 。理解这一点至关重要。
其实,CFS调度器的理念非常古老,就说在业界,CFS的思想早就被应用在了磁盘IO调度,数据包调度等领域,甚至最最古老的SRV3以及4.3BSD UNIX系统的进程调度中早就有了CFS的身影,可以说,Linux只是 使用CFS调度器 ,而不是 设计了CFS调度器 !
就以4.3BSD调度器为例,我们看一下其调度原理。
4.3BSD采用了1秒抢占制,每间隔1秒,会对整个系统进程进行优先级排序,然后找到优先级最高的投入运行,非常简单的一个思想,现在看看它是如何计算优先级的。
首先,每一个进程j均拥有一个CPU滴答的度量值Cj,每一个时钟滴答,当前在运行的进程的CPU度量值C会递增:
当一个1秒的时间区间ii过去之后,Cj被重置,该进程jj的优先级采用下面的公式计算:
可以计算,在一个足够长的时间段内,两个进程运行的总时间比例,将和它们的Base_PrioBase_Prio优先级的比例相等。
4.3BSD的优先级公平调度是CPU滴答驱动的。
现在看Linux的CFS,CFS采用随时抢占制。每一个进程j均携带一个 虚拟时钟VCj ,每一个时钟滴答,当前进程k的VCk会重新计算,同时调度器选择VC最小的进程运行,计算方法非常简单:
可见, Linux的CFS简直就是4.3BSD进程调度的自驱无级变速版本!
如果你想了解CFS的精髓,上面的就是了。换成语言描述,CFS的精髓就是 “ n个进程的系统,任意长的时间周期TT,每一个进程运行T/n的时间! ”
当然,在现实和实现中,会有80%的代码处理20%的剩余问题,比如如何奖励睡眠太久的进程等等,但是这些都不是精髓。
综上,我们总结了:
所以无论从概念还是从效果,Linux CFS调度器均没有带来令人眼前一亮的哇塞效果。但是还缺点什么。嗯,技术上的解释。
分析和解释任何一个机制之前,必然要先问,这个机制的目标是什么,它要解决什么问题,这样才有意义。而不能仅仅是明白了它是怎么工作的。
那么Linux CFS调度器被采用,它的目标是解决什么问题的呢?它肯定是针对O(1)算法的一个问题而被引入并取代O(1),该问题也许并非什么臭名昭著,但是确实是一枚钉子,必须拔除。
O(1)调度器的本质问题在于 进程的优先级和进程可运行的时间片进行了强映射!
也就是说,给定一个进程优先级,就会计算出一个时间片与之对应,我们忽略奖惩相关的动态优先级,看一下原始O(1)算法中一个进程时间片的计算:
直观点显示:
针对上述问题,2.6内核的O(1)O(1)引入了双斜率来解决:
直观图示如下:
貌似问题解决了,但是如果单单揪住上图的某一个优先级子区间来看,还是会有问题,这就是相对优先级的问题。我们看到,高优先级的时间片是缓慢增减的,而低优先级的时间片却是陡然增减,同样都是相差同样优先级的进程,其优先级分布影响了它们的时间片分配。
本来是治瘸子,结果腿好了,但是胳臂坏了。
本质上来讲,这都源自于下面两个原因:
固定的优先级映射到固定的时间片。
相对优先级和绝对优先级混杂。
那么这个问题如何解决?
优先级和时间片本来就是两个概念,二者中间还得有个变量沟通才可以。优先级高只是说明该进程能运行的久一些,但是到底久多少,并不是仅仅优先级就能决定的,还要综合考虑,换句话距离来说,如果只有一个进程,那么即便它优先级再低,它也可以永久运行,如果系统中有很多的进程,即便再高优先级的进程也要让出一些时间给其它进程。
所以,考虑到系统中总体的进程情况,将优先级转换为权重,将时间片转换为份额,CFS就是了。最终的坐标系应该是 权重占比/时间片 坐标系而不是 权重(或者优先级)/时间片 。应该是这个平滑的样子:
看来,Linux CFS只是为了解决O(1)O(1)中一个 “静态优先级/时间片映射” 问题的,那么可想而知,它又能带来什么惊艳效果呢?这里还有个“但是”,这个O(1)O(1)调度器的问题其实在计算密集型的守护进程看来,并不是问题,反而是好事,毕竟高优先级进程可以 无条件持续运行很久而不切换 。这对于吞吐率的提高,cache利用都是有好处的。无非也就侵扰了交互进程呗,又有何妨。
当然,使用调优CFS的时候,难免也要遇到IO睡眠奖惩等剩余的事情去设计一些trick算法,这破费精力。
对了,还要设置你的内核为HZ1000哦,这样更能体现CFS的平滑性,就像它宣称的那样。我难以想象,出了Ubuntu,Suse等花哨的桌面发行版之外,还有哪个Linux需要打开HZ1000,服务器用HZ250不挺好吗?
关于调度的话题基本就说完了,但是在进入下一步固有的喷子环节之前,还有两点要强调:
在CPU核数越来越多的时代,人们更应该关心 把进程调度到哪里CPU核上 而不是 某个CPU核要运行哪个进程 。
单核时代一路走过来的Linux,发展迅猛,这无可厚非,但是成就一个 *** 作系统内核的并不单单是技术,还有别的。这些当然程序员们很不爱听,程序员最烦非技术方面的东西了,程序员跟谁都比写代码,程序员特别喜欢喷领导不会写代码云云。
Linux在纯技术方面并不优秀,Linux总体上优秀的原因是因为有一群非代码不明志的程序员在让它变得越来越优秀,另一方面还要归功于开源和社区。Linux的学习门槛极低,如果一个公司能不费吹灰之力招聘到一个Linux程序员的话,那它干嘛还要费劲九牛二虎之力去招聘什么高端的BSD程序员呢?最终的结果就是,Linux用的人极多,想换也换不掉了。
但无论如何也没法弥补Linux内核上的一些原则性错误。
Linux内核还是以原始的主线为base,以讲Linux内核的书为例,经典的Robert Love的《Linux内核设计与实现》,以及《深入理解Linux内核》,在讲进程调度的时候,关于多核负载均衡的笔墨都是少之又少甚至没有,如此经典的著作把很多同好引向了那万劫不复的代码深渊。于是乎,铺天盖地的CFS源码分析纷至沓来。
但其实,抛开这么一个再普通不过的Linux内核,现代 *** 作系统进入了多核时代,其核心正是在cache利用上的革新,带来的转变就是进程调度和内存管理的革新。review一下Linux内核源码,这些改变早就已经表现了出来。
可悲的是,关于Linux内核的经典书籍却再也没有更新,所有的从传统学校出来的喜欢看书学习的,依然是抱着10年前的大部头在啃。
http :// www. ece.ubc.ca/~sasha/papers/eurosys16-final29.pdf
浙江温州皮鞋湿,下雨进水不会胖。
作者:CSDN博主「dog250」,本文首发于作者CSDN博客https://blog.csdn.net/dog250/article/details/957298 30 。
【END】
Linux的调度策略区分实时进程和普通进程,实时进程的调度策略是SCHED_FIFO和SCHED_RR,普通的,非实时进程的调度策略是SCHED_NORMAL(SCHED_OTHER)。
实时调度策略被实时调度器管理,普通调度策略被完全公平调度器来管理。实时进程的优先级要高于普通进程(nice越小优先级越高)。
SCHED_FIFO实现了一种简单的先入先出的调度算法,它不使用时间片,但支持抢占,只有优先级更高的SCHED_FIFO或者SCHED_RR进程才能抢占它,否则它会一直执行下去,低优先级的进程不能抢占它,直到它受阻塞或自己主动释放处理器。
SCHED_RR是带有时间片的一种实时轮流调度算法,当SCHED_RR进程耗尽它的时间片时,同一优先级的其它实时进程被轮流调度,时间片只用来重新调用同一优先级的进程,低优先级的进程决不能抢占SCHED_RR任务,即使它的时间片耗尽。SCHED_RR是带时间片的SCHED_FIFO。
Linux的实时调度算法提供了一种软实时工作方式,软实时的含义是尽力调度进程,尽力使进程在它的限定时间到来前运行,但内核不保证总能满足这些进程的要求,相反,硬实时系统保证在一定的条件下,可以满足任何调度的要求。
SCHED_NORMAL使用完全公平调度算法(CFS),之前的算法直接将nice值对应时间片的长度,而在CFS中,nice值只作为进程获取处理器运行比的权重,每个进程都有一个权重,nice优先级越高,权重越大,表示应该运行更长的时间。Linux的实现中,每个进程都有一个vruntime字段,vruntime是经过量化的进程运行时间,也就是实际运行时间除以权重,所以每个量化后的vruntime应该相等,这就体现了公平性。
CFS当然也支持抢占,但与实时调度算法不同,实时调度算法是根据优先级进行抢占,CFS是根据vruntime进行抢占,vruntime小就拥有优先被运行的权利。
为了计算时间片,CFS算法需要为完美多任务中的无限小调度周期设定近似值,这个近似值也称作目标延迟,指每个可运行进程在目标延迟内都会调度一次,如果进程数量太多,则时间粒度太小,所以约定时间片的默认最小粒度是1ms。
进程可以分为I/O消耗型和处理器消耗型,这两种进程的调度策略应该不同,I/O消耗型应该更加实时,给对端的感觉是响应很快,同时它一般又不会消耗太多的处理器,因而I/O消耗型需要调度频繁。相对来说,处理器消耗型不需要特别实时,应该尽量降低它的调度频度,延长其运行时间。
参考: linux内核分析——CFS(完全公平调度算法) - 一路向北你好 - 博客园
cgroups的一个设计目标是为不同的应用情况提供统一的接口,从控制单一进程到 *** 作系统层虚拟化(像OpenVZ,Linux-VServer,LXC)。cgroups提供:
每个 cgroup 子系统代表一种资源,如针对某个 cgroup 的处理器时间或者 pid 的数量,也叫进程数。Linux 内核提供对以下 12 种 cgroup 子系统的支持:
这里面每一个子系统都需要与内核的其他模块配合来完成资源的控制,比如对 cpu 资源的限制是通过进程调度模块根据 cpu 子系统的配置来完成的;对内存资源的限制则是内存模块根据 memory 子系统的配置来完成的,而对网络数据包的控制则需要 Traffic Control 子系统来配合完成。本文不会讨论内核是如何使用每一个子系统来实现资源的限制,而是重点放在内核是如何把 cgroups 对资源进行限制的配置有效的组织起来的,和内核如何把cgroups 配置和进程进行关联的,以及内核是如何通过 cgroups 文件系统把cgroups的功能暴露给用户态的。
cpu子系统用于控制cgroup中所有进程可以使用的cpu时间片。
cpu子系统主要涉及5个参数: cpu.cfs_period_us,cpu.cfs_quota_us,cpu.shares,cpu.rt_period_us,cpu.rt_runtime_us 。cfs表示Completely Fair Scheduler完全公平调度器,是Linux内核的一部分,负责进程调度。
cpu.cfs_quota_us/cpu.cfs_period_us决定cpu控制组中所有进程所能使用CPU资源的最大值,而cpu.shares决定了cpu控制组间可用CPU的相对比例,这个比例只有当主机上的CPU完全被打满时才会起作用。
cpuacct子系统(CPU accounting)会自动生成报告来显示cgroup中任务所使用的CPU资源。报告有两大类: cpuacct.stat和cpuacct.usage。
cpuset主要是为了numa使用的,numa技术将CPU划分成不同的node,每个node由多个CPU组成,并且有独立的本地内存、I/O等资源(硬件上保证)。可以使用numactl查看当前系统的node信息。
memory 子系统 自动生成 cgroup 任务使用内存资源的报告,并限定这些任务所用内存的大小。
容器可以通过设置 memory.swappiness 参数来决定是否使用 swap 空间。
Linux通过文件的方式,将cgroups的功能和配置暴露给用户,这得益于Linux的虚拟文件系统(VFS)。VFS将具体文件系统的细节隐藏起来,给用户态提供一个统一的文件系统API接口,cgroups和VFS之间的链接部分,称之为cgroups文件系统。
比如挂在 cpu、cpuset、memory 三个子系统到 /cgroups/cpu_mem 目录下:
其中-t选项指定文件系统类型为cgroup类型,-o指定本次创建的cgroup实例与cpu和momory子系统(或资源)关联,cpu_momory指定了当前cgroup实例在整个cgroup树中所处的层级名称,最后的路径为文件系统挂载点。
runtime 有两种 cgroup 驱动:一种是 systemd ,另外一种是 cgroupfs :
kubernetes 中默认 kubelet 的 cgroup 驱动就是 cgroupfs,若要使用 systemd,则必须将 kubelet 以及 runtime 都需要配置为 systemd 驱动。
由于 kubeadm 把 kubelet 视为一个系统服务来管理,所以对基于 kubeadm 的安装, 我们推荐使用 systemd 驱动,不推荐 cgroupfs 驱动。
配置 cgroup 驱动
kubelet作为kubernetes中的node agent,所有cgroup的 *** 作都由其内部的containerManager模块实现,containerManager会通过cgroup将资源使用层层限制: container->pod->qos ->node 。每一层都抽象出一种资源管理模型,通过这种方式提供了一种稳定的运行环境。如下图所示:
kubernetes对于容器级别的隔离其实是交由底层的runtime来负责的,例如docker, 当我们指定运行容器所需要资源的request和limit时,docker会为容器设置进程所运行cgroup的cpu.share, cpu.quota, cpu.period, mem.limit等指标来。
首先是 CPU 资源,我们先看一下 CPU request。CPU request 是通过 cgroup 中 CPU 子系统中的 cpu.shares 配置来实现的。当你指定了某个容器的 CPU request 值为 x millicores 时,kubernetes 会为这个 container 所在的 cgroup 的 cpu.shares 的值指定为 x * 1024 / 1000。即:
举个例子,当你的 container 的 CPU request 的值为 1 时,它相当于 1000 millicores,所以此时这个 container 所在的 cgroup 组的 cpu.shares 的值为 1024。
这样做希望达到的最终效果就是:即便在极端情况下,即所有在这个物理机上面的 pod 都是 CPU 繁忙型的作业的时候(分配多少 CPU 就会使用多少 CPU),仍旧能够保证这个 container 的能够被分配到 1 个核的 CPU 计算量。其实就是保证这个 container 的对 CPU 资源的最低需求。
而针对 CPU limit,Kubernetes 是通过 CPU cgroup 控制模块中的 cpu.cfs_period_us , cpu.cfs_quota_us 两个配置来实现的。kubernetes 会为这个 container cgroup 配置两条信息:
在 cgroup 的 CPU 子系统中,可以通过这两个配置,严格控制这个 cgroup 中的进程对 CPU 的使用量,保证使用的 CPU 资源不会超过 cfs_quota_us/cfs_period_us ,也正好就是我们一开始申请的 limit 值。
对于cpu来说,如果没有指定 limit 的话,那么 cfs_quota_us 将会被设置为 -1 ,即没有限制。而如果 limit 和 request 都没有指定的话,cpu.shares 将会被指定为 2 ,这个是 cpu.shares 允许指定的最小数值了。可见针对这种 pod,kubernetes 只会给他分配最少的 CPU 资源。
针对内存资源,其实 memory request 信息并不会在 container level cgroup 中有体现。kubernetes 最终只会根据 memory limit 的值来配置 cgroup 的。
在这里 kubernetes 使用的 memory cgroup 子系统中的 memory.limit_in_bytes 配置来实现的。配置方式如下:
memory 子系统中的 memory.limit_in_bytes 配置,可以限制一个 cgroup 中的所有进程可以申请使用的内存的最大量,如果超过这个值,那么根据 kubernetes 的默认配置,这个容器会被 OOM killed,容器实例就会发生重启。
对于内存来说,如果没有 limit 的指定的话,memory.limit_in_bytes 将会被指定为一个非常大的值,一般是 2^64 ,可见含义就是不对内存做出限制。
一个pod中往往有一个或者有多个容器,但是如果我们将这些容器的资源使用进行简单的加和并不能准确的反应出整个pod的资源使用,因为每个pod都会有一些overhead的资源,例如sandbox容器使用的资源,docker的containerd-shim使用的资源,此外如果指定memory类型的volume时,这部分内存资源也是属于该pod占用的。因为这些资源并不属于某一个特定的容器,我们无法仅仅通过容器的资源使用量简单累加获取到整个pod的资源,为了方便统计一个pod所使用的资源(resource accounting),并且合理的将所有使用到的资源都纳入管辖范围内,kubernetes引入了pod level Cgroup,会为每个pod创建一个cgroup。该特性通过指定 --cgroups-per-qos=true 开启, 在1.6+版本中是默认开启。kubelet会为每个pod创建一个 pod<pod.UID>的cgroup,该cgroup的资源限制取决于pod中容器的资源request,limit值。
其实上面三种设置方式对应的就是三种QoS pod。这样设置pod level cgourp可以确保在合理指定容器资源时能够防止资源的超量使用,如果未指定则可以使用到足够多的可用资源。 每次启动pod时kubelet就会同步对应的pod level cgroup。
kubernetes中会将所有的pod按照资源request, limit设置分为不同的QoS classes, 从而拥有不同的优先级。QoS(Quality of Service) 即服务质量,QoS 是一种控制机制,它提供了针对不同用户或者不同数据流采用相应不同的优先级,或者是根据应用程序的要求,保证数据流的性能达到一定的水准。kubernetes 中有三种 QoS,分别为:
三者的优先级如下所示,依次递增:
BestEffort ->Burstable ->Guaranteed
如果指定了 --cgroups-per-qos 也会为每个QoS也会对应一个cgroup,该功能默认开启,这样就可以利用cgroup来做一些QoS级别的资源统计,必要时也可以通过该cgroup限制某个QoS级别的pod能使用的资源总和。此时每个QoS cgroup相当于一个资源pool, 内部的pod可以共用pool中的资源,但是对于整个pool会进行一些资源的限制,避免在资源紧张时低优先级的pod抢占高优先级的pod的资源。
对于guaranteed级别的pod,因为pod本身已经指定了request和limit,拥有了足够的限制,无需再增加cgroup来约束。但是对于Burstable和BestEffort类型的pod,因为有的pod和容器没有指定资源限制,在极端条件下会无限制的占用资源,所以我们需要分别设置Burstable和BestEffort cgroup, 然后将对应的pod都创建在该cgroup下。kubelet希望尽可能提高资源利用率,让Burstable和BestEffort类型的pod在需要的时候能够使用足够多的空闲资源,所以默认并不会为该QoS设置资源的limit。但是也需要保证当高优先级的pod需要使用资源时,低优先级的pod能够及时将资源释放出来:对于可压缩的资源例如CPU, kubelet会通过 cpu.shares 来控制,当CPU资源紧张时通过 cpu.shares 来将资源按照比例分配给各个QoS pod,保证每个pod都能够得到其所申请的资源。具体来说, 对于cpu的设置,besteffort和burstable的资源使用限制如下:
对于不可压缩资源内存,要满足“高优先级pod使用资源时及时释放低优先级的pod占用的资源”就比较困难了,kubelet只能通过资源预留的机制,为高优先级的pod预留一定的资源,该特性默认关闭,用户可以通过开启 QOSReserved 特征门控(默认关闭),并设置 --qos-reserved 参数来预留的资源比例,例如 --qos-reserved=memory=50% 表示预留50%高优先级request的资源值,当前只支持memory , 此时qos cgroups的限制如下:
同时根据 cpu.shares 的背后实现原理,位于不同层级下面的 cgroup,他们看待同样数量的 cpu.shares 配置可能最终获得不同的资源量。比如在 Guaranteed 级别的 pod cgroup 里面指定的 cpu.shares=1024,和 burstable 下面的某个 pod cgroup 指定 cpu.shares=1024 可能最终获取的 cpu 资源并不完全相同。所以每次创建、删除pod都需要根据上述公式动态计算cgroup值并进行调整。此时kubelet先会尽力去更新低优先级的pod,给高优先级的QoS预留足够的资源。因为memory是不可压缩资源,可能当pod启动时,低优先级的pod使用的资源已经超过限制了,如果此时直接设置期望的值会导致失败,此时kubelet会尽力去设置一个能够设置的最小值(即当前cgroup使用的资源值),避免资源使用进一步增加。通过设置qos资源预留能够保障高优先级的资源可用性,但是对低优先级的任务可能不太友好,官方默认是关闭该策略,可以根据不同的任务类型合理取舍。
对于node层面的资源,kubernetes会将一个node上面的资源按照使用对象分为三部分:
通常情况下,我们为提高集群资源利用率,会进行适当超配资源,如果控制不当,业务进程可能会占用完整个node的资源,从而使的第二,三部分核心的程序所使用的资源受到压制,从而影响到系统稳定性,为避免这样的情况发生,我们需要合理限制pods的资源使用,从而 为系统组件等核心程序预留足够的资源 ,保证即使在极端条件下有充足的资源来使用。
kubelet会将所有的pod都创建一个 kubepods 的cgroup下,通过该cgroup来限制node上运行的pod最大可以使用的资源。该cgroup的资源限制取值为:
其中 kube-reserved 是为kubernetes组件提供的资源预留, system-reserved 是为系统组件预留的资源,分别通过 --kube-reserved , --system-reserved 来指定,例如--kube-reserved=cpu=100m,memory=100Mi。
除了指定预留给系统运行的资源外,如果要限制系统运行的资源,可以通过 --enforce-node-allocatable 来设置,该flag指定需要执行限制的资源类型,默认值为pods,即通过上述kubepods来限制pods的使用资源,此外还支持限制的资源类型有:
如果需要指定多种类型,通过逗号分割枚举即可,注意如果开启了 system-reserved 和 kube-reserved 的限制,则意味着将限制这些核心组件的资源使用,以上述--kube-reserved=cpu=100m,memory=100Mi为例,所有的kubernetes组件最多可以使用cpu: 100m,memory: 100Mi。
除非已经很了解自己的资源使用属性,否则并不建议对这两种资源进行限制,避免核心组件CPU饥饿或者内存OOM。
默认情况下该 --enforce-node-allocatable 的值为pods,即只限制容器使用的资源,但不限制系统进程和kubernetes进程的资源使用量。
kubelet会在资源紧张的时候主动驱逐低优先级的pod,可以指定 hard-eviction-threshold 来设置阈值,这样一个node真正可以为pod使用的资源量为:
这也是调度器进行调度时所使用的资源值。
除了上述提到的cgroup设置外,kubelet中还有一些对于单个组件的cgroup设置, 例如:
以上runtime-cgroups,system-cgroups,kubelet-cgoups的设置都是 可选的 ,如果不进行指定也可以正常运行。但是如果显式指定后就需要与前面提到的 --kube-reserved-cgroup 和 --system-reserved-cgroup 搭配使用,如果配置不当难以达到预期效果:
如果在 --enforce-node-allocatable 参数中指定了 kube-reserved 来限制kubernetes组件的资源限制后,kube-reserved-cgroup的应该是:runtime-cgroups, kubelet-cgoups的父cgroup。只有对应的进程都应该运行该cgroup之下,才能进行限制,kubelet会设置 kube-reserved-cgroup 的资源限制但并不会将这些进程加入到该cgroup中,我们要想让该配置生效,就必须让通过制定 --runtime-cgroups , --kubelet-cgoups 来将这些进程加入到该cgroup中。同理如果上述 --enforce-node-allocatable 参数中指定了 system-reserved 来限制系统进程的资源,则 --system-reserved-cgroup 的参数应该与 --system-cgroups 参数相同,这样系统进程才会运行到 system-reserved-cgroup 中起到资源限制的作用。
最后整个整个cgroup hierarchy 如下:
上述所有的 *** 作在 kubelet 中是通过 containerManager 来实现的, containerManager 启动的时候首先会 setupNode 初始化各种cgroup,具体包括:
上述所有更新cgroup的 *** 作都会利用一个 cgroupManager 来实现。
三种 Qos 在调度和底层表现上都不一样:
Linux资源管理之cgroups简介
kubernetes 中 Qos 的设计与实现
Cgroup中的CPU资源控制
重学容器29: 容器资源限制之限制容器的CPU
深入解析 kubernetes 资源管理
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)