对于不可压缩资源内存,要满足"高优先级pod使用资源时及时释放低优先级的pod占用的资源"就比较困难了,kubelet只能通过资源预留的机制,为高优先级的pod预留一定的资源,该特性默认关闭,用户可以通过--qos-reserved来设置预留的资源比例,例如--qos-reserved=memory=50%表示预留50%高优先级request的资源值,当前只支持memory, 此时qos cgroups的限制如下:
ROOT/burstable/memory.limit_in_bytes = Node.Allocatable - {(summation of memory requests of `Guaranteed` pods)*(reservePercent / 100)} ROOT/besteffort/memory.limit_in_bytes = Node.Allocatable - {(summation of memory requests of all `Guaranteed` and `Burstable` pods)*(reservePercent / 100)}同时根据 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 level cgroups对于node层面的资源,kubernetes会将一个node上面的资源按照使用对象分为三部分:
业务进程使用的资源, 即pods使用的资源;
kubernetes组件使用的资源,例如kubelet, docker;
系统组件使用的资源,例如logind, journald等进程。
通常情况下,我们为提高集群资源利用率,会进行适当超配资源,如果控制不当,业务进程可能会占用完整个node的资源,从而使的第二,三部分核心的程序所使用的资源受到压制,从而影响到系统稳定性,为避免这样的情况发生,我们需要合理限制pods的资源使用,从而为系统组件等核心程序预留足够的资源,保证即使在极端条件下有充足的资源来使用。
kubelet会将所有的pod都创建一个kubepods的cgroup下,通过该cgroup来限制node上运行的pod最大可以使用的资源。该cgroup的资源限制取值为: ${Node Capacity} - ${Kube-Reserved} - ${System-Reserved},其中kube-reserved是为kubernetes组件提供的资源预留,system-reserved是为系统组件预留的资源,分别通过--kube-reserved, --system-reserved来指定,例如--kube-reserved=cpu=100m,memory=100Mi。
除了指定预留给系统运行的资源外, 如果要限制系统运行的资源,可以通过--enforce-node-allocatable来设置,该flag指定需要执行限制的资源类型,默认值为pods,即通过上述kubepods来限制pods的使用资源,此外还支持限制的资源类型有:
system-reserved: 限制kubernetes组件的资源使用,如果开启该限制,则需要同时设置--kube-reserved-cgroup参数指定所作用的cgroup
kube-reserved: 限制系统组件的资源使用,如果开启该限制则需要同时设置--system-reserved-cgroup
- none: 不进行任何资源限制
如果需要指定多种类型,通过逗号分割枚举即可,注意如果开启了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使用的资源量为: ${Allocatable} = ${Node Capacity} - ${Kube-Reserved} - ${System-Reserved} - ${Hard-Eviction-Threshold}, 这也是调度器进行调度时所使用的资源值。
核心组件的Cgroup除了上述提到的cgroup设置外,kubelet中还有一些对于单个组件的cgroup设置, 例如:
--runtime-cgroups: 用来指定docker等runtime运行的Cgroup。目前docker-CRI的实现dockershim会管理该cgroup和oom score, 确保dockerd和docker-containerd进程是运行在该cgroup之内,这里会对内存进行限制,使其最大使用宿主机70%的内存,主要是为了防止docker之前内存泄露的bug。 kubelet在此处只是不断获取该cgroup信息供kuelet SummarProvider进行获取统计信息,从而通过summary api暴露出去。