一文读懂k8s中的QoS机制 导读 QoS介绍 Qos In

这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

pexels-miguel-constantin-montes-2623968.jpg

导读

QoS(Quality of Servcie)对于大家并不陌生,本义是对外承诺的服务质量保证,越重要的服务承诺的要求就会越高,关于QoS更详细的说明可以参考参考文档[1]

在Kubernetes语义下,QoS是针对POD来定义的,描述了POD在被调度和驱逐时的优先顺序,它对POD的生命周期有直接的影响。POD的QoS类别有三种,分别是Guaranteed、Burstable和BestEffort。具体含义请继续往下读~!

QoS介绍

QoS起源于网络的服务质量,它提供了针对不同用户或者不同数据流采用相应不同的优先级,或者是根据应用程序的要求,保证数据流的性能达到一定的水准。QoS的保证对于容量有限的网络来说是十分重要的,特别是对于流多媒体应用,例如VoIPIPTV等,因为这些应用常常需要固定的传输率,对延时也比较敏感。[2]

Qos In Kubernetes

在Kubernetes系统中,QoS(Quality of Servcie)级别决定了POD的调度和驱逐优先级,从而可以为集群之上的用户提供可靠的符合预期的服务保障。QoS级别越高,获得的保障支持优先级就越高。

在Kubernetes中,QoS并不是由用户直接设置的,而是由Kubernetes自己根据用户对资源的请求设置计算得到的,总共有三种级别,由高到低分别是:Guaranteed、Burstable和BestEffort。下面分别介绍三种等级如何设置的。

Guaranteed

资源设置

当POD内所有容器的的资源申请requests的cpu和memory与limits的cpu和memory想等时,其QoS级别将会被设置为Guaranteed。一个快捷的方式是只设置limits的cpu和memory,Kubernetes会自动设置requests与limits相等的值。

1
2
3
4
5
6
7
8
9
10
11
yaml复制代码Kind: Pod
spec:
containers:
- image: busybox
resource:
requests:
cpu: 200m
memory: 10Mi
limits:
cpu: 200m
memory: 10Mi

对调度的影响

Kubernetes调度器只会讲Guaranteed类型的POD调度到完全满足资源请求的节点上。

如果kubelet上报了一个节点状态DiskPressure,Guaranteed POD将不会被调度到这个节点上。当节点的根文件系统或者镜像文件系统的可用磁盘空间和inodes数达到驱逐的阈值时,DiskPressure节点状态将会被触发。当一个节点上报DiskPressure状态时,调度器将停止将新的Guaranteed POD调度到其上。

独占CPU

在Kubernetes的默认CPU管理策略”None”下,Guaranteed POD只能使用节点上的共享CPU池资源,该共享CPU池资源包含节点上所有CPU资源减去kubelet使用–kube-reserved或–system-reserved预留的资源。

在static CPU管理策略下,Guaranteed POD可以申请到独占的CPU资源。在这个策略下,Guaranteed POD的cpu requests值必须是整数值才可以真正获得独占CPU,否则仍然是获得共享CPU池资源。

Burstable

资源设置

如果一个POD中至少一个容器只设置了requests的cpu或memory,或者都设置但是与limits的设置不同,则该POD的QoS将被设置为Burstable。

1
2
3
4
5
6
7
8
yaml复制代码Kind: Pod
spec:
containers:
- image: busybox
resource:
requests:
cpu: 200m
memory: 10Mi

对调度的影响

调度器不能确保Burstable POD可以被调度到完全满足其资源需求的节点。

Burstable POD同样不能被调度到已经上报了DiskPressure状态的节点。调度器不会将任何新的Burstable POD调度到DiskPressure状态的节点。

独占CPU

在”None” CPU管理策略下,Burstable POD必须与BestEffort、Guaranteed POD一起共享资源池,不能被分配独占CPU资源。

BestEffort

资源设置

如果POD中所有的容器都没有设置requests和limits的cpu和memory,则该POD的QoS将被设置为BestEffort。

1
2
3
4
yaml复制代码Kind: Pod
spec:
containers:
- image: busybox

对调度的影响

调度器同样不保证BestEffort POD被调度到有充足资源的节点。然而,它们可以使用节点上任意数量的可用的CPU和memory资源。有时候当BestEffort POD贪婪的占用资源,给其他POD申请预留的资源不足时,将会导致与其他POD的资源竞争。

BestEffort POD不能被调度到状态是DiskPressure和MemoryPressure的节点。当节点可用内存低于预定义的阈值时,将会上报MemoryPressure状态。调度器就会停止调度任何新的BestEffort POD到这些节点上。

独占CPU

与Burstable POD相同,BestEffort POD只能使用共享资源池,不能独占CPU资源。

驱逐

接下来让我们一起看一下,在节点资源不足,例如内存耗尽时,QoS是如何影响kubelet驱逐POD的。

kubelet如何驱逐Guaranteed、Burstable、BestEffort POD

当节点可用计算资源较低时,由kubelet触发POD驱逐。这些驱逐意味着要回收资源以免系统出现OOM。DevOPs可用设置资源的阈值,当这个阈值达到时就由kubelet触发pod驱逐。

POD的QoS级别确实会影响kubelet挑选驱逐对象的顺序。kubelet首先驱逐超出资源请求的BestEffort和Burstable级别的POD。驱逐的顺序依赖于被分配的驱逐优先级以及所消耗资源超出资源请求的量。

Guaranteed和Burstable Pod的资源使用量低于请求值时会有最低的驱逐优先级,因而不会被驱逐。

在发生DiskPressure状态的节点上,kubelet首先驱逐的是BestEffort POD,然后是Burstable POD。只有当前节点不再有BestEffort和Burstable POD之后,才开始驱逐Guaranteed POD。

Guaranteed、Burstable、BestEffort POD的节点OOM行为是什么

节点在kubelet回收资源之前发生OOM时,oom_killer将会基于容器的oom_score值直接杀掉容器。oom_killer会为每一个容器计算出来一个oom_score值,计算方式是基于容器使用的资源占其所申请资源的比例再加上oom_score_adj值。

每个容器的oom_score_adj由其所属的POD的QoS级别所确定。

Quality of Service oom_score_adj
Guaranteed -998
BestEffort 1000
Burstable min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

oom_killer首先会结束QoS级别最低的POD并且使用资源超出请求量最多的容器。这意味着较高QoS级别POD比低级别POD的容器有更低的被杀掉的可能性。但是如果较高级别POD的容器内存使用量超出请求量更大时也可能会被优先清除掉,因为oom_score不仅仅不与QoS有关系,还取决于容器实际内存的使用量。

深入思考

为什么会有这样的三个不同级别,只使用一个级别不可以吗?

我们先回到现实的需求,差不多是这样的

  1. 资源利用率最大化,资源超卖越多越好
  2. 关键业务要有一定的稳定性保障

为了同时满足这个几乎是相反方向的目标,我们必须要提供更灵活的服务级别,于是只使用一个级别不能同时满足这两个目标,需要有不同的级别来保障有不同要求的服务。对于关键性较低,对稳定性保障要求也较低的服务我们可以适当的超卖资源,从而获得资源更大程度的利用率;而对于关键的服务,则允许服务资源存在一定的浪费,从而实现了业务稳定优先。

为什么分三个呢?个人理解主要是因为Kubernetes中QoS主要取决于requests和limits的CPU和memory的值,这两组四个值不同的配置可以分为三种场景,正好可以对应到当前的三种界别,因而分为三种级别是最自然的一种方式。

在实际使用中,Burstable和BestEffort差别并不是很大,而且从业务稳定性角度考虑,系统工程师会提前规划系统的容量,在出现集群资源不足之前就应该提前介入,而不是依赖kubelet或者oom_killer的行为。kubelet和oom_killer更多的是在业务出现异常资源申请时可以及时制止以维护整体集群的稳定。

实战

在实际使用中应该怎样在保证业务稳定的同时尽量提高资源利用率呢?

我推荐的核心原则是依据业务的重要程度以及集群的特性。例如在生产环境集群,推荐所有POD设置为Guaranteed级别,因为我认为生产环境的业务是最重要的,资源超卖相对来说不重要。当然怎么最大可能的降低资源浪费就是一个很重要的问题了。而在线下的测试开发集群则不同,对业务的稳定性要求不高,而对资源超卖相对变得更高,此时POD都可以设置为Burstable或者BestEffort,我们当前测试开发集群都是Burstable。

生产环境如何降低资源的浪费?主要出发点是基于历史的资源使用率设置一个稍微超出一点的请求值,为了应对流量的突增或者抖动,可以附以HPA来保证在请求量变大时可以动态扩容,不需要太多服务实例时再自动缩容。

QoS总结

  • 通过requests和limits来定义QoS等级

  • 内存不足时,POD被驱逐的级别顺序是BestEffort、Burstable、Guaranteed
  • oom_killer杀掉容器时不仅参考容器所属POD的QoS级别,还会参考容器实际资源使用量

参考文档

  1. baike.baidu.com/item/QoS
  2. zh.wikipedia.org/wiki/服务质量

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%