QOS是k8s中一种资源保护机制,其主要是针对不可压缩资源好比的内存的一种控制技术,好比在内存中其经过为不一样的Pod和容器构造OOM评分,而且经过内核的策略的辅助,从而实现当节点内存资源不足的时候,内核能够按照策略的优先级,优先kill掉哪些优先级比较低(分值越高优先级越低)的Pod,今天来分析下背后的实现java
在Linux中一切皆文件,控制CGroup自己也是经过配置文件进行的,这是我建立的一个内存Lmits为200M的Pod的容器的配置node
# pwd /sys/fs/cgroup # cat ./memory/kubepods/pod8e172a5c-57f5-493d-a93d-b0b64bca26df/f2fe67dc90cbfd57d873cd8a81a972213822f3f146ec4458adbe54d868cf410c/memory.limit_in_bytes 209715200
这里咱们重点关注内存相关的两个配置:VMOvercommitMemory其值为1,表示运行分配全部的物理内存资源,注意不包括SWAP资源VMPanicOnOOM其值为0:表示当内存不足的时候触发oom_killer进行选择部分进程进行kill,QOS也是经过影响其kill流程来实现的docker
func setupKernelTunables(option KernelTunableBehavior) error { desiredState := map[string]int{ utilsysctl.VMOvercommitMemory: utilsysctl.VMOvercommitMemoryAlways, utilsysctl.VMPanicOnOOM: utilsysctl.VMPanicOnOOMInvokeOOMKiller, utilsysctl.KernelPanic: utilsysctl.KernelPanicRebootTimeout, utilsysctl.KernelPanicOnOops: utilsysctl.KernelPanicOnOopsAlways, utilsysctl.RootMaxKeys: utilsysctl.RootMaxKeysSetting, utilsysctl.RootMaxBytes: utilsysctl.RootMaxBytesSetting, }
QOS打分机制主要是根据Requests和limits里面的资源限制来进行类型断定与打分的,咱们就来快速看下这部分的实现segmentfault
遍历全部的容器列表,注意这里会包含全部的初始化容器和业务容器微信
requests := v1.ResourceList{} limits := v1.ResourceList{} zeroQuantity := resource.MustParse("0") isGuaranteed := true allContainers := []v1.Container{} allContainers = append(allContainers, pod.Spec.Containers...) // 追加全部的初始化容器 allContainers = append(allContainers, pod.Spec.InitContainers...)
这里遍历全部的Requests和Limits限制的资源,分别加入到不一样的资源集合汇总,其中断定是否是Guaranteed主要是根据limits里面的资源是否包含CPU和内存两种资源,都包含才多是Guaranteedapp
for _, container := range allContainers { // process requests for name, quantity := range container.Resources.Requests { if !isSupportedQoSComputeResource(name) { continue } if quantity.Cmp(zeroQuantity) == 1 { delta := quantity.DeepCopy() if _, exists := requests[name]; !exists { requests[name] = delta } else { delta.Add(requests[name]) requests[name] = delta } } } // process limits qosLimitsFound := sets.NewString() for name, quantity := range container.Resources.Limits { if !isSupportedQoSComputeResource(name) { continue } if quantity.Cmp(zeroQuantity) == 1 { qosLimitsFound.Insert(string(name)) delta := quantity.DeepCopy() if _, exists := limits[name]; !exists { limits[name] = delta } else { delta.Add(limits[name]) limits[name] = delta } } } if !qosLimitsFound.HasAll(string(v1.ResourceMemory), string(v1.ResourceCPU)) { // 必须是所有包含cpu和内存限制 isGuaranteed = false } }
若是Pod里面的容器没有任何requests和limits的限制则就是BestEffortide
if len(requests) == 0 && len(limits) == 0 { return v1.PodQOSBestEffort }
要是Guaranteed必须是资源相等,而且限定的数量相同源码分析
// Check is requests match limits for all resources. if isGuaranteed { for name, req := range requests { if lim, exists := limits[name]; !exists || lim.Cmp(req) != 0 { isGuaranteed = false break } } } if isGuaranteed && len(requests) == len(limits) { return v1.PodQOSGuaranteed }
若是不是上面两种就是最后一种burstable了ui
return v1.PodQOSBurstable
其中guaranteedOOMScoreAdj是-998其实这跟OOM实现有关系,一台node节点上主要是三部分组成:kubelet主进程、docker进程、业务容器进程,而OOM的打分里面-1000表示该进程不会被oom所kill, 那一个业务进程最少也就只能是-999由于你不能保证本身的业务永远不会出现问题,因此在QOS里面-999其实就是kubelet和docker进程所保留的,剩下的才能做为业务容器分配(分值越高越容易被kill)spa
// KubeletOOMScoreAdj is the OOM score adjustment for Kubelet KubeletOOMScoreAdj int = -999 // DockerOOMScoreAdj is the OOM score adjustment for Docker DockerOOMScoreAdj int = -999 // KubeProxyOOMScoreAdj is the OOM score adjustment for kube-proxy KubeProxyOOMScoreAdj int = -999 guaranteedOOMScoreAdj int = -998 besteffortOOMScoreAdj int = 1000
关键Pod是一种特殊的存在,它能够是Burstable或者BestEffort类型的Pod,可是OOM打分却能够跟Guaranteed同样,这种类型的Pod主要包含三种:静态Pod、镜像Pod和高优先级Pod
if types.IsCriticalPod(pod) { return guaranteedOOMScoreAdj }
断定实现
func IsCriticalPod(pod *v1.Pod) bool { if IsStaticPod(pod) { return true } if IsMirrorPod(pod) { return true } if pod.Spec.Priority != nil && IsCriticalPodBasedOnPriority(*pod.Spec.Priority) { return true } return false }
这两种类型都有各自默认的值分别为Guaranteed(-998)和BestEffort(1000)
switch v1qos.GetPodQOS(pod) { case v1.PodQOSGuaranteed: // Guaranteed containers should be the last to get killed. return guaranteedOOMScoreAdj case v1.PodQOSBestEffort: return besteffortOOMScoreAdj }
其中关键的一行就是:oomScoreAdjust := 1000 - (1000memoryRequest)/memoryCapacity,从这个计算里面能够看出,若是咱们申请的资源越多,那么 (1000memoryRequest)/memoryCapacity这个里面计算出来的时机值就会越小,即最终结果就越大,其实也就代表若是咱们占用的内存越少,则打分就越高,这类容器就相对比较容易被kill
memoryRequest := container.Resources.Requests.Memory().Value() oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity // A guaranteed pod using 100% of memory can have an OOM score of 10. Ensure that burstable pods have a higher OOM score adjustment. if int(oomScoreAdjust) < (1000 + guaranteedOOMScoreAdj) { return (1000 + guaranteedOOMScoreAdj) } // Give burstable pods a higher chance of survival over besteffort pods. if int(oomScoreAdjust) == besteffortOOMScoreAdj { return int(oomScoreAdjust - 1) } return int(oomScoreAdjust)
好了今天就到这里,看以前还很懵逼,看完有种豁然开朗的感受,仍是那句话说的对,源码面前了无秘密,加油
k8s源码阅读电子书地址: https://www.yuque.com/baxiaoshi/tyado3
微信号:baxiaoshi2020![]()
关注公告号阅读更多源码分析文章
更多文章关注 www.sreguide.com
本文由博客一文多发平台 OpenWrite 发布!