Kubernetes 使用NUMA感知的內(nèi)存管理器

2022-06-07 10:58 更新

使用 NUMA 感知的內(nèi)存管理器

FEATURE STATE: Kubernetes v1.22 [beta]

Kubernetes 內(nèi)存管理器(Memory Manager)為 ?Guaranteed ?QoS 類 的 Pods 提供可保證的內(nèi)存(及大頁面)分配能力。

內(nèi)存管理器使用提示生成協(xié)議來為 Pod 生成最合適的 NUMA 親和性配置。 內(nèi)存管理器將這類親和性提示輸入給中央管理器(即 Topology Manager)。 基于所給的提示和 Topology Manager(拓?fù)涔芾砥鳎┑牟呗栽O(shè)置,Pod 或者會(huì)被某節(jié)點(diǎn)接受,或者被該節(jié)點(diǎn)拒絕。

此外,內(nèi)存管理器還確保 Pod 所請(qǐng)求的內(nèi)存是從盡量少的 NUMA 節(jié)點(diǎn)分配而來。

內(nèi)存管理器僅能用于 Linux 主機(jī)。

在開始之前

你必須擁有一個(gè) Kubernetes 的集群,同時(shí)你的 Kubernetes 集群必須帶有 kubectl 命令行工具。 建議在至少有兩個(gè)節(jié)點(diǎn)的集群上運(yùn)行本教程,且這些節(jié)點(diǎn)不作為控制平面主機(jī)。 如果你還沒有集群,你可以通過 Minikube 構(gòu)建一個(gè)你自己的集群,或者你可以使用下面任意一個(gè) Kubernetes 工具構(gòu)建:

您的 Kubernetes 服務(wù)器必須是 v1.21 或更高版本。 要檢查版本,請(qǐng)輸入 ?kubectl version?。

為了使得內(nèi)存資源與 Pod 規(guī)約中所請(qǐng)求的其他資源對(duì)齊:

  • CPU 管理器應(yīng)該被啟用,并且在節(jié)點(diǎn)(Node)上要配置合適的 CPU 管理器策略。
  • 拓?fù)涔芾砥饕粏⒂?,并且要在?jié)點(diǎn)上配置合適的拓?fù)涔芾砥鞑呗浴?/li>

從 v1.22 開始,內(nèi)存管理器通過 特性門控 ?MemoryManager ?默認(rèn)啟用。

在 v1.22 之前,?kubelet ?必須在啟動(dòng)時(shí)設(shè)置如下標(biāo)志:

?--feature-gates=MemoryManager=true ?

這樣內(nèi)存管理器特性才會(huì)被啟用。

內(nèi)存管理器如何運(yùn)作?

內(nèi)存管理器目前為 Guaranteed QoS 類中的 Pod 提供可保證的內(nèi)存(和大頁面)分配能力。 若要立即將內(nèi)存管理器啟用,可參照本節(jié)的指南, 之后按本節(jié)中所展示的,準(zhǔn)備并部署一個(gè) ?Guaranteed? Pod。

內(nèi)存管理器是一個(gè)提示驅(qū)動(dòng)組件(Hint Provider),負(fù)責(zé)為拓?fù)涔芾砥魈峁┩負(fù)涮崾荆?nbsp;后者根據(jù)這些拓?fù)涮崾緦?duì)所請(qǐng)求的資源執(zhí)行對(duì)齊操作。 內(nèi)存管理器也會(huì)為 Pods 應(yīng)用 ?cgroups ?設(shè)置(即 ?cpuset.mems?)。 與 Pod 準(zhǔn)入和部署流程相關(guān)的完整流程圖在Memory Manager KEP: Design Overview 和下面。

memory-manager-diagram

在這個(gè)過程中,內(nèi)存管理器會(huì)更新其內(nèi)部存儲(chǔ)于節(jié)點(diǎn)映射和內(nèi)存映射中的計(jì)數(shù)器, 從而管理有保障的內(nèi)存分配。

內(nèi)存管理器在啟動(dòng)和運(yùn)行期間按下述邏輯更新節(jié)點(diǎn)映射(Node Map)。

啟動(dòng) 

當(dāng)節(jié)點(diǎn)管理員應(yīng)用 ?--reserved-memory? 預(yù)留內(nèi)存標(biāo)志時(shí)執(zhí)行此邏輯。 這時(shí),節(jié)點(diǎn)映射會(huì)被更新以反映內(nèi)存的預(yù)留,如 Memory Manager KEP: Memory Maps at start-up (with examples) 所說明。

當(dāng)配置了 ?Static ?策略時(shí),管理員必須提供 ?--reserved-memory? 標(biāo)志設(shè)置。

運(yùn)行時(shí) 

參考文獻(xiàn) Memory Manager KEP: Memory Maps at runtime (with examples) 中說明了成功的 Pod 部署是如何影響節(jié)點(diǎn)映射的,該文檔也解釋了可能發(fā)生的內(nèi)存不足 (Out-of-memory,OOM)情況是如何進(jìn)一步被 Kubernetes 或操作系統(tǒng)處理的。

在內(nèi)存管理器運(yùn)作的語境中,一個(gè)重要的話題是對(duì) NUMA 分組的管理。 每當(dāng) Pod 的內(nèi)存請(qǐng)求超出單個(gè) NUMA 節(jié)點(diǎn)容量時(shí),內(nèi)存管理器會(huì)嘗試創(chuàng)建一個(gè)包含多個(gè) NUMA 節(jié)點(diǎn)的分組,從而擴(kuò)展內(nèi)存容量。解決這個(gè)問題的詳細(xì)描述在文檔 Memory Manager KEP: How to enable the guaranteed memory allocation over many NUMA nodes? 中。同時(shí),關(guān)于 NUMA 分組是如何管理的,你還可以參考文檔 Memory Manager KEP: Simulation - how the Memory Manager works? (by examples)

內(nèi)存管理器配置 

其他管理器也要預(yù)先配置。接下來,內(nèi)存管理器特性需要被啟用, 并且采用 ?Static ?策略(靜態(tài)策略)運(yùn)行。 作為可選操作,可以預(yù)留一定數(shù)量的內(nèi)存給系統(tǒng)或者 kubelet 進(jìn)程以增強(qiáng)節(jié)點(diǎn)的 穩(wěn)定性(預(yù)留內(nèi)存標(biāo)志)。

策略 

內(nèi)存管理器支持兩種策略。你可以通過 ?kubelet ?標(biāo)志 ?--memory-manager-policy? 來 選擇一種策略:

  • ?None ?(默認(rèn))
  • ?Static?

None 策略 

這是默認(rèn)的策略,并且不會(huì)以任何方式影響內(nèi)存分配。該策略的行為好像內(nèi)存管理器不存在一樣。

?None ?策略返回默認(rèn)的拓?fù)涮崾拘畔?。這種特殊的提示會(huì)表明拓?fù)潋?qū)動(dòng)組件(Hint Provider) (在這里是內(nèi)存管理器)對(duì)任何資源都沒有與 NUMA 親和性關(guān)聯(lián)的偏好。

Static 策略 

對(duì) ?Guaranteed ?Pod 而言,?Static ?內(nèi)存管理器策略會(huì)返回拓?fù)涮崾拘畔?,該信?nbsp;與內(nèi)存分配有保障的 NUMA 節(jié)點(diǎn)集合有關(guān),并且內(nèi)存管理器還通過更新內(nèi)部的 節(jié)點(diǎn)映射 對(duì)象來完成內(nèi)存預(yù)留。

對(duì) ?BestEffort ?或 ?Burstable ?Pod 而言,因?yàn)椴淮嬖趯?duì)有保障的內(nèi)存資源的請(qǐng)求, ?Static ?內(nèi)存管理器策略會(huì)返回默認(rèn)的拓?fù)涮崾?,并且不?huì)通過內(nèi)部的節(jié)點(diǎn)映射對(duì)象 來預(yù)留內(nèi)存。

預(yù)留內(nèi)存標(biāo)志 

節(jié)點(diǎn)可分配機(jī)制通常 被節(jié)點(diǎn)管理員用來為 kubelet 或操作系統(tǒng)進(jìn)程預(yù)留 K8S 節(jié)點(diǎn)上的系統(tǒng)資源,目的是提高節(jié)點(diǎn)穩(wěn)定性。 有一組專用的標(biāo)志可用于這個(gè)目的,為節(jié)點(diǎn)設(shè)置總的預(yù)留內(nèi)存量。 此預(yù)配置的值接下來會(huì)被用來計(jì)算節(jié)點(diǎn)上對(duì) Pods “可分配的”內(nèi)存。

Kubernetes 調(diào)度器在優(yōu)化 Pod 調(diào)度過程時(shí),會(huì)考慮“可分配的”內(nèi)存。 前面提到的標(biāo)志包括 ?--kube-reserved?、?--system-reserved? 和 ?--eviction-threshold?。 這些標(biāo)志值的綜合計(jì)作預(yù)留內(nèi)存的總量。

為內(nèi)存管理器而新增加的 ?--reserved-memory? 標(biāo)志可以(讓節(jié)點(diǎn)管理員)將總的預(yù)留內(nèi)存進(jìn)行劃分, 并完成跨 NUMA 節(jié)點(diǎn)的預(yù)留操作。

標(biāo)志設(shè)置的值是一個(gè)按 NUMA 節(jié)點(diǎn)的不同內(nèi)存類型所給的內(nèi)存預(yù)留的值的列表,用逗號(hào)分開。 可以使用分號(hào)作為分隔符來指定跨多個(gè) NUMA 節(jié)點(diǎn)的內(nèi)存預(yù)留。 只有在內(nèi)存管理器特性被啟用的語境下,這個(gè)參數(shù)才有意義。 內(nèi)存管理器不會(huì)使用這些預(yù)留的內(nèi)存來為容器負(fù)載分配內(nèi)存。

例如,如果你有一個(gè)可用內(nèi)存為 10Gi 的 NUMA 節(jié)點(diǎn) "NUMA0",而參數(shù) ?--reserved-memory? 被設(shè)置成要在 "NUMA0" 上預(yù)留 1Gi 的內(nèi)存,那么內(nèi)存管理器會(huì)假定節(jié)點(diǎn)上只有 9Gi 內(nèi)存可用于容器負(fù)載。

你也可以忽略此參數(shù),不過這樣做時(shí),你要清楚,所有 NUMA 節(jié)點(diǎn)上預(yù)留內(nèi)存的數(shù)量要等于 節(jié)點(diǎn)可分配特性 所設(shè)定的內(nèi)存量。如果至少有一個(gè)節(jié)點(diǎn)可分配參數(shù)值為非零,你就需要至少為一個(gè) NUMA 節(jié)點(diǎn)設(shè)置 ?--reserved-memory?。實(shí)際上,?eviction-hard? 閾值默認(rèn)為 100Mi, 所以當(dāng)使用 ?Static ?策略時(shí),?--reserved-memory? 是必須設(shè)置的。

此外,應(yīng)盡量避免如下配置:

  1. 重復(fù)的配置,即同一 NUMA 節(jié)點(diǎn)或內(nèi)存類型被設(shè)置不同的取值;
  2. 為某種內(nèi)存類型設(shè)置約束值為零;
  3. 使用物理硬件上不存在的 NUMA 節(jié)點(diǎn) ID;
  4. 使用名字不是 ?memory ?或 ?hugepages-<size>? 的內(nèi)存類型名稱 (特定的 ?<size>? 的大頁面也必須存在)。

語法:

?--reserved-memory N:memory-type1=value1,memory-type2=value2,...?

  • ?N?(整數(shù))- NUMA 節(jié)點(diǎn)索引,例如,?0?
  • ?memory-type?(字符串)- 代表內(nèi)存類型:
    • ?memory ?- 常規(guī)內(nèi)存;
    • ?hugepages-2Mi? 或 ?hugepages-1Gi? - 大頁面
  • ?value?(字符串) - 預(yù)留內(nèi)存的量,例如 ?1Gi?

用法示例:

?--reserved-memory 0:memory=1Gi,hugepages-1Gi=2Gi?

或者

?--reserved-memory 0:memory=1Gi --reserved-memory 1:memory=2Gi?

當(dāng)你為 ?--reserved-memory? 標(biāo)志指定取值時(shí),必須要遵從之前通過節(jié)點(diǎn)可分配特性標(biāo)志所設(shè)置的值。 換言之,對(duì)每種內(nèi)存類型而言都要遵從下面的規(guī)則:

?sum(reserved-memory(i)) = kube-reserved + system-reserved + eviction-threshold ?

其中,?i? 是 NUMA 節(jié)點(diǎn)的索引。

如果你不遵守上面的公示,內(nèi)存管理器會(huì)在啟動(dòng)時(shí)輸出錯(cuò)誤信息。

換言之,上面的例子我們一共要預(yù)留 ?3Gi? 的常規(guī)內(nèi)存(?type=memory?),即:

?sum(reserved-memory(i)) = reserved-memory(0) + reserved-memory(1) = 1Gi + 2Gi = 3Gi ?

下面的例子中給出與節(jié)點(diǎn)可分配配置相關(guān)的 kubelet 命令行參數(shù):

  • ?--kube-reserved=cpu=500m,memory=50Mi ?
  • ?--system-reserved=cpu=123m,memory=333Mi ?
  • ?--eviction-hard=memory.available<500Mi?
Note:
默認(rèn)的硬性驅(qū)逐閾值是 100MiB,不是零。 請(qǐng)記得在使用 ?--reserved-memory? 設(shè)置要預(yù)留的內(nèi)存量時(shí),加上這個(gè)硬性驅(qū)逐閾值。 否則 kubelet 不會(huì)啟動(dòng)內(nèi)存管理器,而會(huì)輸出一個(gè)錯(cuò)誤信息。

下面是一個(gè)正確配置的示例:

--feature-gates=MemoryManager=true 
--kube-reserved=cpu=4,memory=4Gi 
--system-reserved=cpu=1,memory=1Gi 
--memory-manager-policy=Static 
--reserved-memory '0:memory=3Gi;1:memory=2148Mi'

我們對(duì)上面的配置做一個(gè)檢查:

  1. ?kube-reserved + system-reserved + eviction-hard(default) = reserved-memory(0) + reserved-memory(1) ?
  2. ?4GiB + 1GiB + 100MiB = 3GiB + 2148MiB ?
  3. ?5120MiB + 100MiB = 3072MiB + 2148MiB ?
  4. ?5220MiB = 5220MiB? (這是對(duì)的)

將 Pod 放入 Guaranteed QoS 類 

若所選擇的策略不是 ?None?,則內(nèi)存管理器會(huì)辨識(shí)處于 ?Guaranteed ?QoS 類中的 Pod。 內(nèi)存管理器為每個(gè) ?Guaranteed ?Pod 向拓?fù)涔芾砥魈峁┩負(fù)涮崾拘畔ⅰ?nbsp;對(duì)于不在 ?Guaranteed ?QoS 類中的其他 Pod,內(nèi)存管理器向拓?fù)涔芾砥魈峁┠J(rèn)的 拓?fù)涮崾拘畔ⅰ?

下面的來自 Pod 清單的片段將 Pod 加入到 ?Guaranteed ?QoS 類中。

當(dāng) Pod 的 CPU ?requests ?等于 ?limits ?且為整數(shù)值時(shí),Pod 將運(yùn)行在 ?Guaranteed ?QoS 類中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"

此外,共享 CPU 的 Pods 在 ?requests ?等于 ?limits ?值時(shí)也運(yùn)行在 ?Guaranteed ?QoS 類中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"

要注意的是,只有 CPU 和內(nèi)存請(qǐng)求都被設(shè)置時(shí),Pod 才會(huì)進(jìn)入 Guaranteed QoS 類。

故障排查 

下面的方法可用來排查為什么 Pod 無法被調(diào)度或者被節(jié)點(diǎn)拒絕:

  • Pod 狀態(tài) - 可表明拓?fù)溆H和性錯(cuò)誤
  • 系統(tǒng)日志 - 包含用來調(diào)試的有價(jià)值的信息,例如,關(guān)于所生成的提示信息
  • 狀態(tài)文件 - 其中包含內(nèi)存管理器內(nèi)部狀態(tài)的轉(zhuǎn)儲(chǔ)(包含節(jié)點(diǎn)映射和內(nèi)存映射
  • 從 v1.22 開始,設(shè)備插件資源 API 可以用來 檢索關(guān)于為容器預(yù)留的內(nèi)存的信息

Pod 狀態(tài) (TopologyAffinityError) 

這類錯(cuò)誤通常在以下情形出現(xiàn):

  • 節(jié)點(diǎn)缺少足夠的資源來滿足 Pod 請(qǐng)求
  • Pod 的請(qǐng)求因?yàn)樘囟ǖ耐負(fù)涔芾砥鞑呗韵拗贫痪芙^

錯(cuò)誤信息會(huì)出現(xiàn)在 Pod 的狀態(tài)中:

kubectl get pods
NAME         READY   STATUS                  RESTARTS   AGE
guaranteed   0/1     TopologyAffinityError   0          113s

使用 ?kubectl describe pod <id>? 或 ?kubectl get events? 可以獲得詳細(xì)的錯(cuò)誤信息。

Warning  TopologyAffinityError  10m   kubelet, dell8  Resources cannot be allocated with Topology locality

系統(tǒng)日志 

針對(duì)特定的 Pod 搜索系統(tǒng)日志。

內(nèi)存管理器為 Pod 所生成的提示信息可以在日志中找到。 此外,日志中應(yīng)該也存在 CPU 管理器所生成的提示信息。

拓?fù)涔芾砥鲗⑦@些提示信息進(jìn)行合并,計(jì)算得到唯一的最合適的提示數(shù)據(jù)。 此最佳提示數(shù)據(jù)也應(yīng)該出現(xiàn)在日志中。

最佳提示表明要在哪里分配所有的資源。拓?fù)涔芾砥鲿?huì)用當(dāng)前的策略來測(cè)試此數(shù)據(jù), 并基于得出的結(jié)論或者接納 Pod 到節(jié)點(diǎn),或者將其拒絕。

此外,你可以搜索日志查找與內(nèi)存管理器相關(guān)的其他條目,例如 ?cgroups? 和 ?cpuset.mems? 的更新信息等。

檢查節(jié)點(diǎn)上內(nèi)存管理器狀態(tài)

我們首先部署一個(gè) ?Guaranteed ?Pod 示例,其規(guī)約如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: guaranteed
spec:
  containers:
  - name: guaranteed
    image: consumer
    imagePullPolicy: Never
    resources:
      limits:
        cpu: "2"
        memory: 150Gi
      requests:
        cpu: "2"
        memory: 150Gi
    command: ["sleep","infinity"]

接下來,我們登錄到 Pod 運(yùn)行所在的節(jié)點(diǎn),檢查位于 ?/var/lib/kubelet/memory_manager_state? 的狀態(tài)文件:

{
   "policyName":"Static",
   "machineState":{
      "0":{
         "numberOfAssignments":1,
         "memoryMap":{
            "hugepages-1Gi":{
               "total":0,
               "systemReserved":0,
               "allocatable":0,
               "reserved":0,
               "free":0
            },
            "memory":{
               "total":134987354112,
               "systemReserved":3221225472,
               "allocatable":131766128640,
               "reserved":131766128640,
               "free":0
            }
         },
         "nodes":[
            0,
            1
         ]
      },
      "1":{
         "numberOfAssignments":1,
         "memoryMap":{
            "hugepages-1Gi":{
               "total":0,
               "systemReserved":0,
               "allocatable":0,
               "reserved":0,
               "free":0
            },
            "memory":{
               "total":135286722560,
               "systemReserved":2252341248,
               "allocatable":133034381312,
               "reserved":29295144960,
               "free":103739236352
            }
         },
         "nodes":[
            0,
            1
         ]
      }
   },
   "entries":{
      "fa9bdd38-6df9-4cf9-aa67-8c4814da37a8":{
         "guaranteed":[
            {
               "numaAffinity":[
                  0,
                  1
               ],
               "type":"memory",
               "size":161061273600
            }
         ]
      }
   },
   "checksum":4142013182
}

從這個(gè)狀態(tài)文件,可以推斷 Pod 被同時(shí)綁定到兩個(gè) NUMA 節(jié)點(diǎn),即:

"numaAffinity":[
   0,
   1
],

術(shù)語綁定(pinned)意味著 Pod 的內(nèi)存使用被(通過 ?cgroups ?配置)限制到 這些 NUMA 節(jié)點(diǎn)。

這也直接意味著內(nèi)存管理器已經(jīng)創(chuàng)建了一個(gè) NUMA 分組,由這兩個(gè) NUMA 節(jié)點(diǎn)組成, 即索引值分別為 ?0? 和 ?1? 的 NUMA 節(jié)點(diǎn)。

注意 NUMA 分組的管理是有一個(gè)相對(duì)復(fù)雜的管理器處理的,相關(guān)邏輯的進(jìn)一步細(xì)節(jié)可在內(nèi)存管理器的 KEP 中示例1跨 NUMA 節(jié)點(diǎn)節(jié)找到。

為了分析 NUMA 組中可用的內(nèi)存資源,必須對(duì)分組內(nèi) NUMA 節(jié)點(diǎn)對(duì)應(yīng)的條目進(jìn)行匯總。

例如,NUMA 分組中空閑的“常規(guī)”內(nèi)存的總量可以通過將分組內(nèi)所有 NUMA 節(jié)點(diǎn)上空閑內(nèi)存加和來計(jì)算,即將 NUMA 節(jié)點(diǎn) ?0? 和 NUMA 節(jié)點(diǎn) ?1? 的 ?"memory"? 節(jié) (分別是 ?"free":0? 和 ?"free": 103739236352?)相加,得到此分組中空閑的“常規(guī)” 內(nèi)存總量為 ?0 + 103739236352? 字節(jié)。

?"systemReserved": 3221225472? 這一行表明節(jié)點(diǎn)的管理員使用 ?--reserved-memory? 為 NUMA 節(jié)點(diǎn) ?0? 上運(yùn)行的 kubelet 和系統(tǒng)進(jìn)程預(yù)留了 ?3221225472 ?字節(jié) (即 ?3Gi?)。

設(shè)備插件資源 API 

通過使用此 API, 可以獲得每個(gè)容器的預(yù)留內(nèi)存信息,該信息位于 protobuf 協(xié)議的 ?ContainerMemory ?消息中。 只能針對(duì) Guaranteed QoS 類中的 Pod 來檢索此信息。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)