前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >图解 K8S 源码 - QoS 篇

图解 K8S 源码 - QoS 篇

作者头像
郭旭东
发布2020-12-30 10:46:43
发布2020-12-30 10:46:43
1K00
代码可运行
举报
文章被收录于专栏:云原生工具箱云原生工具箱
运行总次数:0
代码可运行

前言

日常使用 Kubernetes 时,时长会出现 Node 节点中的 Pod 被 OOMKill 掉的情况,但 Node 节点中 Pod 众多,为什么单单选中这个 Pod Kill 掉呢?这里就引出了 QoS 的概念,本篇文章就会从源码的角度介绍 QoS 的分类、打分机制,并简单介绍不同 QoS 的本质区别。看看这个机制是如何保证运行在 Kubernetes 中服务质量的。

QoS

QoS(Quality of Service) 即服务质量,是 Kubernetes 中的一种控制机制,其会对运行在 Kubernetes 中的 Pod 进行一个质量划分,根据 Pod 中 container 的 Limit 和 request 将 Pod 分为 GuaranteedBurstableBestEffort 三类并对所有 Pod 进行一个打分。在资源尤其是内存这种不可压缩资源不够时,为保证整体质量的稳定,Kubernetes 就会根据 QoS 的不同优先级,对 Pod 进行资源回收。这也是有时集群中的 Pod 突然被 kill 掉的原因。

Qos

QoS 分类

以下代码用来获取 Pod 的 QoS 类,用于区分不同 Pod 的 QoS。

代码语言:javascript
代码运行次数:0
运行
复制
// github/kubernetes/pkg/apis/core/v1/helper/qos/qos.gofunc GetPodQOS(pod *v1.Pod) v1.PodQOSClass {  requests := v1.ResourceList{}  limits := v1.ResourceList{}  zeroQuantity := resource.MustParse("0")  isGuaranteed := true  allContainers := []v1.Container{}  allContainers = append(allContainers, pod.Spec.Containers...)  // InitContainers 容器也会被加入 QoS 的计算中  allContainers = append(allContainers, pod.Spec.InitContainers...)  for _, container := range allContainers {    // 遍历 requests 中的 CPU 和 memory 值    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        }      }    }    // 遍历 limits 中的 CPU 和 memory 值    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        }      }    }    // 判断是否同时设置了 limits 和 requests,如果没有则不是 Guaranteed    if !qosLimitsFound.HasAll(string(v1.ResourceMemory), string(v1.ResourceCPU)) {      isGuaranteed = false    }  }  // 如果 requests 和 limits 都没有设置,则为 BestEffort  if len(requests) == 0 && len(limits) == 0 {    return v1.PodQOSBestEffort  }  // 检查所有资源的 requests 和 limits 是否都相等  if isGuaranteed {    for name, req := range requests {      if lim, exists := limits[name]; !exists || lim.Cmp(req) != 0 {        isGuaranteed = false        break      }    }  }  // 都设置了 requests 和 limits,则为 Guaranteed  if isGuaranteed &&    len(requests) == len(limits) {    return v1.PodQOSGuaranteed  }  return v1.PodQOSBurstable}

QoS 打分

QoS 会根据不同的分类进行 OOMScore 打分,当宿主机上内存不足时,系统会优先 kill 掉 OOMScore 分数高的进程。

QoS 打分

值得注意的是不久之前 guaranteedOOMScoreAdj 的值还是 -998,今年 9 月 22 日才合并 PR[1] 将其修改为 -997,而修改的 PR 及 相关 ISSUE[2] 在 2018 年就已经提出了,感兴趣的同学可以去看看。这里附上源码:

代码语言:javascript
代码运行次数:0
运行
复制
// github/kubernetes/pkg/kubelet/qos/policy.goconst (  // KubeletOOMScoreAdj is the OOM score adjustment for Kubelet  KubeletOOMScoreAdj int = -999  // KubeProxyOOMScoreAdj is the OOM score adjustment for kube-proxy  KubeProxyOOMScoreAdj  int = -999  guaranteedOOMScoreAdj int = -997  besteffortOOMScoreAdj int = 1000)func GetContainerOOMScoreAdjust(pod *v1.Pod, container *v1.Container, memoryCapacity int64) int {  // 高优先级 Pod 直接返回 guaranteedOOMScoreAdj  if types.IsCriticalPod(pod) {    // Critical pods should be the last to get killed.    return guaranteedOOMScoreAdj  }  // 根据 QoS 等级,返回 guaranteedOOMScoreAdj 或 besteffortOOMScoreAdj 的分数,这里只处理 Guaranteed 与 BestEffort  switch v1qos.GetPodQOS(pod) {  case v1.PodQOSGuaranteed:    // Guaranteed containers should be the last to get killed.    return guaranteedOOMScoreAdj  case v1.PodQOSBestEffort:    return besteffortOOMScoreAdj  }  memoryRequest := container.Resources.Requests.Memory().Value()  // 内存占用越少,分数越高  oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity  // 保证 Burstable 分数高于 Guaranteed  if int(oomScoreAdjust) < (1000 + guaranteedOOMScoreAdj) {    return (1000 + guaranteedOOMScoreAdj)  }  // 保证 Burstable 分数低于 BestEffect  if int(oomScoreAdjust) == besteffortOOMScoreAdj {    return int(oomScoreAdjust - 1)  }  return int(oomScoreAdjust)}

QoS 的本质区别

三种 QoS 在调度和实现都存在着区别:

•调度时,调度器只会根据 request 值进行调度,这也就解释了有些 Node 节点 Resource Limit 超出 100% 的情况•当 OOM 时,系统会根据 oom_score 值来选择优先 kill 掉的进程,分数越高越先被 kill 掉。oom_score 由系统计算所得,用户是不能设置的。但是如上文所述,而根据 QoS 的类型,kubelet 会计算出 oom_score_adj 的值,通过 oom_score_adj 来调整 oom_score 的分数,从而影响 OOM 被 kill 进程的优先级。•对于资源的限制,是由 CGroup 来完成的。kubelet 会为三种 QoS 分别创建 QoS level CGroup: •Guaranteed Pod Qos 的 CGroup level 会直接创建在 RootCgroup/kubepods 下 •Burstable Pod Qos 的创建在 RootCgroup/kubepods/burstable 下 •BestEffort Pod Qos 的创建在 RootCgroup/kubepods/BestEffort下•而在 Pod level CGroup 中还会创建 Container level CGroup,其结构如下图所示:

Qos-CGroup

结语

本文我们讨论了 Kubernetes 中 QoS 机制的分类、打分及其本质,除了这些 QoS 的实现 QOSContainerManager 中还有三种 QoS 以宿主机上 allocatable 资源量为基础为 Pod 分配资源,并通过多个 level cgroup 进行层层限制的逻辑,由于篇幅有限,就不做详细介绍了。

欢迎扫描二维码关注公众号,了解更多云原生知识

引用链接

[1] PR: https://github.com/kubernetes/kubernetes/pull/71269 [2] 相关 ISSUE: https://github.com/kubernetes/kubernetes/issues/72294

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云原生之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • QoS
    • QoS 分类
    • QoS 打分
    • QoS 的本质区别
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档