10.4.3 资源服务质量管理(Resource QoS)
本节对Kubernetes如何根据Pod的Requests和Limits配置来实现针对Pod的不同级别的资源服务质量控制(QoS)进行说明。
在Kubernetes的资源QoS体系中,需要保证高可靠性的Pod可以申请可靠资源,而一些不需要高可靠性的Pod可以申请可靠性较低或者不可靠的资源。在10.4.1节中讲到了容器的资源配置分为Requests和Limits,其中Requests是Kubernetes调度时能为容器提供的完全可保障的资源量(最低保障),而Limits是系统允许容器运行时可能使用的资源量的上限(最高上限)。Pod级别的资源配置是通过计算Pod内所有容器的资源配置的总和得出来的。
Kubernetes中Pod的Requests和Limits资源配置有如下特点。
(1)如果Pod配置的Requests值等于Limits值,那么该Pod可以获得的资源是完全可靠的。
(2)如果Pod的Requests值小于Limits值,那么该Pod获得的资源可分成两部分:
◎ 完全可靠的资源,资源量的大小等于Requests值;
◎ 不可靠的资源,资源量最大等于Limits与Requests的差额,这份不可靠的资源能够申请到多少,取决于当时主机上容器可用资源的余量。
通过这种机制,Kubernetes可以实现节点资源的超售(Over Subscription),比如在CPU完全充足的情况下,某机器共有32GiB内存可提供给容器使用,容器配置为Requests值1GiB,Limits值为2GiB,那么在该机器上最多可以同时运行32个容器,每个容器最多可以使用2GiB内存,如果这些容器的内存使用峰值能错开,那么所有容器都可以正常运行。
超售机制能有效提高资源的利用率,同时不会影响容器申请的完全可靠资源的可靠性。
1.Requests和Limits对不同计算资源类型的限制机制
根据前面的内容可知,容器的资源配置满足以下两个条件:
◎ Requests<=节点可用资源;
◎ Requests<=Limits。
Kubernetes根据Pod配置的Requests值来调度Pod,Pod在成功调度之后会得到Requests值定义的资源来运行;而如果Pod所在机器上的资源有空余,则Pod可以申请更多的资源,最多不能超过Limits的值。下面看一下Requests和Limits针对不同计算资源类型的限制机制的差异。这种差异主要取决于计算资源类型是可压缩资源还是不可压缩资源。
1)可压缩资源
◎ Kubernetes目前支持的可压缩资源是CPU。
◎ Pod可以得到Pod的Requests配置的CPU使用量,而能否使用超过Requests值的部分取决于系统的负载和调度。不过由于目前Kubernetes和Docker的CPU隔离机制都是在容器级别隔离的,所以Pod级别的资源配置并不能完全得到保障;Pod级别的cgroups正在紧锣密鼓地开发中,如果将来引入,就可以确保Pod级别的资源配置准确运行。
◎ 空闲CPU资源按照容器Requests值的比例分配。举例说明:容器A的CPU配置为Requests 1 Limits 10,容器B的CPU配置为Request 2 Limits 8,A和B同时运行在一个节点上,初始状态下容器的可用CPU为3cores,那么A和B恰好得到在它们的Requests中定义的CPU用量,即1CPU和2CPU。如果A和B都需要更多的CPU资源,而恰好此时系统的其他任务释放出1.5CPU,那么这1.5CPU将按照A和B的Requests值的比例1∶2分配给A和B,即最终A可使用1.5CPU,B可使用3CPU。
◎ 如果Pod使用了超过在Limits 10中配置的CPU用量,那么cgroups会对Pod中的容器的CPU使用进行限流(Throttled);如果Pod没有配置Limits 10,那么Pod会尝试抢占所有空闲的CPU资源(Kubernetes从1.2版本开始默认开启--cpu-cfs-quota,因此在默认情况下必须配置Limits)。
2)不可压缩资源
◎ Kubernetes目前支持的不可压缩资源是内存。
◎ Pod可以得到在Requests中配置的内存。如果Pod使用的内存量小于它的Requests的配置,那么这个Pod可以正常运行(除非出现操作系统级别的内存不足等严重问题);如果Pod使用的内存量超过了它的Requests的配置,那么这个Pod有可能被Kubernetes杀掉:比如Pod A使用了超过Requests而不到Limits的内存量,此时同一机器上另外一个Pod B之前只使用了远少于自己的Requests值的内存,此时程序压力增大,Pod B向系统申请的总量不超过自己的Requests值的内存,那么Kubernetes可能会直接杀掉Pod A;另外一种情况是Pod A使用了超过Requests而不到Limits的内存量,此时Kubernetes将一个新的Pod调度到这台机器上,新的Pod需要使用内存,而只有Pod A使用了超过了自己的Requests值的内存,那么Kubernetes也可能会杀掉Pod A来释放内存资源。
◎ 如果Pod使用的内存量超过了它的Limits设置,那么操作系统内核会杀掉Pod所有容器的所有进程中使用内存最多的一个,直到内存不超过Limits为止。
2.对调度策略的影响
◎ Kubernetes的kubelet通过计算Pod中所有容器的Requests的总和来决定对Pod的调度。
◎ 不管是CPU还是内存,Kubernetes调度器和kubelet都会确保节点上所有Pod的Requests的总和不会超过在该节点上可分配给容器使用的资源容量上限。
3.服务质量等级(QoS Classes)
在一个超用(Over Committed,容器Limits总和大于系统容量上限)系统中,由于容器负载的波动可能导致操作系统的资源不足,最终可能导致部分容器被杀掉。在这种情况下,我们当然会希望优先杀掉那些不太重要的容器,那么如何衡量重要程度呢?Kubernetes将容器划分成3个QoS等级:Guaranteed(完全可靠的)、Burstable(弹性波动、较可靠的)和BestEffort(尽力而为、不太可靠的),这三种优先级依次递减,如图10.4所示。
从理论上来说,QoS级别应该作为一个单独的参数来提供API,并由用户对Pod进行配置,这种配置应该与Requests和Limits无关。但在当前版本的Kubernetes的设计中,为了简化模式及避免引入太多的复杂性,QoS级别直接由Requests和Limits来定义。
在Kubernetes中容器的QoS级别等于容器所在Pod的QoS级别,而Kubernetes的资源配置定义了Pod的三种QoS级别,如下所述。
1)Guaranteed
如果Pod中的所有容器对所有资源类型都定义了Limits和Requests,并且所有容器的Limits值都和Requests值全部相等(且都不为0),那么该Pod的QoS级别就是Guaranteed。注意:在这种情况下,容器可以不定义Requests,因为Requests值在未定义时默认等于Limits。
在下面这两个例子中定义的Pod QoS级别就是Guaranteed:
apiVersion: v1 kind: Pod metadata:name: guaranteed-pod spec:containers:- name: nginximage: nginxresources:requests:cpu: "500m"memory: "128Mi"limits:cpu: "500m"memory: "128Mi"
对于未定义Requests值,所以其默认等于Limits值
2) Burstable
Burstable级别的Pod包括两种情况。
第1种情况:Pod中的一部分容器在一种或多种资源类型的资源配置中定义了Requests值和Limits值(都不为0),且Requests值小于Limits值;
第2种情况:Pod中的一部分容器未定义资源配置(Requests和Limits都未定义)。注意:在容器未定义Limits时,Limits值默认等于节点资源容量的上限。
如果Pod中所有容器都未定义资源配置(Requests和Limits都未定义),那么该Pod的QoS级别就是BestEffort。
- Pod 中至少一个容器配置了 CPU 或 内存 的
requests
。 - Pod 中存在容器的
requests
和limits
不相等,或者部分容器没有limits
。
apiVersion: v1 kind: Pod metadata:name: burstable-pod spec:containers:- name: nginximage: nginxresources:requests:cpu: "250m"memory: "64Mi"limits:cpu: "500m"memory: "128Mi"- name: sidecarimage: busyboxresources:requests:cpu: "100m"
2)BestEffort
如果Pod中所有容器都未定义资源配置(Requests和Limits都未定义),那么该Pod的QoS级别就是BestEffort。
apiVersion: v1 kind: Pod metadata:name: besteffort-pod spec:containers:- name: nginximage: nginx
4)Kubernetes QoS的工作特点
Pod的CPU Requests无法得到满足(比如节点的系统级任务占用过多的CPU导致无法分配足够的CPU给容器使用)时,容器得到的CPU会被压缩限流。
由于内存是不可压缩的资源,所以针对内存资源紧缺的情况,会按照以下逻辑进行处理。
(1)BestEffort Pod的优先级最低,在这类Pod中运行的进程会在系统内存紧缺时被第一优先杀掉。当然,从另外一个角度来看,BestEffort Pod由于没有设置资源Limits,所以在资源充足时,它们可以充分使用所有的闲置资源。
(2)Burstable Pod的优先级居中,这类Pod初始时会分配较少的可靠资源,但可以按需申请更多的资源。当然,如果整个系统内存紧缺,又没有BestEffort容器可以被杀掉以释放资源,那么这类Pod中的进程可能会被杀掉。
(3)Guaranteed Pod的优先级最高,而且一般情况下这类Pod只要不超过其资源Limits的限制就不会被杀掉。当然,如果整个系统内存紧缺,又没有其他更低优先级的容器可以被杀掉以释放资源,那么这类Pod中的进程也可能会被杀掉。