分享
  1. 首页
  2. 文章

图解kubernetes资源QOS机制实现原理

仔仔 · · 1127 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

QOS是k8s中一种资源保护机制,其主要是针对不可压缩资源比如的内存的一种控制技术,比如在内存中其通过为不同的Pod和容器构造OOM评分,并且通过内核的策略的辅助,从而实现当节点内存资源不足的时候,内核可以按照策略的优先级,优先kill掉哪些优先级比较低(分值越高优先级越低)的Pod,今天来分析下背后的实现

1.关键基础特性

image.png

1.1 一切皆文件

在Linux中一切皆文件,控制CGroup本身也是通过配置文件进行的,这是我创建的一个内存Lmits为200M的Pod的容器的配置

# pwd
/sys/fs/cgroup
# cat ./memory/kubepods/pod8e172a5c-57f5-493d-a93d-b0b64bca26df/f2fe67dc90cbfd57d873cd8a81a972213822f3f146ec4458adbe54d868cf410c/memory.limit_in_bytes
209715200

1.2 内核内存配置

这里我们重点关注内存相关的两个配置:VMOvercommitMemory其值为1,表示运行分配所有的物理内存资源,注意不包括SWAP资源VMPanicOnOOM其值为0:表示当内存不足的时候触发oom_killer进行选择部分进程进行kill,QOS也是通过影响其kill流程来实现的

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,
 }

2.QOS打分机制与判定实现

QOS打分机制主要是根据Requests和limits里面的资源限制来进行类型判定与打分的,我们就来快速看下这部分的实现

2.1 根据容器判定QOS类型

2.1.1 构建容器列表

遍历所有的容器列表,注意这里会包含所有的初始化容器和业务容器

 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...)

2.1.2 处理Requests和limits

这里遍历所有的Requests和Limits限制的资源,分别加入到不同的资源集合汇总,其中判定是不是Guaranteed主要是根据limits里面的资源是否包含CPU和内存两种资源,都包含才可能是Guaranteed

 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
 }
 }

2.1.3 BestEffort

如果Pod里面的容器没有任何requests和limits的限制则就是BestEffort

 if len(requests) == 0 && len(limits) == 0 {
 return v1.PodQOSBestEffort
 }

2.1.4 Guaranteed

要是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
 }

2.1.5 Burstable

如果不是上面两种就是最后一种burstable了

 return v1.PodQOSBurstable

2.2 QOS OOM打分机制

2.2.1 OOM打分机制

其中guaranteedOOMScoreAdj是-998其实这跟OOM实现有关系,一台node节点上主要是三部分组成:kubelet主进程、docker进程、业务容器进程,而OOM的打分里面-1000表示该进程不会被oom所kill, 那一个业务进程最少也就只能是-999因为你不能保证自己的业务永远不会出现问题,所以在QOS里面-999其实就是kubelet和docker进程所保留的,剩下的才能作为业务容器分配(分值越高越容易被kill)

 // 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

2.2.2 关键Pod

关键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
}

2.2.3 Guaranteed与BestEffort

这两种类型都有各自默认的值分别为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
 }

2.2.4 Burstable

其中关键的一行就是: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

关注公告号阅读更多源码分析文章 21天大棚

更多文章关注 www.sreguide.com

本文由博客一文多发平台 OpenWrite 发布!


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:仔仔

查看原文:图解kubernetes资源QOS机制实现原理

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1127 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏