# IT明星不是夢 # 圖解kubernetes資源QOS機制實現原理

QOS是k8s中一種資源保護機制,其主要是針對不可壓縮資源比如的內存的一種控制技術,比如在內存中其通過爲不同的Pod和容器構造OOM評分,並且通過內核的策略的輔助,從而實現當節點內存資源不足的時候,內核可以按照策略的優先級,優先kill掉哪些優先級比較低(分值越高優先級越低)的Pod,今天來分析下背後的實現

1.關鍵基礎特性

image.png

1.1 一切皆文件

在Linux中一切皆文件,控制CGroup本身也是通過配置文件進行的,這是我創建的一個內存Lmits爲200M的Pod的容器的配置

# pwd
/sys/fs/cgroup
# cat ./memory/kubepods/pod8e172a5c-57f5-493d-a93d-b0b64bca26df/f2fe67dc90cbfd57d873cd8a81a972213822f3f146ec4458adbe54d868cf410c/memory.limit_in_bytes
209715200

1.2 內核內存配置

這裏我們重點關注內存相關的兩個配置:VMOvercommitMemory其值爲1,表示運行分配所有的物理內存資源,注意不包括SWAP資源VMPanicOnOOM其值爲0:表示當內存不足的時候觸發oom_killer進行選擇部分進程進行kill,QOS也是通過影響其kill流程來實現的

func setupKernelTunables(option KernelTunableBehavior) error {
    desiredState := map[string]int{
        utilsysctl.VMOvercommitMemory: utilsysctl.VMOvercommitMemoryAlways,
        utilsysctl.VMPanicOnOOM:       utilsysctl.VMPanicOnOOMInvokeOOMKiller,
        utilsysctl.KernelPanic:        utilsysctl.KernelPanicRebootTimeout,
        utilsysctl.KernelPanicOnOops:  utilsysctl.KernelPanicOnOopsAlways,
        utilsysctl.RootMaxKeys:        utilsysctl.RootMaxKeysSetting,
        utilsysctl.RootMaxBytes:       utilsysctl.RootMaxBytesSetting,
    }

2.QOS打分機制與判定實現

QOS打分機制主要是根據Requests和limits裏面的資源限制來進行類型判定與打分的,我們就來快速看下這部分的實現

2.1 根據容器判定QOS類型

2.1.1 構建容器列表

遍歷所有的容器列表,注意這裏會包含所有的初始化容器和業務容器

    requests := v1.ResourceList{}
    limits := v1.ResourceList{}
    zeroQuantity := resource.MustParse("0")
    isGuaranteed := true
    allContainers := []v1.Container{}
    allContainers = append(allContainers, pod.Spec.Containers...)
// 追加所有的初始化容器 
    allContainers = append(allContainers, pod.Spec.InitContainers...)

2.1.2 處理Requests和limits

這裏遍歷所有的Requests和Limits限制的資源,分別加入到不同的資源集合彙總,其中判定是不是Guaranteed主要是根據limits裏面的資源是否包含CPU和內存兩種資源,都包含纔可能是Guaranteed

    for _, container := range allContainers {
        // process requests
        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
                }
            }
        }
        // process limits
        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
                }
            }
        }

        if !qosLimitsFound.HasAll(string(v1.ResourceMemory), string(v1.ResourceCPU)) {
            // 必須是全部包含cpu和內存限制
            isGuaranteed = false
        }
    }

2.1.3 BestEffort

如果Pod裏面的容器沒有任何requests和limits的限制則就是BestEffort

    if len(requests) == 0 && len(limits) == 0 {
        return v1.PodQOSBestEffort
    }

2.1.4 Guaranteed

要是Guaranteed必須是資源相等,並且限定的數量相同

    // 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 v1.PodQOSGuaranteed
    }

2.1.5 Burstable

如果不是上面兩種就是最後一種burstable了

    return v1.PodQOSBurstable

2.2 QOS OOM打分機制

2.2.1 OOM打分機制

其中guaranteedOOMScoreAdj是-998其實這跟OOM實現有關係,一臺node節點上主要是三部分組成:kubelet主進程、docker進程、業務容器進程,而OOM的打分裏面-1000表示該進程不會被oom所kill, 那一個業務進程最少也就只能是-999因爲你不能保證自己的業務永遠不會出現問題,所以在QOS裏面-999其實就是kubelet和docker進程所保留的,剩下的才能作爲業務容器分配(分值越高越容易被kill)

    // KubeletOOMScoreAdj is the OOM score adjustment for Kubelet
    KubeletOOMScoreAdj int = -999
    // DockerOOMScoreAdj is the OOM score adjustment for Docker
    DockerOOMScoreAdj int = -999
    // KubeProxyOOMScoreAdj is the OOM score adjustment for kube-proxy
    KubeProxyOOMScoreAdj  int = -999
    guaranteedOOMScoreAdj int = -998
    besteffortOOMScoreAdj int = 1000

2.2.2 關鍵Pod

關鍵Pod是一種特殊的存在,它可以是Burstable或者BestEffort類型的Pod,但是OOM打分卻可以跟Guaranteed一樣,這種類型的Pod主要包含三種:靜態Pod、鏡像Pod和高優先級Pod

    if types.IsCriticalPod(pod) {
        return guaranteedOOMScoreAdj
    }

判定實現

func IsCriticalPod(pod *v1.Pod) bool {
    if IsStaticPod(pod) {
        return true
    }
    if IsMirrorPod(pod) {
        return true
    }
    if pod.Spec.Priority != nil && IsCriticalPodBasedOnPriority(*pod.Spec.Priority) {
        return true
    }
    return false
}

2.2.3 Guaranteed與BestEffort

這兩種類型都有各自默認的值分別爲Guaranteed(-998)和BestEffort(1000)

    switch v1qos.GetPodQOS(pod) {
    case v1.PodQOSGuaranteed:
        // Guaranteed containers should be the last to get killed.
        return guaranteedOOMScoreAdj
    case v1.PodQOSBestEffort:
        return besteffortOOMScoreAdj
    }

2.2.4 Burstable

其中關鍵的一行就是:oomScoreAdjust := 1000 - (1000memoryRequest)/memoryCapacity,從這個計算裏面可以看出,如果我們申請的資源越多,那麼 (1000memoryRequest)/memoryCapacity這個裏面計算出來的時機值就會越小,即最終結果就越大,其實也就表明如果我們佔用的內存越少,則打分就越高,這類容器就相對比較容易被kill


    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)

好了今天就到這裏,看之前還很懵逼,看完有種豁然開朗的感覺,還是那句話說的對,源碼面前了無祕密,加油

k8s源碼閱讀電子書地址: https://www.yuque.com/baxiaoshi/tyado3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章