利用 NUMA 感知記憶體管理器

特性狀態: Kubernetes v1.32 [stable] (預設啟用:true)

Kubernetes 記憶體管理器 實現了在 Guaranteed QoS 類 中為 Pod 保證記憶體(和巨頁)分配的功能。

記憶體管理器採用提示生成協議來為 Pod 生成最合適的 NUMA 親和性。記憶體管理器將這些親和性提示提供給中央管理器(拓撲管理器)。根據提示和拓撲管理器策略,Pod 被拒絕或被接納到節點。

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

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

準備工作

你需要擁有一個 Kubernetes 叢集,並且 kubectl 命令列工具已配置為與你的叢集通訊。建議在至少有兩個不是控制平面主機的節點的叢集上執行本教程。如果你還沒有叢集,你可以使用 minikube 建立一個,或者你可以使用這些 Kubernetes 遊樂場之一

你的 Kubernetes 伺服器必須是 v1.32 或更高版本。

要檢查版本,請輸入 kubectl version

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

從 v1.22 開始,記憶體管理器透過 MemoryManager 特性門控 預設啟用。

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

--feature-gates=MemoryManager=true

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

記憶體管理器如何運作?

記憶體管理器目前為 Guaranteed QoS 類中的 Pod 提供有保證的記憶體(和巨頁)分配。要立即啟用記憶體管理器,請遵循 記憶體管理器配置 部分中的指南,然後,按照 將 Pod 放入 Guaranteed QoS 類 部分的說明準備和部署一個 Guaranteed Pod。

記憶體管理器是一個提示提供者,它為拓撲管理器提供拓撲提示,然後拓撲管理器根據這些拓撲提示對齊請求的資源。在 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] (預設停用)

Windows 支援可以透過 WindowsCPUAndMemoryAffinity 特性門控啟用,並且需要容器執行時的支援。Windows 上僅支援 BestEffort 策略

記憶體管理器配置

其他管理器應首先預配置。接下來,應啟用記憶體管理器功能並使用 Static 策略執行(靜態策略 部分)。可選地,可以為系統或 kubelet 程序保留一定量的記憶體,以提高節點穩定性(保留記憶體標誌 部分)。

策略

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

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

None 策略

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

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

Static 策略

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

對於 BestEffortBurstable Pod,Static 記憶體管理器策略傳送回預設拓撲提示,因為沒有請求保證記憶體,並且不在內部 NodeMap 物件中保留記憶體。

此策略僅在 Linux 上受支援。

BestEffort 策略

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

此策略僅在 Windows 上受支援。

在 Windows 上,NUMA 節點分配的工作方式與 Linux 不同。沒有機制可以確保記憶體訪問僅來自特定的 NUMA 節點。相反,Windows 排程程式將根據 CPU 分配選擇最最佳化 NUMA 節點。如果 Windows 排程程式認為最佳,Windows 可能會使用其他 NUMA 節點。

該策略透過內部 NodeMap 跟蹤可用和請求的記憶體量。記憶體管理器將在分配之前盡力確保 NUMA 節點上有足夠的記憶體可用。
這意味著在大多數情況下,記憶體分配應按預期執行。

保留記憶體標誌

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

Kubernetes 排程程式包含“可分配”以最佳化 Pod 排程過程。上述標誌包括 --kube-reserved--system-reserved--eviction-threshold。它們的總和將構成保留記憶體的總量。

記憶體管理器中添加了一個新的 --reserved-memory 標誌,以允許此總保留記憶體由節點管理員拆分並在多個 NUMA 節點上相應保留。

該標誌指定了每 NUMA 節點不同記憶體型別的逗號分隔記憶體保留列表。跨多個 NUMA 節點的記憶體保留可以使用分號作為分隔符指定。此引數僅在記憶體管理器功能的上下文中有效。記憶體管理器不會將此保留記憶體用於容器工作負載的分配。

例如,如果你有一個可用記憶體為 10Gi 的 NUMA 節點“NUMA0”,並且指定了 --reserved-memory 以在“NUMA0”保留 1Gi 記憶體,則記憶體管理器假定只有 9Gi 可用於容器。

你可以省略此引數,但是,你應該注意所有 NUMA 節點的保留記憶體量應等於 節點可分配功能 指定的記憶體量。如果至少一個節點可分配引數不為零,則你需要為至少一個 NUMA 節點指定 --reserved-memory。實際上,eviction-hard 閾值預設為 100Mi,因此如果使用 Static 策略,則 --reserved-memory 是強制性的。

此外,避免以下配置

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

語法

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

  • N(整數)- NUMA 節點索引,例如 0
  • memory-type(字串)- 表示記憶體型別
    • memory - 常規記憶體
    • hugepages-2Mihugepages-1Gi - 巨頁
  • 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 標誌指定值時,你必須遵守你之前透過節點可分配功能標誌提供的設定。也就是說,對於每種記憶體型別都必須遵守以下規則

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

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(default) = 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 提供特定的拓撲提示給拓撲管理器。對於不屬於 Guaranteed QoS 類的 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 狀態 - 指示拓撲親和性錯誤
  • 系統日誌 - 包含有價值的除錯資訊,例如有關生成的提示
  • 狀態檔案 - 記憶體管理器內部狀態的轉儲(包括 節點對映和記憶體對映
  • 從 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
],

“固定”一詞表示 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 標誌保留了 3221225472 位元組(即 3Gi)以在 NUMA 節點 0 上為 kubelet 和系統程序提供服務。

裝置外掛資源 API

kubelet 提供了一個 PodResourceLister gRPC 服務,以實現資源和關聯元資料的發現。透過使用其 List gRPC 端點,可以檢索每個容器保留記憶體的資訊,該資訊包含在 protobuf ContainerMemory 訊息中。此資訊只能針對 Guaranteed QoS 類中的 Pod 檢索。

下一步

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