本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

記憶體資源的服務質量

Kubernetes v1.22(於 2021 年 8 月釋出)引入了一項新的 Alpha 功能,改進了 Linux 節點實現記憶體資源請求和限制的方式。

在之前的版本中,Kubernetes 不支援記憶體質量保證。例如,如果將容器資源設定如下:

apiVersion: v1
kind: Pod
metadata:
  name: example
spec:
  containers:
  - name: nginx
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "64Mi"
        cpu: "500m"

spec.containers[].resources.requests(例如 cpu、記憶體)用於排程。當你建立一個 Pod 時,Kubernetes 排程器會為該 Pod 選擇一個節點來執行。每個節點對每種資源型別都有最大容量:它可以為 Pods 提供的 CPU 和記憶體量。排程器確保,對於每種資源型別,已排程的容器的資源請求總和小於節點的容量。

spec.containers[].resources.limits 在 kubelet 啟動容器時傳遞給容器執行時。CPU 被認為是“可壓縮”資源。如果您的應用程式開始達到 CPU 限制,Kubernetes 會開始限制您的容器,從而可能導致您的應用程式效能下降。但是,它不會被終止。這就是“可壓縮”的含義。

在 cgroup v1 中,並且在該功能之前,容器執行時從未考慮並有效地忽略了 spec.containers[].resources.requests["memory"]。這與 CPU 不同,在 CPU 中,容器執行時會同時考慮請求和限制。此外,cgroup v1 中的記憶體實際上無法壓縮。由於無法限制記憶體使用,如果容器超過其記憶體限制,它將被核心透過 OOM (Out Of Memory) 殺死而終止。

幸運的是,cgroup v2 帶來了新的設計和實現,以實現記憶體的全面保護。新功能依賴於 cgroups v2,大多數當前釋出的 Linux 作業系統都已提供該功能。透過這項實驗性功能,Pod 和容器的服務質量擴充套件到不僅涵蓋 CPU 時間,還包括記憶體。

它是如何工作的?

記憶體 QoS 使用 cgroup v2 的記憶體控制器來保證 Kubernetes 中的記憶體資源。Pod 中容器的記憶體請求和限制用於設定記憶體控制器提供的特定介面 memory.minmemory.high。當 memory.min 設定為記憶體請求時,記憶體資源被保留,並且永遠不會被核心回收;這就是記憶體 QoS 確保 Kubernetes Pods 記憶體可用性的方式。如果在容器中設定了記憶體限制,這意味著系統需要限制容器的記憶體使用,記憶體 QoS 使用 memory.high 來限制接近記憶體限制的工作負載,確保系統不會因瞬時記憶體分配而過載。

下表詳細說明了這兩個引數的具體功能以及它們與 Kubernetes 容器資源的對應關係。

檔案描述
memory.minmemory.min 指定 cgroup 必須始終保留的最小記憶體量,即系統永遠不能回收的記憶體。如果 cgroup 的記憶體使用達到此低限制並且無法增加,系統 OOM killer 將被呼叫。

我們將其對映到容器的記憶體請求
memory.highmemory.high 是記憶體使用限制。這是控制 cgroup 記憶體使用的主要機制。如果 cgroup 的記憶體使用超過此處指定的高邊界,則 cgroup 的程序將被限制並承受嚴重的回收壓力。預設值為 max,表示沒有限制。

我們使用一個公式來計算 memory.high,具體取決於容器的記憶體限制或節點可分配記憶體(如果容器的記憶體限制為空)以及一個限制因子。有關該公式的更多詳細資訊,請參閱 KEP。

當發出容器記憶體請求時,kubelet 會在容器建立期間透過 CRI 中的 Unified 欄位將 memory.min 傳遞給後端 CRI 執行時(可能是 containerd、cri-o)。容器級別 cgroup 中的 memory.min 將設定為


i: Pod 中的第 i 個容器

由於 memory.min 介面要求所有祖先 cgroup 目錄都已設定,因此需要正確設定 Pod 和節點 cgroup 目錄。

Pod 級別 cgroup 中的 memory.min

i: Pod 中的第 i 個容器

節點級別 cgroup 中的 memory.min

i: 節點中的第 i 個 Pod,j: Pod 中的第 j 個容器

Kubelet 將直接使用 runc libcontainer 庫管理 Pod 級別和節點級別 cgroups 的 cgroup 層次結構,而容器 cgroup 限制由容器執行時管理。

對於記憶體限制,除了限制記憶體使用的原始方式外,記憶體 QoS 還增加了一個限制記憶體分配的額外功能。引入了一個限制因子作為乘數(預設為 0.8)。如果記憶體限制乘以該因子的結果大於記憶體請求,kubelet 將把 memory.high 設定為該值並透過 CRI 使用 Unified。如果容器未指定記憶體限制,kubelet 將改用節點可分配記憶體。容器級別 cgroup 中的 memory.high 設定為


i: Pod 中的第 i 個容器

這有助於在 Pod 記憶體使用增加時提高穩定性,確保記憶體接近限制時受到限制。

我該如何使用它?

以下是在您的 Linux 節點上啟用記憶體 QoS 的先決條件,其中一些與 Kubernetes 對 cgroup v2 的支援有關。

  1. Kubernetes v1.22 及更高版本
  2. runc v1.0.0-rc93 及更高版本;containerd v1.4 及更高版本;cri-o v1.20 及更高版本
  3. Linux 核心最低版本:4.15,推薦版本:5.2+
  4. 啟用 cgroupv2 的 Linux 映象或手動啟用 cgroupv2 unified_cgroup_hierarchy

OCI 執行時(如 runc 和 crun)已經支援 cgroups v2 Unified,Kubernetes CRI 也已進行所需更改以支援傳遞 Unified。但是,CRI 執行時支援也是必需的。Alpha 階段的記憶體 QoS 旨在支援 containerd 和 cri-o。相關 PR 功能:containerd-cri 支援 LinuxContainerResources.Unified #5627 已合併,將在 containerd 1.6 中釋出。CRI-O 為 1.22 實現 kube alpha 功能 #5207 仍在進行中。

滿足這些先決條件後,您可以啟用記憶體 QoS 功能門(請參閱透過配置檔案設定 kubelet 引數)。

我如何瞭解更多資訊?

您可以找到更多詳細資訊如下:

我如何參與?

你可以透過多種方式聯絡 SIG Node

您也可以直接聯絡我