利用 NUMA 感知記憶體管理器

功能狀態: Kubernetes v1.32 [穩定] (預設啟用:true)

Kubernetes _記憶體管理器_ 啟用 Guaranteed QoS 類別中 Pod 的保證記憶體(和巨頁)分配功能。

記憶體管理器採用提示產生協定,為 Pod 產生最適合的 NUMA 親和性。記憶體管理器將這些親和性提示饋送到中央管理器(_拓撲管理器_)。根據提示和拓撲管理器策略,Pod 將被拒絕或允許加入節點。

此外,記憶體管理器確保 Pod 請求的記憶體從最少數量的 NUMA 節點分配。

記憶體管理器僅適用於基於 Linux 的主機。

開始之前

您需要有一個 Kubernetes 叢集,並且必須設定 kubectl 命令列工具以與您的叢集通訊。建議在至少兩個未充當控制平面主機的節點的叢集上執行本教學課程。如果您還沒有叢集,可以使用 minikube 建立一個,或者您可以使用以下 Kubernetes playground 之一

您的 Kubernetes 伺服器必須是 v1.32 版本。若要檢查版本,請輸入 kubectl version

為了使記憶體資源與 Pod 規格中其他請求的資源對齊

  • 應該啟用 CPU 管理器,並且應在節點上設定正確的 CPU 管理器策略。請參閱 控制 CPU 管理策略
  • 應該啟用拓撲管理器,並且應在節點上設定正確的拓撲管理器策略。請參閱 控制拓撲管理策略

從 v1.22 開始,記憶體管理器透過 MemoryManager 功能閘道 預設啟用。

在 v1.22 之前,必須使用以下標誌啟動 kubelet

--feature-gates=MemoryManager=true

以便啟用記憶體管理器功能。

記憶體管理器如何運作?

記憶體管理器目前為 Guaranteed QoS 類別中的 Pod 提供保證記憶體(和巨頁)分配。若要立即將記憶體管理器投入運作,請遵循 記憶體管理器組態 章節中的指南,然後準備和部署 Guaranteed Pod,如 將 Pod 放置在 Guaranteed QoS 類別中 章節所示。

記憶體管理器是一個提示提供者,它為拓撲管理器提供拓撲提示,然後拓撲管理器根據這些拓撲提示對齊請求的資源。在 Linux 上,它還為 Pod 強制執行 cgroups(即 cpuset.mems)。關於 Pod 許可和部署過程的完整流程圖在 記憶體管理器 KEP:設計總覽 和下方說明

Memory Manager in the pod admission and deployment process

在此過程中,記憶體管理器會更新儲存在 節點地圖和記憶體地圖 中的內部計數器,以管理保證記憶體分配。

記憶體管理器在啟動和執行期間更新節點地圖,如下所示。

啟動

當節點管理員使用 --reserved-memory保留記憶體標誌 章節)時,就會發生這種情況。在這種情況下,節點地圖將會更新以反映此保留,如 記憶體管理器 KEP:啟動時的記憶體地圖(附範例) 所示。

當設定 Static 策略時,管理員必須提供 --reserved-memory 標誌。

執行期

參考 記憶體管理器 KEP:執行時的記憶體地圖(附範例) 說明成功的 Pod 部署如何影響節點地圖,以及 Kubernetes 或作業系統如何進一步處理潛在的記憶體不足 (OOM) 情況。

在記憶體管理器運作的上下文中,重要的主題是 NUMA 群組的管理。每次 Pod 的記憶體請求超過單一 NUMA 節點容量時,記憶體管理器都會嘗試建立一個包含多個 NUMA 節點並具有擴展記憶體容量的群組。這個問題已在 記憶體管理器 KEP:如何跨多個 NUMA 節點啟用保證記憶體分配? 中詳細說明。此外,參考 記憶體管理器 KEP:模擬 - 記憶體管理器如何運作?(附範例) 說明群組的管理是如何發生的。

Windows 支援

功能狀態: Kubernetes v1.32 [Alpha] (預設停用:false)

可以透過 WindowsCPUAndMemoryAffinity 功能閘道啟用 Windows 支援,並且需要容器執行期的支援。Windows 上僅支援 BestEffort 策略

記憶體管理器組態

應先預先設定其他管理器。接下來,應啟用記憶體管理器功能,並使用 Static 策略執行(Static 策略 章節)。或者,可以為系統或 kubelet 程序保留一些記憶體,以提高節點穩定性(保留記憶體標誌 章節)。

策略

記憶體管理器支援兩種策略。您可以透過 kubelet 標誌 --memory-manager-policy 選擇策略

  • None (預設)
  • Static (僅限 Linux)
  • BestEffort (僅限 Windows)

None 策略

這是預設的策略,且不會以任何方式影響記憶體配置。它的作用就像記憶體管理器完全不存在一樣。

None 策略會返回預設的拓撲提示。這個特殊的提示表示提示提供者(在此情況下為記憶體管理器)對於任何資源的 NUMA 親和性沒有偏好。

靜態策略

Guaranteed Pod 的情況下,Static 記憶體管理器策略會返回與可以保證記憶體的 NUMA 節點集合相關的拓撲提示,並透過更新內部的 NodeMap 物件來保留記憶體。

BestEffortBurstable Pod 的情況下,由於沒有保證記憶體的需求,Static 記憶體管理器策略會返回預設的拓撲提示,並且不會在內部的 NodeMap 物件中保留記憶體。

此策略僅在 Linux 上受支援。

盡力而為策略

功能狀態: Kubernetes v1.32 [Alpha] (預設停用:false)

此策略僅在 Windows 上受支援。

在 Windows 上,NUMA 節點分配與 Linux 的運作方式不同。沒有機制可以確保記憶體存取僅來自特定的 NUMA 節點。相反地,Windows 排程器會根據 CPU 分配選擇最優的 NUMA 節點。Windows 可能會使用其他 NUMA 節點,如果 Windows 排程器認為這樣做是最佳的。

此策略確實會透過內部的 NodeMap 追蹤可用和請求的記憶體數量。記憶體管理器將盡力確保在進行分配之前,NUMA 節點上有足夠的記憶體可用。
這表示在大多數情況下,記憶體分配應該會如預期般運作。

保留記憶體旗標

節點管理員通常使用 節點可分配資源 (Node Allocatable) 機制來為 kubelet 或作業系統程序保留 K8S 節點系統資源,以增強節點穩定性。一組專用的旗標可用於此目的,以設定節點的總保留記憶體量。此預先配置的值隨後用於計算節點可供 Pod 使用的實際「可分配」記憶體量。

Kubernetes 排程器整合了「可分配資源」以優化 Pod 排程程序。上述旗標包括 --kube-reserved--system-reserved--eviction-threshold。它們的值總和將計算出保留記憶體的總量。

記憶體管理器新增了一個新的 --reserved-memory 旗標,以允許(由節點管理員)分割此總保留記憶體,並據此跨多個 NUMA 節點進行保留。

此旗標指定每個 NUMA 節點的不同記憶體類型的以逗號分隔的記憶體保留清單。跨多個 NUMA 節點的記憶體保留可以使用分號作為分隔符號來指定。此參數僅在記憶體管理器功能的情境下有用。記憶體管理器不會使用此保留記憶體來分配容器工作負載。

例如,如果您有一個 NUMA 節點 "NUMA0",其可用記憶體為 10Gi,並且指定 --reserved-memory 在 "NUMA0" 保留 1Gi 的記憶體,則記憶體管理器會假設只有 9Gi 可供容器使用。

您可以省略此參數,但是,您應該注意,來自所有 NUMA 節點的保留記憶體量應等於 節點可分配資源 (Node Allocatable) 功能 指定的記憶體量。如果至少有一個節點可分配資源參數為非零值,您將需要為至少一個 NUMA 節點指定 --reserved-memory。實際上,eviction-hard 閾值預設值為 100Mi,因此如果使用 Static 策略,則 --reserved-memory 是必須的。

此外,請避免以下配置

  1. 重複項,即相同的 NUMA 節點或記憶體類型,但具有不同的值;
  2. 為任何記憶體類型設定零限制;
  3. 機器硬體中不存在的 NUMA 節點 ID;
  4. 記憶體類型名稱不是 memoryhugepages-<size> (特定 <size> 的巨頁 (hugepages) 也應該存在)。

語法

--reserved-memory N:記憶體類型1=值1,記憶體類型2=值2,...

  • N (整數) - NUMA 節點索引,例如 0
  • memory-type (字串) - 代表記憶體類型
    • memory - 傳統記憶體
    • hugepages-2Mihugepages-1Gi - 巨頁 (hugepages)
  • value (字串) - 保留記憶體的數量,例如 1Gi

使用範例

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

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

--reserved-memory '0:memory=1Gi;1:memory=2Gi'

當您為 --reserved-memory 旗標指定值時,您必須遵守您先前透過節點可分配資源 (Node Allocatable Feature) 旗標提供的設定。也就是說,對於每種記憶體類型,都必須遵守以下規則

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

其中 i 是 NUMA 節點的索引。

如果您不遵循上述公式,記憶體管理器將在啟動時顯示錯誤。

換句話說,上面的範例說明,對於傳統記憶體 (type=memory),我們總共保留了 3Gi,即

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

與節點可分配資源 (Node Allocatable) 配置相關的 kubelet 命令列引數範例

  • --kube-reserved=cpu=500m,memory=50Mi
  • --system-reserved=cpu=123m,memory=333Mi
  • --eviction-hard=memory.available<500Mi

這是一個正確配置的範例

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

在 Kubernetes 1.32 之前,您還需要新增

--feature-gates=MemoryManager=true

讓我們驗證上面的配置

  1. kube-reserved + system-reserved + eviction-hard(預設) = reserved-memory(0) + reserved-memory(1)
  2. 4GiB + 1GiB + 100MiB = 3GiB + 2148MiB
  3. 5120MiB + 100MiB = 3072MiB + 2148MiB
  4. 5220MiB = 5220MiB (正確)

將 Pod 放置在 Guaranteed QoS 類別中

如果選取的策略不是 None,則記憶體管理器會識別屬於 Guaranteed QoS 類別的 Pod。記憶體管理器會為每個 Guaranteed Pod 提供特定的拓撲提示給拓撲管理器。對於 QoS 類別不是 Guaranteed 的 Pod,記憶體管理器會提供預設的拓撲提示給拓撲管理器。

以下 Pod 清單摘錄將 Pod 指派給 Guaranteed QoS 類別。

requests 等於 limits 時,具有整數 CPU 的 Pod 會在 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"

此外,當 requests 等於 limits 時,共用 CPU 的 Pod 也會在 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"

請注意,必須為 Pod 指定 CPU 和記憶體請求,才能將其歸類為 Guaranteed QoS 類別。

疑難排解

以下方法可用於疑難排解 Pod 無法部署或在節點上遭到拒絕的原因

  • Pod 狀態 - 指示拓撲親和性錯誤
  • 系統日誌 - 包含用於除錯的寶貴資訊,例如,關於產生的提示
  • 狀態檔案 - 記憶體管理器內部狀態的傾印 (包含 Node Map 和 Memory Maps)
  • 從 v1.22 開始,可以使用 裝置外掛程式資源 API 來檢索關於為容器保留的記憶體的資訊

Pod 狀態 (TopologyAffinityError)

此錯誤通常在以下情況發生

  • 節點沒有足夠的可用資源來滿足 Pod 的請求
  • Pod 的請求由於特定的拓撲管理器策略限制而被拒絕

此錯誤會出現在 Pod 的狀態中

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

使用 kubectl describe pod <id>kubectl get events 來取得詳細的錯誤訊息

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

系統日誌

搜尋關於特定 Pod 的系統日誌。

在日誌中可以找到記憶體管理器為 Pod 產生的一組提示。此外,CPU 管理器產生的一組提示也應該在日誌中。

拓撲管理器合併這些提示以計算出單一最佳提示。最佳提示也應該在日誌中。

最佳提示指示在哪裡分配所有資源。拓撲管理器根據其當前策略測試此提示,並根據判斷結果,決定是否允許 Pod 加入節點或拒絕它。

此外,搜尋日誌中與記憶體管理器相關的事件,例如,找出關於 cgroupscpuset.mems 更新的資訊。

檢查節點上的記憶體管理器狀態

讓我們先部署一個範例 Guaranteed Pod,其規格如下

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"]

接下來,讓我們登入部署它的節點,並檢查 /var/lib/kubelet/memory_manager_state 中的狀態檔案

{
   "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
}

可以從狀態檔案中推斷出,Pod 已釘選到兩個 NUMA 節點,即

"numaAffinity":[
   0,
   1
],

「釘選 (Pinned)」一詞表示 Pod 的記憶體消耗 (透過 cgroups 配置) 受限於這些 NUMA 節點。

這自動暗示記憶體管理器實例化了一個新群組,該群組包含這兩個 NUMA 節點,即索引為 01 的 NUMA 節點。

請注意,群組的管理以相對複雜的方式處理,更詳細的說明在記憶體管理器 KEP 的 節中提供。

為了分析群組中可用的記憶體資源,必須將屬於該群組的 NUMA 節點中的相應條目加總。

例如,群組中可用的「傳統」記憶體總量可以透過加總群組中每個 NUMA 節點上可用的可用記憶體來計算,即 NUMA 節點 0"memory" 區段 ("free":0) 和 NUMA 節點 1"memory" 區段 ("free":103739236352)。因此,此群組中可用的「傳統」記憶體總量等於 0 + 103739236352 位元組。

"systemReserved":3221225472 表示此節點的管理員使用 --reserved-memory 旗標,在 NUMA 節點 0 上保留了 3221225472 位元組(即 3Gi)以服務 kubelet 和系統程序。

裝置外掛程式資源 API

kubelet 提供 PodResourceLister gRPC 服務,以啟用資源和相關聯的 metadata 的探索。透過使用其 List gRPC 端點,可以檢索關於每個容器的保留記憶體資訊,該資訊包含在 protobuf ContainerMemory 訊息中。此資訊僅能為 Guaranteed QoS 類別中的 Pod 檢索。

接下來是什麼

最後修改時間:2024 年 11 月 21 日下午 7:38 PST:Update content/en/docs/tasks/administer-cluster/memory-manager.md (0867522df3)