Kubelet從入門到放棄:識透CPU管理

<Kubelet從入門到放棄>系列將對Kubelet組件由基礎知識到源碼進行深刻梳理。在這篇文章中zouyee會介紹CPU的相關概念以及Kubelet組件CPU Manager的源碼。關於《Kubernetes調度框架系列》剩餘的配置及源碼部分,將陸續放出。html

1、背景介紹node

1.1 需求說明nginx

默認狀況下,kubelet 基於CFS 調度算法來執行 Pod 的 CPU 分配。 可是當節點上運行了 CPU 密集型 Pod 時,應用可能會因搶佔等狀況致使 CPU 切換,而上述的切換致使的延時與中斷對於業務敏感性Pod是沒法接受的。

爲了解決上述問題。 Kubelet 提供了可選的 CPU 管理策略,以知足不一樣的業務場景。
複製代碼
  • 以API形式向用戶提供配置方案
  • 同時支持多種CPU使用場景共存
  • 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)模型,這些模型的區別在於存儲器和外圍資源如何共享或分佈。服務器

  • S2MP全稱爲可擴展共享存儲多處理(Scalable Shared-Memory Multiprocessing)技術。S2MP系統將大量高性能微處理器鏈接起來,共享一個統一的地址空間,較好地解決其餘並行處理系統沒法解決的問題。
  • MMP也被稱爲海量並行處理架構。MPP提供了另一種進行系統擴展的方式,它由多個SMP服務器經過必定的節點互聯網絡進行鏈接,協同工做,完成相同的任務,從用戶的角度來看是一個服務器系統。

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。

  • Socket是一個物理上的概念,指的是主板上的cpu插槽
  • Node是一個邏輯上的概念,上圖中沒有說起。因爲SMP體系中各個CPU訪問內存只能經過單一的通道,致使內存訪問成爲瓶頸,cpu再多也無用。後來引入了NUMA,經過劃分node,每一個node有本地RAM,這樣node內訪問RAM速度會很是快。但跨Node的RAM訪問代價會相對高一點,咱們用Node之間的距離(Distance,抽象的概念)來定義各個Node之間互訪資源的開銷。
  • Core就是一個物理cpu,一個獨立的硬件執行單元,好比寄存器,計算單元等
  • Thread就是超線程(HyperThreading)的概念,是一個邏輯cpu,共享core上的執行單元

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。

  1. 查看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
複製代碼
  1. 查看Socket對應哪幾個Processor

    awk -F: '{ if (1 ~ /processor/) { gsub(/ /,"", 2 ) ; p i d = 2); p_id= 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
複製代碼
  1. Logical Processor

查看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

github.com/google/cadv…

三個關鍵的結構體定義以下:

github.com/google/cadv…

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"`
}
複製代碼

說明以下:

  • Node對應NUMA節點,其中ID由/sys/devices/system/node/node i 中的 i中的 i得到
  • Core對應 Core,其中ID由/sys/devices/system/node/node i / c p u i/cpu j/topology/core_id得到
  • Threads對應Logical Processor,其中ID由/sys/devices/system/node/node i / c p u i/cpu j中的$j得到
  • SocketID對應Socket,其中ID由/sys/devices/system/node/node i / c p u i/cpu j/topology/physical_package_id得到

2、功能介紹

注:其中涉及到的拓撲管理、設備管理等內容,後續有針對性文章進行介紹,此處帶過。

CPU 管理器(CPU Manager)做爲 alpha 特性引入 Kubernetes 1.8 版本,Kubernetes 1.12進入beta版本後,默認開啓。CPU 管理策略經過 kubelet 參數 --cpu-manager-policy 來指定。支持兩種策略:

  • none: 默認策略,保持現有的調度行爲。
  • static: 節點上知足某些資源特徵的 Pod 根據 CPU 親和性和獨佔性進行分配。

CPU 管理器(即:經過goroutine方式執行reconcileState方法)按期經過 CRI(即containerRuntime) 寫入資源更新,以保證內存中 CPU 分配與 cgroupfs 一致(可參考第三節)。 同步頻率經過新增的 Kubelet 配置參數 --cpu-manager-reconcile-period 來設置。 若是不指定,默認與 --node-status-update-frequency 的週期相同。關於CPU管理器須要注意如下幾點:

  • 像容器運行時和 kubelet 此類的系統服務能夠繼續在這些獨佔 CPU 上運行。獨佔性僅針對通常 Pod。
  • CPU 管理器不支持offline/online CPUs熱更新。此外,若是節點上的 CPUs 發生變化, 則必須驅逐 Pod,並經過刪除 kubelet 配置的 cpu_manager_state 文件以重置 CPU 管理器。
  • 當啓用 static 策略時, kube-reserved 加上 system-reserved 或 reserved-cpus 設置的 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

相關文章
相關標籤/搜索