本文小编为大家详细介绍“Kubernetes Resource QoS Classes概念是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Kubernetes Resource QoS Classes概念是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
Kubernetes Resource QoS Classes介绍
基本概念
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的划分。
注意,如果一个容器只指明了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
Examples:
containers:
name: foo
resources:
name: bar
resources:
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掉.
管理和调度策略
如何根据不同的QoS回收Resources
Node上的OOM Score 配置
Pod OOM 打分配置
mm/oom_kill.c 中的badness()给每个进程一个OOM score,更高OOM得分的进程更容易被kill。得分取决于:
主要是看进程的内存消耗情况,包括驻留内存、pagetable和swap的使用
参考用户权限,比如root权限启动的进程,打分会减少30。
OOM打分因子:/proc/pid/oom_score_adj (加减) 和 /proc/pid/oom_adj(乘除)
这里提供一个计算系统上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
Guaranteed
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
Kubelet, Docker
已知的issue和潜在优化点
源码分析
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概念是什么”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注天达云行业资讯频道。