更多kubernetes文章:weiliang-ms/kubernetes-docs: kubernetes學(xué)習(xí)筆記 (github.com)
Memory Manager介紹說明
Memory Manager(譯為內(nèi)存管理器)是 kubelet 內(nèi)部的一個組件乙帮,旨在為 Guaranteed QoS 類型 pod 提供保證內(nèi)存(和大頁內(nèi)存)分配功能,該特性提供了幾種分配策略:
- 單 NUMA 策略:用于高性能和性能敏感的應(yīng)用程序
- 多 NUMA 策略:補充完善單 NUMA 策略無法管理的情況
也就是說鹰祸,只要 pod 所需的內(nèi)存量超過單個 NUMA 節(jié)點的容量,就會使用多 NUMA 策略跨多個 NUMA 節(jié)點提供保證的內(nèi)存。
在這兩種場景中,內(nèi)存管理器都使用提示生成協(xié)議為 pod 生成最合適的 NUMA 關(guān)聯(lián)沃疮,并將這些關(guān)聯(lián)提示提供給中央管理器(Topology Manager)。此外梅肤,內(nèi)存管理器確保 pod 請求的內(nèi)存從最小數(shù)量的 NUMA 節(jié)點分配司蔬。
從技術(shù)上講,單 NUMA 策略是多 NUMA 策略的一種特殊情況姨蝴,因此 kubernetes 開發(fā)團隊沒有為它們開發(fā)單獨的實現(xiàn)俊啼。
什么是NUMA?
早期的計算機左医,內(nèi)存控制器還沒有整合進 CPU授帕,所有的內(nèi)存訪問都需要經(jīng)過北橋芯片來完成同木。如下圖所示,CPU 通過前端總線(FSB跛十,F(xiàn)ront Side Bus)連接到北橋芯片彤路,然后北橋芯片連接到內(nèi)存——內(nèi)存控制器集成在北橋芯片里面。
上面這種架構(gòu)被稱為 UMA(Uniform Memory Access, 一致性內(nèi)存訪問 ):總線模型保證了 CPU 的所有內(nèi)存訪問都是一致的芥映,不必考慮不同內(nèi)存地址之間的差異洲尊。
在 UMA 架構(gòu)下,CPU 和內(nèi)存之間的通信全部都要通過前端總線奈偏。而提高性能的方式坞嘀,就是不斷地提高 CPU、前端總線和內(nèi)存的工作頻率惊来。
由于物理條件的限制丽涩,不斷提高工作頻率的方式接近瓶頸。CPU 性能的提升開始從提高主頻轉(zhuǎn)向增加 CPU 數(shù)量(多核唁盏、多 CPU)内狸。越來越多的 CPU 對前端總線的爭用,使前端總線成為了瓶頸厘擂。為了消除 UMA 架構(gòu)的瓶頸昆淡,NUMA(Non-Uniform Memory Access, 非一致性內(nèi)存訪問)架構(gòu)誕生了:
- CPU 廠商把內(nèi)存控制器集成到 CPU 內(nèi)部,一般一個 CPU socket 會有一個獨立的內(nèi)存控制器刽严。
- 每個 CPU scoket 獨立連接到一部分內(nèi)存昂灵,這部分 CPU 直連的內(nèi)存稱為“本地內(nèi)存”。
- CPU 之間通過 QPI(Quick Path Interconnect) 總線進行連接舞萄。CPU 可以通過 QPI 總線訪問不和自己直連的“遠(yuǎn)程內(nèi)存”眨补。
和 UMA 架構(gòu)不同,在 NUMA 架構(gòu)下倒脓,內(nèi)存的訪問出現(xiàn)了本地和遠(yuǎn)程的區(qū)別:訪問遠(yuǎn)程內(nèi)存的延時會明顯高于訪問本地內(nèi)存撑螺。
什么是大頁內(nèi)存?
大頁內(nèi)存(HugePages)崎弃,有時也叫“大內(nèi)存頁”甘晤、“內(nèi)存大頁”、“標(biāo)準(zhǔn)大頁”饲做。計算機內(nèi)存以頁的形式分配給進程线婚。通常這些頁相當(dāng)小,這意味著消耗大量內(nèi)存的進程也將消耗大量的頁盆均。對于那些內(nèi)存操作非常頻繁的業(yè)務(wù)來說塞弊,大頁內(nèi)存可以有效的提高性能。簡而言之,通過啟用大頁內(nèi)存游沿,系統(tǒng)只需要處理較少的頁面映射表饰抒,從而減少訪問/維護它們的開銷!大頁內(nèi)存在數(shù)據(jù)庫服務(wù)器這樣的系統(tǒng)上特別有用奏候。像 MySQL 和 PostgreSQL 這樣的進程可以使用大頁內(nèi)存循集,以減少對 RAM 緩存的壓力。
什么是Guaranteed QoS pod蔗草?
Kubernetes 通過 服務(wù)質(zhì)量類(Quality of Service class咒彤,QoS class)定義了三種 pod 類型, Kubernetes 在 Node 資源不足時使用 QoS 類來就驅(qū)逐 Pod 作出決定:
- Guaranteed: pod 中 每個容器 limits.cpu == requests.cpu && limits.memory== requests.memory. Guaranteed 類型 Pod 具有最嚴(yán)格的資源限制咒精,并且最不可能面臨驅(qū)逐镶柱。
- Burstable: Pod 中至少一個容器有內(nèi)存或 CPU 的 request 或 limit 且非 Guaranteed。
- BestEffort: Pod 不滿足 Guaranteed 或 Burstable 的判據(jù)條件模叙,即Pod 中的所有容器沒有設(shè)置內(nèi)存 limit 或內(nèi)存 request歇拆,也沒有設(shè)置 CPU limit 或 CPU request 。
三種 pod 優(yōu)先級:Guaranteed > Burstable > BestEffort
Memory Manager開發(fā)動機
- 為容器(同一 pod 內(nèi)的容器)提供最小數(shù)量的 NUMA 節(jié)點上有保證的內(nèi)存(和大頁內(nèi)存)分配范咨。
- 保證整個容器組(同一 pod 內(nèi)的容器)的內(nèi)存和大頁面與相同 NUMA 節(jié)點的關(guān)聯(lián)故觅。
內(nèi)存管理器的設(shè)計應(yīng)用對于性能敏感的程序、數(shù)據(jù)庫渠啊、虛擬化影響巨大:
由于數(shù)據(jù)庫(例如输吏,Oracle, PostgreSQL和MySQL)需要相對大量的內(nèi)存和大頁內(nèi)存,來相對有效地訪問大量數(shù)據(jù)替蛉。而為了減少由跨 NUMA 內(nèi)存訪問和共享引起的延遲贯溅,所有資源(CPU內(nèi)核、內(nèi)存躲查、大頁內(nèi)存和I/O設(shè)備)都應(yīng)該對齊到同一個 NUMA 節(jié)點它浅,這將極大地提高穩(wěn)定性和性能。
并且內(nèi)存數(shù)據(jù)庫(Redis等)具有更大的內(nèi)存需求镣煮,可以擴展到多個 NUMA 節(jié)點姐霍。這就產(chǎn)生了跨多個 NUMA 節(jié)點維持和管理內(nèi)存的需求。
Memory Manager架構(gòu)設(shè)計
一旦 kubelet 請求 Guaranteed QoS 類型 pod 許可典唇,如上圖所示邮弹,拓?fù)涔芾砥鳎═opology Manager)就會向內(nèi)存管理器 (Memory Manager) 查詢 pod 中所有容器的內(nèi)存和大頁內(nèi)存的首選 NUMA 親和關(guān)系。
對于 pod 中的每個容器蚓聘,內(nèi)存管理器使用其內(nèi)部數(shù)據(jù)庫(即Node Map)計算關(guān)聯(lián)。Node Map 是一個對象盟劫,它負(fù)責(zé)跟蹤 Guaranteed QoS 類 Pod 中所有容器的內(nèi)存(和大頁內(nèi)存)的使用情況夜牡。一旦內(nèi)存管理器完成計算,它將結(jié)果返回給拓?fù)涔芾砥鳎员阃負(fù)涔芾砥骺梢杂嬎愠瞿膫€ NUMA 節(jié)點或一組 NUMA 節(jié)點最適合容器的內(nèi)存固定塘装。對 pod 中的所有容器執(zhí)行總體計算急迂,如果沒有容器被拒絕,則 kubelet 最終接收并部署該 pod蹦肴。
在 pod 接收階段僚碎,內(nèi)存管理器調(diào)用 Allocate() 方法并更新其 Node Map 對象。隨后內(nèi)存管理器調(diào)用 AddContainer() 方法并強制分配容器的內(nèi)存和大頁內(nèi)存阴幌,并限制到對應(yīng) NUMA 節(jié)點或 NUMA 節(jié)點組勺阐。最終通過CRI 接口更新控制組配置項(cpuset.mems項)。
內(nèi)存管理器為(且僅為) Guaranteed QoS類中的pod提供有保證的內(nèi)存分配矛双。
多NUMA節(jié)點保證內(nèi)存分配原理
主要思想是將一組 NUMA 節(jié)點視為一個獨立的單元渊抽,并由內(nèi)存管理器管理這些獨立的單元。
NUMA 節(jié)點組不能相交议忽。下面的圖舉例說明了一組不相交的 NUMA 組懒闷。圖中的組是不相交的,即:[0]栈幸,[1,2]愤估,[3]。必須遵守該規(guī)則速址,因為重疊的組基本上不能確保在多個 NUMA 節(jié)點上有保證的內(nèi)存分配玩焰。
例如,以下組重疊壳繁,[0,1]震捣,[1,2]和[3],因為它們有一個以1為索引的公共 NUMA 節(jié)點闹炉。換句話說蒿赢,如果組重疊(比如:[0,1]和[1,2]),則[1,2]組的內(nèi)存資源可能會優(yōu)先被另一組([0,1])消耗渣触,[1,2]組可用內(nèi)存資源會被搶占羡棵。
節(jié)點映射(Node Map)
內(nèi)存管理器有一個內(nèi)部數(shù)據(jù)庫,即節(jié)點映射(Node Map)嗅钻,它包含內(nèi)存映射(Memory Map)皂冰。該數(shù)據(jù)庫用于記錄在 Guaranteed QoS 類中為已部署的容器保留的內(nèi)存。節(jié)點映射對象記錄 Node 對象中不相交的 NUMA-node組的動態(tài)配置养篓。注意秃流,實際上內(nèi)存映射提供了跟蹤內(nèi)存的計數(shù)器。因此柳弄,不應(yīng)該將映射理解為允許保留內(nèi)存范圍或連續(xù)內(nèi)存塊的映射舶胀。
在部署容器時,還使用映射來計算NUMA關(guān)聯(lián)。內(nèi)存管理器支持傳統(tǒng)內(nèi)存和各種可能大小的大頁內(nèi)存(例如2 MiB或1 GiB)嚣伐,節(jié)點向內(nèi)存管理器提供三種類型的內(nèi)存糖赔,即:常規(guī)內(nèi)存、hugepages-1Gi和hugepages-2Mi轩端。
在啟動時放典,內(nèi)存管理器為每個 NUMA 節(jié)點和各自的內(nèi)存類型初始化一個 Memory Table 集合,從而生成準(zhǔn)備使用的內(nèi)存映射對象基茵。
// MemoryTable 包含內(nèi)存信息
type MemoryTable struct {
TotalMemSize uint64 `json:"total"`
SystemReserved uint64 `json:"systemReserved"`
Allocatable uint64 `json:"allocatable"`
Reserved uint64 `json:"reserved"` Free uint64 `json:"free"`
}
// NodeState 包含 NUMA 節(jié)點關(guān)聯(lián)信息
type NodeState struct {
// NumberOfAssignments contains a number memory assignments from this node
// When the container requires memory and hugepages it will increase number of assignments by two
NumberOfAssignments int `json:"numberOfAssignments"`
// MemoryTable 包含 NUMA 節(jié)點內(nèi)存關(guān)聯(lián)信息
MemoryMap map[v1.ResourceName] *MemoryTable `json:"memoryMap"`
// NodeGroups contains NUMA nodes that current NUMA node in group with them
// It means that we have container that pinned to the current NUMA node and all group nodes
Nodes []int `json:"nodes"`
}
// NodeMap 包含 每個 NUMA 節(jié)點的內(nèi)存信息.
type NodeMap map[int] *NodeState
內(nèi)存映射(Memory Map)
內(nèi)存映射用于跟蹤每個 NUMA 節(jié)點的內(nèi)存使用情況奋构。內(nèi)存映射包含幾個計數(shù)器,用于跟蹤內(nèi)存使用情況耿导。存在以下等式:
Allocatable = TotalMemSize - SystemReserved
Free + Reserved = Allocatable
Free = Allocatable - Reserved
Reserved = Allocatable - Free
- TotalMemSize 的值由 cadvisor 為每種內(nèi)存類型(常規(guī)內(nèi)存声怔、hugepages-1Gi等)提供給內(nèi)存管理器。TotalMemSize 的值是恒定的舱呻,表示 NUMA 節(jié)點上可用的特定類型內(nèi)存的總(最大)容量醋火。
- SystemReserved 由 systemReserved 配置項設(shè)置,表示預(yù)留給系統(tǒng)服務(wù)的資源大邢渎馈(如kubelet芥驳、其他系統(tǒng)服務(wù))
- Reserved 表示 Guaranteed QoS 類型 pod 中為容器預(yù)留的保證內(nèi)存總量
啟動階段內(nèi)存映射
下圖展示了節(jié)點啟動后不久的內(nèi)存映射(常規(guī)內(nèi)存):
SystemReserved 的值是由 kubelet 啟動參數(shù)預(yù)先配置。SystemReserved 在運行時保持不變茬高,因此Allocatable在運行時也是不變的兆旬。
SystemReserved 表示系統(tǒng)預(yù)留的內(nèi)存大小,用于系統(tǒng)怎栽,即內(nèi)核丽猬、操作系統(tǒng)守護進程和核心節(jié)點組件,如 kubelet (kubelet守護進程)熏瞄。
運行階段內(nèi)存映射
下圖中脚祟,容器A和容器B實際消耗的內(nèi)存(紅色色塊、紫色色塊)少于內(nèi)存管理器保留的(保證內(nèi)存)內(nèi)存大星恳(紅色虛線框由桌、紫色虛線框),所以對于兩個容器來說邮丰,它們的內(nèi)存消耗都低于它們的內(nèi)存限制(limits.memroy)行您,并且容器正常運行。
當(dāng) kubernetes 節(jié)點發(fā)生 OOM 時(cgroups內(nèi)存限制剪廉、hard-eviction-treshold 等)娃循,由 kubelet、系統(tǒng)內(nèi)核(linux oom killer)進行處理斗蒋,而非由內(nèi)存管理器處理淮野。
工作方式
下面展示內(nèi)存管理器如何管理不同 QoS 類(Guaranteed, bestefort /Burstable)中的 pod捧书,以及內(nèi)存管理器如何動態(tài)管理(創(chuàng)建或刪除) NUMA 節(jié)點的非相交組。
多NUMA節(jié)點
- 創(chuàng)建 pod1 骤星,其中 pod1 內(nèi)存配額為15G,且 requests.memory == limits.memory (即Guaranteed QoS 類型pod)
內(nèi)存管理器隨后創(chuàng)建 名為 group1 的組爆哑,group1 包含 NUMA 節(jié)點與 pod1洞难,同時更新 pod1 控制組cpuset.mems 參數(shù)[[0,1], 15G]
- 創(chuàng)建 pod2 ,其中 pod2 內(nèi)存配額為5G揭朝,且 requests.memory == limits.memory (即Guaranteed QoS 類型pod)
盡管 group1剩余內(nèi)存(5G)滿足 Pod2 內(nèi)存需求(5G)队贱,但由于 group1 是多 NUMA 節(jié)點,集群節(jié)點中存在能滿足 pod2 內(nèi)存需求的單 NUMA 節(jié)點(假設(shè)存在), 所以 pod2 準(zhǔn)入請求將被拒絕潭袱。
- 當(dāng) pod3 (非Guaranteed QoS類型)嘗試加入 group1 時柱嫌,將被放行 。這是為什么呢屯换?
因為內(nèi)存管理器只管理 Guaranteed QoS類型 Pod编丘,對于非Guaranteed QoS類型Pod一律放行準(zhǔn)入。
由于 pod3 是非Guaranteed QoS類型 pod 彤悔, pod3 內(nèi)存使用有可能會超過 5G 時嘉抓,當(dāng)出現(xiàn)這種情況時將會將觸發(fā) OOM。這種 OOM 情況有兩種處理機制:
- kubelet 觸發(fā) pod 驅(qū)逐機制晕窑,進而釋放出更多可用內(nèi)存
- 觸發(fā)linux OOM killer抑片,優(yōu)先 kill 低優(yōu)先級進程(如:BestEffort/QoS、Burstable/QoS類型 pod)
兩種機制優(yōu)先級:kubelet > linux OOM killer
單NUMA節(jié)點
- 創(chuàng)建 pod4 杨赤,其中 pod4 內(nèi)存配額為2G敞斋,且 requests.memory == limits.memory (即Guaranteed QoS 類型pod)
內(nèi)存管理器隨后創(chuàng)建 名為 group2 的組,group2 包含 NUMA 節(jié)點與 pod4
- 創(chuàng)建 pod5 疾牲,其中 pod5內(nèi)存配額為6G植捎,且 requests.memory == limits.memory (即Guaranteed QoS 類型pod)
由于group2仍有8G可用內(nèi)存,pod5將被加入到group2
- 創(chuàng)建 pod6 说敏,其中 pod6內(nèi)存配額為3G鸥跟,且 requests.memory == limits.memory (即Guaranteed QoS 類型pod),由于group2僅有2G可用內(nèi)存,pod6將被加入到group3
- 創(chuàng)建 pod7 盔沫,其中 pod6內(nèi)存配額為8G医咨,且 requests.memory == limits.memory (即Guaranteed QoS 類型pod),由于group3僅有7G可用內(nèi)存,pod7準(zhǔn)入請求將被拒絕架诞。(盡管group2 + group3 剩余容量之和滿pod7拟淮,但無法跨group)
參考資料