Kubernetes Resource QoS Classes介绍

news/2024/7/20 12:34:01 标签: 开发工具, 内存管理, 运维

基本概念


Kubernetes根据Pod中Containers Resource的request和limit的值来定义Pod的QoS Class。其中,指定容器request,代表系统确保能够提供的资源下限值。指定容器limit,代表系统允许提供的资源上限值。

Pods需要保证长期稳定运行需要设定“确保运行的最少资源”,然而pod能够使用的资源经常是不能确保的。

通常,Kubernetes通过设置request和limit的值来指定超卖比例,进而提升资源利用率。K8S的调度基于request,而不是limit。Borg通过使用“non-guranteed”的资源,提升了20%的资源利用率。

在一个资源被“超卖”的系统(总limits > machine capacity),容器在资源被耗尽的情况下会被kill。理想情况是那些“不重要”的容器先被kill。

对于每一种Resource都可以将容器分为3中QoS Classes: Guaranteed, Burstable, and Best-Effort,它们的QoS级别依次递减。K8S底层实际上是通过 limit和request值来实现不同等级QoS的划分。

  • Guaranteed 如果Pod中所有Container的所有Resource的limit和request都相等且不为0,则这个Pod的QoS Class就是Guaranteed。

注意,如果一个容器只指明了limit,而未指明request,则表明request的值等于limit的值。

Examples: containers:
 name: foo
 resources:
 limits:
 cpu: 10m
 memory: 1Gi
 name: bar
 resources:
 limits:
 cpu: 100m
 memory: 100Mi
containers:
 name: foo
 resources:
 limits:
 cpu: 10m
 memory: 1Gi
 requests:
 cpu: 10m
 memory: 1Gi

 name: bar
 resources:
 limits:
 cpu: 100m
 memory: 100Mi
 requests:
 cpu: 100m
 memory: 100Mi
  • Best-Effort 如果Pod中所有容器的所有Resource的request和limit都没有赋值,则这个Pod的QoS Class就是Best-Effort.
Examples: containers:
 name: foo
 resources:
 name: bar
 resources:
  • Burstable 除了符合Guaranteed和Best-Effort的场景,其他场景的Pod QoS Class都属于Burstable。 当limit值未指定时,其有效值其实是对应Node Resource的Capacity。

Examples: 容器bar没有对Resource进行指定。

containers:
 name: foo
 resources:
 limits:
 cpu: 10m
 memory: 1Gi
 requests:
 cpu: 10m
 memory: 1Gi

 name: bar

容器foo和bar对不同的Resource进行了指定。

containers:
 name: foo
 resources:
 limits:
 memory: 1Gi

 name: bar
 resources:
 limits:
 cpu: 100m

容器foo未指定limit,容器bar未指定request和limit。

containers:
 name: foo
 resources:
 requests:
 cpu: 10m
 memory: 1Gi

 name: bar

可压缩/不可压缩资源的区别

kube-scheduler调度时,是基于Pod的request值进行Node Select完成调度的。Pod和它的所有Container都不允许Consume limit指定的有效值(if have)。

request和limit如何生效,依赖于资源是否是压缩的

可压缩资源的保证

  • 目前仅支持CPU。
  • Pods确保可以获取请求的CPU总量,但并不能获得额外的CPU时间。这并不能完全确保容器能够用到设置的资源下限值,因为CPU隔离是容器级别的。之后会引入Pod级别的cgroups资源隔离来解决这个问题。
  • 过量/竞争使用CPU资源,会基于CPU request设置。可通过cpu.share来分派不同比例的时间片来理解,如果某个容器A的request 设置为600 milli,容器B设置为300mili , 两者竞争CPU时间时,通过2:1的比例来分配。
  • 如果达到Pod CPU资源limit上限,CPU会减速(throttled),而不是kill pod。如果pod没有设置limit上限,pods可以使用超过CPU limit上限。

不可压缩资源的保证

  • 目前仅支持内存。
  • Pods可以拿到requests设置的内存总量。如果某个pod超过memory request值,当其他pod需要内存时,这个pod可能被kill掉。但是如果pods使用内存少于request值,它们不会被kill,除非系统任务或daemon需要更多资源。(说白了,还是要看触发oom killer时,遍历系统上所有进程打分的情况。)
  • 当Pods使用内存超过了limit,某个在pod中容器内进程使用了大量内存,则该进程会被内核kill掉.

管理和调度策略

  • Pods由kubelet 确认和 scheduler 调度,会基于分配给容器的requests值,确保所有容器的requests总量在Node可分配容量的范围之内。https://github.com/fabric8io/jenkinshift/blob/master/vendor/k8s.io/kubernetes/docs/proposals/node-allocatable.md

如何根据不同的QoS回收Resources

  • CPU 当CPU使用不能达到request值,比如系统任务和daemons使用了大量CPU,则Pods不会被kill,CPU效率会下降(throttled)。
  • Memory 内存是不可压缩资源,从内存管理的角度做如下区分:
    • Best-Effort pods 优先级最低。如果系统内存耗尽,该类型的pods中的进程最先被kill。这些容器可以使用系统上任意量的空闲内存。
    • Guaranteed pods 优先级最高。它们能够确保不达到容器设置的limit上限一定不会被kill。只有在系统存在内存压力且没有更低优先级容器时才被驱逐。
    • Burstable pods 有一些形式的最小资源保证,但当需要时可以使用更多资源。在系统存在内存瓶颈时,一旦内存超过他们的request值并且没有Best-Effort 类型的容器存在,这些容器就先被kill掉。

Node上的OOM Score 配置

Pod OOM 打分配置

mm/oom_kill.c 中的badness()给每个进程一个OOM score,更高OOM得分的进程更容易被kill。得分取决于:

  • 主要是看进程的内存消耗情况,包括驻留内存、pagetable和swap的使用
    • 一般是内存耗费的百分比*10(percent-times-ten)
  • 参考用户权限,比如root权限启动的进程,打分会减少30。
  • OOM打分因子:/proc/pid/oom_score_adj (加减) 和 /proc/pid/oom_adj(乘除)
    • oom_adj: -15~ 15的系数调整
    • oom_score_adj:oom_score会加上oom_score_adj这个值
    • 最终oom score的值 还是在 0~1000

这里提供一个计算系统上oom_score分数TPO10进程(最容易被oom killer杀掉的进程)脚本:

# vim oomscore.sh #!/bin/bash for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do printf "%2d %5d %s\n" \
 "$(cat $proc/oom_score)" \
 "$(basename $proc)" \
 "$(cat $proc/cmdline | tr '\0' ' ' | head -c 50)" done 2>/dev/null | sort -nr | head -n 10

以下是几种K8S QoS 等级的OOM score:

Best-effort

  • Set OOM_SCORE_ADJ: 1000
  • 所以best-effort容器的OOM_SCORE 值为1000

Guaranteed

  • Set OOM_SCORE_ADJ: -998
  • 所以guaranteed容器的OOM_SCORE 值为0 或 1

Burstable

  • 如果总的memory request 大于 99.9%的可用内存,OOM_SCORE_ADJ设置为 2。否则, OOM_SCORE_ADJ = 1000 - 10 * (% of memory requested),这确保了burstable的 POD OOM_SCORE > 1
  • 如果memory request设置为0,OOM_SCORE_ADJ 默认设置为999。所以如果burstable pods和guaranteed pods冲突时,前者会被kill。
  • 如果burstable pod使用的内存少于request值,那它的OOM_SCORE < 1000。如果best-effort pod和这些 burstable pod冲突时,best-effort pod会先被kill掉。
  • 如果 burstable pod容器中进程使用比request值的内存更多,OOM_SCORE设置为1000。反之,OOM_SCORES少于1000。
  • 在一堆burstable pod中,使用内存超过request值的pod,优先于内存使用少于request值的pod被kill。
  • 如果 burstable pod 有多个进程冲突,则OOM_SCORE会被随机设置,不受“request & limit”限制。

Pod infra containers or Special Pod init process

  • OOM_SCORE_ADJ: -998

Kubelet, Docker

  • OOM_SCORE_ADJ: -999 (won’t be OOM killed)
  • 系统上的关键进程,如果和guranteed 进程冲突,则会优先被kill 。将来会被放到一个单独的cgroup中,并且限制内存。

已知的issue和潜在优化点

  • 支持swap: 当前QoS策略默认swap关闭。如果开启swap,那些guaranteed 容器资源使用达到limit值,还可以使用磁盘来提供内存分配。最终,当swap空间不够时,pod中的进程才会被kill.此时,node需要在提供隔离策略时,把swap空间考虑进去。
  • 提供用户指定优先级:用户让kubelet指定哪些tasks可以被kill.

源码分析

QoS的源码位于:pkg/kubelet/qos,代码非常简单,主要就两个文件pkg/kubelet/qos/policy.go,pkg/kubelet/qos/qos.go。 上面讨论的各个QoS Class对应的OOM_SCORE_ADJ定义在:

pkg/kubelet/qos/policy.go:21 const (
 PodInfraOOMAdj int = -998
 KubeletOOMScoreAdj int = -999
 DockerOOMScoreAdj int = -999
 KubeProxyOOMScoreAdj int = -999
 guaranteedOOMScoreAdj int = -998
 besteffortOOMScoreAdj int = 1000
)

容器的OOM_SCORE_ADJ的计算方法定义在:

pkg/kubelet/qos/policy.go:40 func GetContainerOOMScoreAdjust(pod *v1.Pod, container *v1.Container, memoryCapacity int64) int {
 switch GetPodQOS(pod) {
 case Guaranteed:
 // Guaranteed containers should be the last to get killed. return guaranteedOOMScoreAdj
 case BestEffort:
 return besteffortOOMScoreAdj
 }

 // Burstable containers are a middle tier, between Guaranteed and Best-Effort. Ideally, // we want to protect Burstable containers that consume less memory than requested. // The formula below is a heuristic. A container requesting for 10% of a system's // memory will have an OOM score adjust of 900. If a process in container Y // uses over 10% of memory, its OOM score will be 1000. The idea is that containers // which use more than their request will have an OOM score of 1000 and will be prime // targets for OOM kills. // Note that this is a heuristic, it won't work if a container has many small processes.
 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)
}

获取Pod的QoS Class的方法为:

pkg/kubelet/qos/qos.go:50 // GetPodQOS returns the QoS class of a pod. // A pod is besteffort if none of its containers have specified any requests or limits. // A pod is guaranteed only when requests and limits are specified for all the containers and they are equal. // A pod is burstable if limits and requests do not match across all containers. func GetPodQOS(pod *v1.Pod) QOSClass {
 requests := v1.ResourceList{}
 limits := v1.ResourceList{}
 zeroQuantity := resource.MustParse("0")
 isGuaranteed := true for _, container := range pod.Spec.Containers {
 // process requests for name, quantity := range container.Resources.Requests {
 if !supportedQoSComputeResources.Has(string(name)) {
 continue
 }
 if quantity.Cmp(zeroQuantity) == 1 {
 delta := quantity.Copy()
 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 !supportedQoSComputeResources.Has(string(name)) {
 continue
 }
 if quantity.Cmp(zeroQuantity) == 1 {
 qosLimitsFound.Insert(string(name))
 delta := quantity.Copy()
 if _, exists := limits[name]; !exists {
 limits[name] = *delta
 } else {
 delta.Add(limits[name])
 limits[name] = *delta
 }
 }
 }

 if len(qosLimitsFound) != len(supportedQoSComputeResources) {
 isGuaranteed = false
 }
 }
 if len(requests) == 0 && len(limits) == 0 {
 return BestEffort
 }
 // 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 Guaranteed
 }
 return Burstable
}

PodQoS会在eviction_manager和scheduler的Predicates阶段被调用,也就说会在k8s处理超配和调度预选阶段中被使用。

本文转自开源中国-Kubernetes Resource QoS Classes介绍


http://www.niftyadmin.cn/n/541679.html

相关文章

java提高篇(四)-----理解java的三大特性之多态

面向对象编程有三大特性&#xff1a;封装、继承、多态。 封装隐藏了类的内部实现机制&#xff0c;可以在不影响使用的情况下改变类的内部结构&#xff0c;同时也保护了数据。对外界而已它的内部细节是隐藏的&#xff0c;暴露给外界的只是它的访问方法。 继承是为…

hdu_5769_Substring(后缀数组)

题目链接&#xff1a;hdu_5769_Substring 题意&#xff1a; 给你一个字符a和一个串b&#xff0c;问你有多少个包括a的字串 题解&#xff1a; 1 #include<bits/stdc.h>2 #define F(i,a,b) for(int ia;i<b;i)3 using namespace std;4 typedef long long ll;5 namespace …

「洛谷P2891」[USACO07OPEN]吃饭Dining 解题报告

P2891 [USACO07OPEN]吃饭Dining 题目描述 Cows are such finicky eaters. Each cow has a preference for certain foods and drinks, and she will consume no others. Farmer John has cooked fabulous meals for his cows, but he forgot to check his menu against their p…

iOS开发-UIImage的常用分类

// // UIImageZZ.h // ZZ_APP主流框架 // // Created by ZZ_Macpro on 15/9/25. // Copyright (c) 2015年 ZZ_Macpro. All rights reserved. // #import <UIKit/UIKit.h> interface UIImage (ZZ) /** * 返回一张没有渲染的原始图片 * * param name 文件名 *…

关于PackageInfo、ApplicationInfo、ActivityInfo、ResolveInfo四种信息类的区别之我见

关于PackageInfo、ApplicationInfo、ActivityInfo、ResolveInfo四种信息类的区别之我见 2014年09月05日 ⁄ 综合 ⁄ 共 2871字 ⁄ 字号 小 中 大 ⁄ 评论关闭PackageInfo&#xff1a; 获得方法&#xff1a; [java] view plaincopy print?PackageManager packageManager conte…

学软件测试最好的几本书,这8本书能帮你很多

真的勇士&#xff0c; 敢于直面惨淡的 warning 、 敢于正视淋漓的 error !有人喜欢创造世界&#xff0c;他们做了开发者&#xff1b;有的人喜欢开发者&#xff0c;他们做了测试员。什么是软件测试&#xff1f;软件测试就是一场本该在用户面前发生的灾难提前在自己面前发生了&am…

理解js中的原型链,prototype与__proto__的关系

说到prototype&#xff0c;就不得不先说下new的过程。 我们先看看这样一段代码&#xff1a; <script type"text/javascript">var Person function () { };var p new Person();</script> 很简单的一段代码&#xff0c;我们来看看这个new究竟做了什么&am…

iOS开发-自定义UIBarButtonItem

// // UIBarButtonItemZZ.h // ZZ_APP主流框架 // // Created by ZZ_Macpro on 15/9/27. // Copyright (c) 2015年 ZZ_Macpro. All rights reserved. // #import <UIKit/UIKit.h> interface UIBarButtonItem (ZZ) /** * 快速创建一个item对象 (内部包装一个U…