kubelet - container manager

containerManager 負責 node 節點上運行的容器的配置信息,如cgroupcpudevice

 

pod的創建流程參考:

http://www.tianfeiyu.com/?p=2825


 

一構成

1.QOSContainerManager維護pod對應的Qos,用於調度、驅逐、設置各資源的cgroup等模塊

2.cgroupManager根據pod的Qos設置不同級別的cgroup參數,並不斷根據pod的變化維護這些cgroup參數

3.devicemanager管理節點上的device,主要包含分配、回收、本地記錄存儲;

4.cpumanager管理節點上的cpu,主要包含分配、回收、本地記錄存儲

5.devicemanager通過topologymanagercontainer選擇最優的device組合

6.cpumanager通過topologymanagercontainer選擇最優cpuset

 

調用流程:

1.kubeRuntimeManager.SyncPod創建完container後,通過cpumanager設置container的啓動參數;

2.kubeRuntimeManager.SyncPod.startContainer啓動container時會調用containerManager獲取container跑在device上的參數,最終回調用到devicemanager

3.Kubelet.SyncPod設置cgroup參數階段-->podContainerManager-->cgroupManager(依賴QOSContainerManager設置QOS)-->runc.libcontainer.cgroupfs將設置的參數apply到container

 

 

二、Kubelet.syncPod vs kubeRuntimeManager.SyncPod:調用關係:

 

Kubelet.syncPod

1.如果pod需要被刪除,調用containerRuntime.KillPod殺掉pod

2.爲第一次創建的pod記錄創建時間,當pod狀態變化時也記錄當前時間

3.通過一系列admit判斷pod是否能在當前node上運行,如安全相關的appArmorValidatorcontainerRuntime是否允許root Privilege

4.設置podcgroup參數

5.static pod創建mirror pod

6.創建相應的工作目錄,如pod的工作目錄、存儲目錄、插件plugins目錄

7,等待volume掛載到pod的存儲目錄下

8.拉取下載container imagesecret

9.調用kubeRuntimeManager.SyncPodpod創建container

10.statusManager更新pod的狀態

 

kubeRuntimeManager.SyncPod

//  1. Compute sandbox and container changes.
//  2. Kill pod sandbox if necessary.
//  3. Kill any containers that should not be running.
//  4. Create sandbox if necessary.
//  5. Create ephemeral containers.
//  6. Create init containers.
//  7. Create normal containers.

這部分的修改不會導致pod重建

 

 

二cgroupmanager

cgroupmanager負責 node 節點上運行的容器的 cgroup 配置信息,kubelet 啓動參數如果指定 --cgroups-per-qos 的時候,kubelet 會啓動 goroutine 來週期性的更新 pod 的 cgroup 信息,維護其正確性,該參數默認爲 true,實現了 pod 的Guaranteed/BestEffort/Burstable 三種級別的 Qos。

目前 kubernetes 僅支持 cpu、memory、pids 、hugetlb 四個 cgroup 子系統

runtime 有兩種 cgroup 驅動:一種是 systemd,另外一種是 cgroupfs

  • cgroupfs 比較好理解,比如說要限制內存是多少、要用 CPU share 爲多少,其實直接把 pid 寫入到對應cgroup task 文件中,然後把對應需要限制的資源也寫入相應的 memory cgroup 文件和 CPU 的 cgroup 文件就可以了;

  • 另外一個是 systemd 的 cgroup 驅動,這個驅動是因爲 systemd 本身可以提供一個 cgroup 管理方式。所以如果用 systemd 做 cgroup 驅動的話,所有的寫 cgroup 操作都必須通過 systemd 的接口來完成,不能手動更改 cgroup 的文件;


 

Kubelet cgroup level:

/sys/fs/cgroup/cpu/kubepods – kubepods cgroup
/sys/fs/cgroup/cpu/kubepods/burstable – Qos level cgroup
/sys/fs/cgroup/cpu/kubepods/burstable/pod<UID> – pod level cgroup
/sys/fs/cgroup/cpu/kubepods/burstable/pod<UID>/container<UID> – container level cgroup

1.cgroupmanager 會把本機的 allocatable 資源寫入到 kubepods 下對應的 cgroup 文件中,比如 kubepods/cpu.share

2.Qos level cgroup有兩個cgroupburstable besteffort,分別作爲 burstable級別podbesteffort級別pod的父cgroup;而Guaranteed Qos 對應的 pod 會直接在 kubepods 同級的 cgroup 中創建 pod cgroup,原因:guaranteed 級別的 pod 有明確的資源申請量(request)和資源限制量(limit),不需要qos級別的cgroup來管理和限制資源

3.pod level cgroup包含pod對應的container level cgroup

4.創建流程kubepods>qos>pod>container,設置流程container->pod->qos->kubepods,即設置好container level cgroup中各文件的限制,再層層往外更新其它level的限制

5.從級別和目錄可以看出:burstable 的 cgroup 需要爲比他等級高的 guaranteed 級別的 pod 的內存資源做預留,而 besteffort 需要爲 burstable 和 guaranteed 都要預留內存資源。


 

例:

/sys/fs/cgroup/cpu/kubepods/burstable/pod<UID>/container<UID>/cpu.shares=2

/sys/fs/cgroup/cpu/kubepods/burstable/pod<UID>/cpu.shares=204
/sys/fs/cgroup/cpu/kubepods/burstable/cpu.shares=1126
/sys/fs/cgroup/cpu/kubepods/cpu.shares=49512

 

對於每一個 pod 設定的 requests 和 limits,kubernetes 都會轉換爲 cgroup 中的計算方式,CPU 的轉換方式如下所示:

  • cpu.shares = (cpu in millicores * 1024) / 1000

  • cpu.cfs_period_us = 100000 (i.e. 100ms)

  • cpu.cfs_quota_us = quota = (cpu in millicores * 100000) / 1000

  • memory.limit_in_bytes

CPU 最終都會轉換爲以微秒爲單位,memory 會轉換爲以 bytes 爲單位

如果 pod 指定了 requests 和 limits,kubelet 會按以上的計算方式爲 pod 設置資源限制,如果沒有指定 limit 的話,那麼 cpu.cfs_quota_us 將會被設置爲 -1,即沒有限制。而如果 limit 和 request 都沒有指定的話,cpu.shares 將會被指定爲 2,這個是 cpu.shares 允許指定的最小數值了,可見針對這種 pod,kubernetes 只會給它分配最少的 cpu 資源。而對於內存來說,如果沒有 limit 的指定的話,memory.limit_in_bytes 將會被指定爲一個非常大的值,一般是 2^64 ,可見含義就是不對內存做出限制。

參考:https://www.jianshu.com/p/924e3c48cb9b

 

三QOSContainerManager

QoS(Quality of Service) 即服務質量,QoS 是一種控制機制,它提供了針對不同用戶或者不同數據流採用相應不同的優先級,或者是根據應用程序的要求,保證數據流的性能達到一定的水準。kubernetes 中有三種 Qos,分別爲:

  • 1、Guaranteed:pod 的 requests 與 limits 設定的值相等;

  • 2、Burstable:pod requests 小於 limits 的值且不爲 0;

  • 3、BestEffort:pod 的 requests 與 limits 均爲 0;

三者的優先級如下所示,依次遞增,OOM_SCORE_ADJ遞減

BestEffort -> Burstable -> Guaranteed

三種 Qos 在調度和底層表現上都不一樣:

  • 1、在調度時調度器只會根據 request 值進行調度;

  • 2、二是當系統 OOM上時對於處理不同 OOMScore 的進程表現不同,OOMScore 是針對 memory 的,當宿主上 memory 不足時系統會優先 kill 掉 OOMScore 值低的進程,可以使用 $ cat /proc/$PID/oom_score 查看進程的 OOMScore。OOMScore 的取值範圍爲 [-1000, 1000],Guaranteed pod 的默認值爲 -998,Burstable pod 的值爲 2~999,BestEffort pod 的值爲 1000,也就是說當系統 OOM 時,首先會 kill 掉 BestEffort pod 的進程,若系統依然處於 OOM 狀態,然後纔會 kill 掉 Burstable pod,最後是 Guaranteed pod;

  • 3、三是 cgroup 的配置不同,kubelet 爲會三種 Qos 分別創建對應的 QoS level cgroups,Guaranteed Pod Qos 的 cgroup level 會直接創建在 RootCgroup/kubepods 下,Burstable Pod Qos 的創建在 RootCgroup/kubepods/burstable 下,BestEffort Pod Qos 的創建在 RootCgroup/kubepods/BestEffort 下,上文已經說了 root cgroup 可以通過 $ mount | grep cgroup看到,在 cgroup 的每個子系統下都會創建 Qos level cgroups, 此外在對應的 QoS level cgroups 還會爲 pod 創建 Pod level cgroups;


 

qos的計算方式:

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的container使用memory越多,它的 OOMScore越小,越不容易被驅逐。

 

 

 

三 device manager

 

四topology manager

白話:爲container的resource選擇最優組合,如同組的cpuset,container所需的device來自同一個NUMA Node.

topology manager不是單獨起作用的,當前k8s 1.17.3中,它和CPU manager,device manager共同爲pod分配資源,優化資源訪問。

topology manager從Hint Providers中以bitmask的形式接受NUMA拓撲信息(Topology Hint),包括當前可用的NUMA節點和傾向的資源分配指示。Topology manager策略對提供的hint進行一系列操作以後將所有的hint決定匯聚起來,得出一個最佳的hint(bestHint)。通過配置的策略(policy),主機節點可以選擇接受或者拒絕pod。

topology manager當前所支持的策略有四個:None,BestEffort,Restricted和SingleNumaNode。

 

None是默認的策略,這個策略不會做任何的NUMA感知的資源對齊。

 

BestEffort對於pod裏面的每一個container都會調用它們的hint provider來發現它們的資源可用性。topology manager計算存儲這些container的NUMA親和度的信息,如果親和度不能被滿足的話,manager也會存儲親和度信息同時也會允許pod加入這個主機節點。

 

Restricted同樣也會對pod裏面的每一個container計算NUMA親和度。不同的是,如果親和度不能被滿足的話,主機節點會拒絕這個pod的加入。這樣會導致pod處於Terminated的狀態。如果pod被接受了,那麼親和度信息將會被用於container的資源分配。

 

SingleNumaNode策略用來判斷單個NUMA節點的親和度是否滿足,如果滿足的話hint provider即可利用親和度信息進行資源分配;如果不滿足的話主機節點會拒絕pod的加入,這樣也會導致pod處於Terminated的狀態。

 

參考:https://blog.csdn.net/qjm1993/article/details/103237944

 

 

 

五cpu manager

白話:根據NUMA爲container選擇最優的cpuset,讓container從cpu方向得到更高的性能

目前CPU Manager支持兩種Policy,分別爲none和static,通過kubelet --cpu-manager-policy設置,未來會增加dynamic policy做Container生命週期內的cpuset動態調整。

    • none: 爲cpu manager的默認值,相當於沒有啓用cpuset的能力。cpu request對應到cpu share,cpu limit對應到cpu quota。

    • static: 目前,請設置--cpu-manager-policy=static來啓用,kubelet將在Container啓動前分配綁定的cpu set,分配時還會考慮cpu topology來提升cpu affinity,後面會提到。

  • 確保kubelet爲--kube-reserved--system-reserved都配置了值,可以不是整數個cpu,最終會計算reserved cpus時會向上取整。這樣做的目的是爲了防止CPU Manager把Node上所有的cpu cores分配出去了,導致kubelet及系統進程都沒有可用的cpu了。

注意CPU Manager還有一個配置項--cpu-manager-reconcile-period,用來配置CPU Manager Reconcile Kubelet內存中CPU分配情況到cpuset cgroups的修復週期。如果沒有配置該項,那麼將使用--node-status-update-frequencydefault 10s配置的值。

使用CPU Manager的Pod、Container具備以下兩個條件:

  • Pod QoS爲Guaranteed;

  • Pod中該Container的Cpu request必須爲整數CPUs;

工作流程:

  • Kuberuntime調用容器運行時去create該Container。

  • 創建完後,調用PreStartContainer將該Container交給CPU Manager處理。

  • 創建業務container時,先把init container佔用的cpu釋放

  • CPU Manager爲Container按照static policy邏輯進行處理。

  • CPU Manager從當前Shared Pool中挑選“最佳”Set拓撲結構的CPU,對於不滿足Static Policy的Contianer,則返回Shared Pool中所有CPUS組成的Set。

  • 挑選方式:根據Node上的NUMA CPU Topology劃分cpu組,當container需要多個cpu時,讓這幾個cpu儘量來自同一個組(簡單理解爲一個插槽),這樣同組訪問能加快速度提高性能。

  • CPU Manager將對該Container的CPUs分配情況記錄到Checkpoint State中,並且從Shared Pool中刪除剛分配的CPUs。

  • CPU Manager再從state中讀取該Container的CPU分配信息,然後通過UpdateContainerResources cRI接口將其更新到Cpuset Cgroups中,包括對於非Static Policy Container。

  • Kuberuntime調用容器運行時Start該容器。

Checkpoint文件內容就是CPUManagerCheckpoint結構體的json格式,其中Entries的key是ContainerID,value爲該Container對應的Assigned CPU Set信息。

當這些通過CPU Manager分配CPUs的Container要Delete時,CPU Manager工作流大致如下:

  1. Kuberuntime會調用CPU Manager去按照static policy中定義邏輯處理。

  2. CPU Manager將該Container分配的Cpu Set重新歸還到Shared Pool中。

  3. Kuberuntime調用容器運行時Remove該容器。

  4. CPU Manager會異步地進行Reconcile Loop,爲使用Shared Pool中的Cpus的Containers更新CPU集合。

 

CPU Manager Reconcile按照--cpu-manager-reconcile-period配置的週期進行Loop,Reconcile注意進行如下處理:

  • 遍歷所有activePods中的所有Containers,注意包括InitContainers,對每個Container繼續進行下面處理。

  • 檢查該ContainerID是否在CPU Manager維護的Memory State assignments中,

    • 如果不在Memory State assignments中:

      • 再檢查對應的Pod.Status.Phase是否爲Running且DeletionTimestamp爲nil,如果是,則調用CPU Manager的AddContainer對該Container/Pod進行QoS和cpu request檢查,如果滿足static policy的條件,則調用takeByTopology爲該Container分配“最佳”CPU Set,並寫入到Memory State和Checkpoint文件(cpu_manager_sate)中,並繼續後面流程。

      • 如果對應的Pod.Status.Phase是否爲Running且DeletionTimestamp爲nil爲false,則跳過該Container,該Container處理結束。不滿足static policy的Containers因爲不在Memory State assignments中,所以對它們的處理流程也到此結束。

    • 如果ContainerID在CPU Manager assignments維護的Memory State中,繼續後面流程。

  • 然後從Memory State中獲取該ContainerID對應的CPU Set。

  • 最後調用CRI UpdateContainerCPUSet更新到cpuset cgroups中。

 

cpumanager調用State進行validate處理:

  • 當Memory State中Shared(Default) CPU Set爲空時,CPU Assginments也必須爲空,然後對Memory State中的Shared Pool進行初始化,並寫入到Checkpoint文件中(初始化Checkpoint)。

  • 只要我們沒有手動去刪Checkpoint文件,那麼在前面提到的state.NewCheckpointState中會根據Checkpoint文件restore到Memory State中,因此之前Assgned CPU Set、Default CPU Set都還在。

  • 當檢測到Memory State已經成功初始化(根據Checkpoint restore),則檢查這次啓動時reserved cpu set是否都在Default CPU Set中,如果不是(比如kube/system reserved cpus增加了),則報錯返回,因爲這意味着reserved cpu set中有些cpus被Assigned到了某些Container中了,這可能會導致這些容器啓動失敗,此時需要用戶自己手動的去修正Checkpoint文件。

  • 檢測reserved cpu set通過後,再檢測Default CPU Set和Assigned CPU Set是否有交集,如果有交集,說明Checkpoint文件restore到Memory State的數據有錯,報錯返回。

  • 最後檢查這次啓動時從cAdvisor中獲取到的CPU Topology中的所有CPUs是否與Memory State(從Checkpoint中restore)中記錄的所有CPUs(Default CPU Set + Assigned CPU Set)相同,如果不同,則報錯返回。可能因爲上次CPU Manager停止到這次啓動這個時間內,Node上的可用CPUs發生變化。

參考:https://blog.csdn.net/weixin_34050389/article/details/92574505

 

 

 

 

 

 

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