<Kubelet從入門到放棄>系列將對Kubelet組件由基礎知識到源碼進行深刻梳理。在這篇文章中zouyee會介紹CPU的相關概念以及Kubelet組件CPU Manager的源碼。關於《Kubernetes調度框架系列》剩餘的配置及源碼部分,將陸續放出。html
1、背景介紹node
1.1 需求說明nginx
默認狀況下,kubelet 基於CFS 調度算法來執行 Pod 的 CPU 分配。 可是當節點上運行了 CPU 密集型 Pod 時,應用可能會因搶佔等狀況致使 CPU 切換,而上述的切換致使的延時與中斷對於業務敏感性Pod是沒法接受的。
爲了解決上述問題。 Kubelet 提供了可選的 CPU 管理策略,以知足不一樣的業務場景。
複製代碼
1.2 CPU架構git
a. SMTgithub
同時多線程Simultaneous multithreading,簡稱SMT,SMT可經過複製處理器上的結構狀態,讓同一個處理器上的多個線程同步執行並共享處理器的執行資源,可最大限度地實現寬發射、亂序的超標量處理,提升處理器運算部件的利用率,緩和因爲數據相關或Cache未命中帶來的訪問內存延時。當沒有多個線程可用時,SMT處理器幾乎和傳統的寬發射超標量處理器同樣。多線程技術則能夠爲高速的運算核心準備更多的待處理數據,減小運算核心的閒置時間。 Intel的hyper-threading其實就是 two-thread SMT.算法
b. CMPjson
片上多處理器(Chip multiprocessors,簡稱CMP,其思想是將大規模並行處理器中的SMP(對稱多處理器)集成到同一芯片內,各個處理器並行執行不一樣的進程。因爲CMP結構已經被劃分紅多個處理器核來設計,每一個核都比較簡單,有利於優化設計。多核處理器能夠在處理器內部共享緩存,提升緩存利用率,同時簡化多處理器系統設計的複雜度。api
c. SMP緩存
對稱多處理器(Symmetric Multi-Processors,簡稱SMP),其是指在一個計算機上聚集了一組處理器(多CPU),各CPU之間共享內存子系統以及總線結構。共享存儲型多處理機有三種模型:均勻存儲器存取(Uniform-Memory-Access,簡稱UMA)模型、非均勻存儲器存取(Non-uniform Memory Access,簡稱NUMA)模型和只用高速緩存的存儲器結構(Cache-Only Memory Architecture,簡稱COMA)模型,這些模型的區別在於存儲器和外圍資源如何共享或分佈。服務器
1.3 相關技術
在CPU管理中,涉及NUMA、HT及cpuset技術,如下爲簡要介紹。
NUMA
NUMA,之內存訪問的不一致性爲代價,減輕對總線和memory的帶寬需求。這種結構對進程調度算法的要求較高,儘可能減小跨Node的內存訪問次數,以提高系統性能。Core之間會共享總線、內存等資源。若是Core的數量較少,則沒什麼問題,但隨着Core的增多,對總線以及內存帶寬的需求就會顯著增大,最終總線和內存會成爲系統性能的瓶頸。
以下圖所示,一個NUMA Node包括一個或者多個Socket,以及與之相連的local memory。一個多核的Socket有多個Core。若是CPU支持HT,OS還會把這個Core當作 2個Logical Processor。
HT
Hyperthreading 使操做系統認爲處理器的核心數是實際核心數的2倍,超線程(hyper-threading)本質上就是CPU支持的同時多線程(simultaneous multi-threading)技術,簡單理解就是對CPU的虛擬化,一顆物理CPU能夠被操做系統當作多顆CPU來使用。Hyper-threading只是一種「欺騙」手段。
cpuset
cpuset做爲cgroup的子系統,主要用於numa架構,用於設置cpu的親和性,爲 cgroup 中的 task 分配獨立的 CPU和內存等。
cpuset使用sysfs提供用戶態接口,能夠經過普通文件讀寫,工做流程爲:cpuset調用sched_setaffinity來設置進程的cpu、內存的親和性,調用mbind和set_mempolicy來設置內存的親和性。
1.4 數聽說明
a. Numa Node
numactl是設定進程NUMA策略的命令行工具,也能夠用來查看當前的Nuwa node:
[root@paasn2 ~]# numactl -H
available: 1 nodes (0)
node 0 cpus: 0 1 2 3 4 5 6 7
node 0 size: 16047 MB
node 0 free: 3693 MB
node distances:
node 0
0: 10
複製代碼
從上面能夠看出本機有一個Numa node(操做系統配置numa=on效果同樣...),若是要進一步知道一個Node包含哪幾個CPU,該怎麼辦?
一種方法是經過查看ls /sys/devices/system/node/目錄下的信息,例如:
[root@paasn2 ~]# ls /sys/devices/system/node/
has_cpu has_normal_memory node0 online possible power uevent
[root@paasn2 ~]# ls /sys/devices/system/node/node0
compact cpu0 cpu1 cpu2 cpu3 cpu4 cpu5
cpu6 cpu7 cpulist cpumap distance hugepages
...
複製代碼
可見, node0包含0/1/2/3/4/5/6/7八個Processor。
b、查看Socket
一個Socket對應主板上的一個插槽,在本文中是指一個CPU封裝。在/proc/cpuinfo中的physical id就是Socket的ID,能夠從中找到本機到底有多少個Socket,而且每一個Socket有那幾個Processor。
查看Socket數量
grep 'physical id' /proc/cpuinfo | awk -F: '{print2 | "sort -un"}' 0 1 grep 'physical id' /proc/cpuinfo | awk -F: '{print2 | "sort -un"}' | wc -l 2
2)查看每一個Socket有幾個Processor
$ grep 'physical id' /proc/cpuinfo | awk -F: '{print $2}' | sort | uniq -c
4 0
4 1
複製代碼
查看Socket對應哪幾個Processor
awk -F: '{ if (1 ~ /processor/) { gsub(/ /,"", 2; } else if (1 ~ /physical id/){ gsub(/ /,"",2); s_id=$2; arr[s_id]=arr[s_id] " " p_id } }
END{ for (i in arr) print arr[i]; }' /proc/cpuinfo | cut -c2- 0 1 2 3 4 5 6 7
4)Core
/proc/cpuinfo文件中的cpu cores代表一個socket中有幾個cores,例如:
cat /proc/cpuinfo | grep 'core' | sort -u
core id : 0
core id : 1
core id : 2
core id : 3
cpu cores : 4
複製代碼
查看Processors的個數就比較簡單了,從上面的統計結果中咱們已經能夠知道有8個Logical processor,不過也能夠直接從/proc/cpuinfo文件中獲取:
$ grep 'processor' /proc/cpuinfo | wc -l
8
複製代碼
其實,每一個socket中能有幾個processor也能夠從siblings字段中獲取:
$ grep 'siblings' /proc/cpuinfo | sort -u
siblings : 4
複製代碼
1.5 結構體
須要注意的是,Kubelet內部啓動cadvisor Manager,封裝cadvisor接口爲cadvisor.Interface,其對外暴露MachineInfo() (*cadvisorapi.MachineInfo, error)方法,CPU manager經過cadvisor的MachineInfo結構體信息生產CPU 拓撲信息, 具體實現爲調用GetNodesInfo
三個關鍵的結構體定義以下:
type MachineInfo struct {
...
// Machine Topology
// Describes cpu/memory layout and hierarchy.
Topology []Node `json:"topology"`
...
}
type Node struct {
Id int `json:"node_id"`
// Per-node memory
Memory uint64 `json:"memory"`
HugePages []HugePagesInfo `json:"hugepages"`
Cores []Core `json:"cores"`
Caches []Cache `json:"caches"`
}
type Core struct {
Id int `json:"core_id"`
Threads []int `json:"thread_ids"`
Caches []Cache `json:"caches"`
SocketID int `json:"socket_id"`
}
複製代碼
說明以下:
2、功能介紹
注:其中涉及到的拓撲管理、設備管理等內容,後續有針對性文章進行介紹,此處帶過。
CPU 管理器(CPU Manager)做爲 alpha 特性引入 Kubernetes 1.8 版本,Kubernetes 1.12進入beta版本後,默認開啓。CPU 管理策略經過 kubelet 參數 --cpu-manager-policy 來指定。支持兩種策略:
CPU 管理器(即:經過goroutine方式執行reconcileState方法)按期經過 CRI(即containerRuntime) 寫入資源更新,以保證內存中 CPU 分配與 cgroupfs 一致(可參考第三節)。 同步頻率經過新增的 Kubelet 配置參數 --cpu-manager-reconcile-period 來設置。 若是不指定,默認與 --node-status-update-frequency 的週期相同。關於CPU管理器須要注意如下幾點:
當前支持如下兩種策略:
a. none
none 策略顯式地啓用現有的默認 CPU 親和方案。 經過 CFS 配額來實現 Guaranteed pods的 CPU 使用限制。
b. static
static 策略針對具備整數型 CPU請求 的 Guaranteed Pod (後續文章介紹),它容許該類 Pod 中的容器獨佔 CPU 資源。其基於 cpuset cgroup 控制器 實現的
從1.17版本開始,CPU保留列表能夠經過 kublet 的 --reserved-cpus參數設置, 而且--reserved-cpus 指定的CPU列表優先級高於--kube-reserved 和 --system-reserved 參數指定的保留CPU,若同時指定,將進行覆蓋。
- `static`策略管理一個CPU共享資源池,起初,該資源池包含節點上全部的 CPU 資源。可用且獨佔的CPU 數量等於節點的 CPU總量減去經過 `reserved-cpus`或`--kube-reserved` 或 `--system-reserved` 命令行保留的 CPU(其實還有eviction資源,但當前不支持CPU類型,所以省略)。
- 經過這些參數預留的 CPU 是以整數方式,按物理內核 ID 升序從初始共享池獲取的。 共享池是 `BestEffort` 和 `Burstable` pod 運行的CPU 集合。`Guaranteed` Pod 中的容器,若是聲明瞭非整數值的 CPU `requests` ,也將運行在共享池的 CPU 上。只有 指定了正整數型的 CPU `requests` 的`Guaranteed` Pod ,才能獨佔 CPU 資源
- 當 `Guaranteed` Pod 調度到節點上時,若是其容器符合獨佔要求, 相應的CPU會從共享池中移除,並放置到容器的cpuset 中。 容器cgroup目錄的 cpuset文件 中的 CPU 數量與 Pod 中指定的 CPU `limit` 相等。 這種分配加強了CPU親和性,減小了CPU上下文切換。
複製代碼
由於其 requests 值與 limits相等,下述Pod屬於Guaranteed QoS類型, 且容器對 CPU 資源的限制值是正整數值。符合獨佔要求,所以該 nginx 容器獨佔2個CPU。
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "2"
requests:
memory: "200Mi"
cpu: "2"
複製代碼
後續相關內容,請查看公衆號:DCOS
4、參考資料
一、cpuset
二、cpu topology
三、cpu manager policy