cgroup是linux内核中用于实现资源使用限制和统计的模块,docker的风靡一时少不了cgroup等特性的支持。kubernetes作为容器编排引擎,除了借助docker进行容器进程的资源管理外,还提供了一些更加高级的资源管理功能,以提高资源利用率和更加稳定的程序运行环境,其中必然少不了cgroup这类资源管控技术的应用,那么kubernetes是如何使用cgroup哪? 如果仔细观察kubelet中关于cgroup的配置,就会发现这些配置参数多达十几个,错综复杂,怎样才能合理的配置这些参数哪?
kubelet作为kubernetes中的node agent,所有cgroup的操作都由其内部的containerManager模块实现,containerManager会通过cgroup将资源使用层层限制: container-> pod-> qos -> node。每一层都抽象出一种资源管理模型,通过这种方式提供了一种稳定的运行环境。
Conainer level cgroupskubernetes对于容器级别的隔离其实是交由底层的runtime来负责的,例如docker, 当我们指定运行容器所需要资源的request和limit时,docker会为容器设置进程所运行cgroup的cpu.share, cpu.quota, cpu.period, mem.limit等指标来,具体cgroup中各个参数含义处不再分析,感兴趣的同学可以查阅相关资料。
Pod level cgroups一个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值。
如果为所有容器都指定了request和limit值,则pod cgroups资源值设置为所有容器的加和,即:
pod<UID>/cpu.shares = sum(pod.spec.containers.resources.requests[cpu]) pod<UID>/cpu.cfs_quota_us = sum(pod.spec.containers.resources.limits[cpu]) pod<UID>/memory.limit_in_bytes = sum(pod.spec.containers.resources.limits[memory])如果其中某个容器只指定了request没有指定limit则并不会设置pod cgroup的limit值, 只设置其cpu.share值:
pod<UID>/cpu.shares = sum(pod.spec.containers.resources.requests[cpu])如果所有容器没有指定request和limit值,则只设置pod cgroup的cpu.share, 该pod在资源空闲的时候可以使用完node所有的资源,但是当资源紧张的时候无法获取到任何资源来执行,这也符合低优先级任务的定位:
pod<UID>/cpu.shares = 2其实上面三种设置方式对应的就是三种QoS pod。 这样设置pod level cgourp可以确保在合理指定容器资源时能够防止资源的超量使用, 如果未指定则可以使用到足够多的可用资源。 每次启动pod时kubelet就会同步对应的pod level cgroup。
QoS level cgroupkubernetes中会将所有的pod按照资源request, limit设置分为不同的QoS classes, 从而拥有不同的优先级。如果指定了--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和BestEffortcgroup, 然后将对应的pod都创建在该cgroup下。kubelet希望尽可能提高资源利用率,让Burstable和BestEffort类型的pod在需要的时候能够使用足够多的空闲资源,所以默认并不会为该QoS设置资源的limit。但是也需要保证当高优先级的pod需要使用资源时,低优先级的pod能够及时将资源释放出来:对于可压缩的资源例如CPU, kubelet会通过CPU CFS shares来控制,当CPU资源紧张时通过CFS share来将资源按照比例分配给各个QoS pod,保证每个pod都能够得到其所申请的资源。具体来说: 对于cpu的设置,besteffort和burstable的资源使用限制如下:
ROOT/besteffort/cpu.shares = 2 ROOT/burstable/cpu.shares = max(sum(Burstable pods cpu requests, 2)