交換記憶體管理
Kubernetes 可以配置為在節點上使用 swap 記憶體,允許核心透過將頁面換出到後端儲存來釋放物理記憶體。這對於多種用例都很有用。例如,執行可以從使用 swap 中受益的工作負載的節點,例如那些記憶體佔用大但在任何給定時間只訪問其中一部分記憶體的工作負載。它還有助於防止 Pod 在記憶體壓力高峰期間被終止,保護節點免受可能損害其穩定性的系統級記憶體高峰的影響,允許在節點上進行更靈活的記憶體管理,等等。
如何使用?
先決條件
- 必須在節點上啟用並配置 swap。
- 節點必須執行 Linux 作業系統。
- 節點必須使用 cgroup v2。Kubernetes 不支援 cgroup v1 節點上的 swap。
為 Kubernetes 工作負載啟用 swap
要允許 Kubernetes 工作負載使用 swap,您必須停用 kubelet 預設的在檢測到 swap 時失敗的行為,並將 memory-swap 行為指定為 LimitedSwap
。
更新 kubelet 配置
# this fragment goes into the kubelet's configuration file
failSwapOn: false
memorySwap:
swapBehavior: LimitedSwap
swapBehavior
的可用選項是
NoSwap
(預設):Kubernetes 工作負載不能使用 swap。然而,Kubernetes 範圍之外的程序,例如系統守護程序(如 kubelet 本身!)可以使用 swap。這種行為有利於保護節點免受系統級記憶體高峰的影響,但它不能保護工作負載本身免受此類高峰的影響。LimitedSwap
:Kubernetes 工作負載可以使用 swap 記憶體。可用於 Pod 的 swap 量會自動確定。有關更多詳細資訊,請參閱下面的章節。
如果未指定 memorySwap
的配置,kubelet 將預設應用與 NoSwap
設定相同的行為。
請注意,以下 Pod 將被排除在 swap 訪問之外(更多資訊請參閱下面的章節)
- 未歸類為“可突發 QoS (Burstable QoS)”的 Pod。
- 高優先順序 Pod。
- 記憶體限制等於記憶體請求的容器。
注意
Kubernetes 僅支援 Linux 節點上的 swap。它是如何工作的?
節點上使用 swap 的方式有多種可能。如果 kubelet 已經在節點上執行,則在配置 swap 後需要重新啟動它才能識別 swap。
當 kubelet 在配置並可用 swap 的節點上啟動時(使用 failSwapOn: false
配置),kubelet 將
- 能夠在啟用 swap 的節點上啟動。
- 預設指示容器執行時介面(CRI)實現(通常稱為容器執行時)為 Kubernetes 工作負載分配零 swap 記憶體。
節點上的 swap 配置透過 KubeletConfiguration 中的 memorySwap
暴露給叢集管理員。作為叢集管理員,您可以透過設定 memorySwap.swapBehavior
來指定節點在存在 swap 記憶體時的行為。
kubelet 使用容器執行時 API,並指示容器執行時應用特定配置(例如,在 cgroup v2 的情況下,memory.swap.max
),以啟用容器所需的 swap 配置。對於使用控制組(cgroups)的執行時,容器執行時負責將這些設定寫入容器級 cgroup。
swap 使用的可觀測性
節點和容器級指標統計
Kubelet 現在收集節點和容器級指標統計資訊,可以透過 /metrics/resource
(主要由 Prometheus 等監控工具使用)和 /stats/summary
(主要由自動擴縮器使用)kubelet HTTP 端點訪問。這允許直接請求 kubelet 的客戶端在使用 LimitedSwap
時監控 swap 使用情況和剩餘 swap 記憶體。此外,cadvisor 中添加了 machine_swap_bytes
指標,以顯示機器的總物理 swap 容量。有關更多資訊,請參閱此頁面。
例如,支援以下 /metrics/resource
node_swap_usage_bytes
:節點的當前 swap 使用量(以位元組為單位)。container_swap_usage_bytes
:容器當前 swap 使用量(以位元組為單位)。container_swap_limit_bytes
:容器當前 swap 限制量(以位元組為單位)。
使用 kubectl top --show-swap
查詢指標很有價值,但有些繁瑣,因為這些指標是為軟體而不是人類設計的。為了以更使用者友好的方式使用這些資料,kubectl top
命令已擴充套件以支援 swap 指標,使用 --show-swap
標誌。
為了接收有關節點上 swap 使用情況的資訊,可以使用 kubectl top nodes --show-swap
kubectl top nodes --show-swap
這將產生類似於以下內容的輸出
NAME CPU(cores) CPU(%) MEMORY(bytes) MEMORY(%) SWAP(bytes) SWAP(%)
node1 1m 10% 2Mi 10% 1Mi 0%
node2 5m 10% 6Mi 10% 2Mi 0%
node3 3m 10% 4Mi 10% <unknown> <unknown>
為了接收有關 Pod swap 使用情況的資訊,可以使用 kubectl top nodes --show-swap
kubectl top pod -n kube-system --show-swap
這將產生類似於以下內容的輸出
NAME CPU(cores) MEMORY(bytes) SWAP(bytes)
coredns-58d5bc5cdb-5nbk4 2m 19Mi 0Mi
coredns-58d5bc5cdb-jsh26 3m 37Mi 0Mi
etcd-node01 51m 143Mi 5Mi
kube-apiserver-node01 98m 824Mi 16Mi
kube-controller-manager-node01 20m 135Mi 9Mi
kube-proxy-ffgs2 1m 24Mi 0Mi
kube-proxy-fhvwx 1m 39Mi 0Mi
kube-scheduler-node01 13m 69Mi 0Mi
metrics-server-8598789fdb-d2kcj 5m 26Mi 0Mi
節點將 swap 容量作為節點狀態的一部分進行報告
現在添加了一個新的節點狀態欄位 node.status.nodeInfo.swap.capacity
,用於報告節點的 swap 容量。
例如,以下命令可用於檢索叢集中節點的 swap 容量
kubectl get nodes -o go-template='{{range .items}}{{.metadata.name}}: {{if .status.nodeInfo.swap.capacity}}{{.status.nodeInfo.swap.capacity}}{{else}}<unknown>{{end}}{{"\n"}}{{end}}'
這將產生類似於以下內容的輸出
node1: 21474836480
node2: 42949664768
node3: <unknown>
注意
<unknown>
值表示該節點的 .status.nodeInfo.swap.capacity
欄位未設定。這可能意味著該節點未配置 swap,或者不太可能,kubelet 無法確定節點的 swap 容量。使用節點特徵發現(NFD)進行 swap 發現
節點特徵發現 (Node Feature Discovery) 是一個 Kubernetes 外掛,用於檢測硬體特徵和配置。它可以用於發現哪些節點配置了 swap。
例如,要找出哪些節點配置了 swap,請使用以下命令
kubectl get nodes -o jsonpath='{range .items[?(@.metadata.labels.feature\.node\.kubernetes\.io/memory-swap)]}{.metadata.name}{"\t"}{.metadata.labels.feature\.node\.kubernetes\.io/memory-swap}{"\n"}{end}'
這將產生類似於以下內容的輸出
k8s-worker1: true
k8s-worker2: true
k8s-worker3: false
在此示例中,節點 k8s-worker1
和 k8s-worker2
上配置了 swap,但 k8s-worker3
上沒有。
風險和注意事項
注意
強烈建議加密 swap 空間。有關更多資訊,請參閱記憶體支援的卷memory-backed volumes。系統上存在 swap 會降低可預測性。雖然 swap 可以透過提供更多 RAM 來提高效能,但將資料換回記憶體是一項繁重的操作,有時速度會慢很多個數量級,這可能導致意外的效能下降。此外,swap 會改變系統在記憶體壓力下的行為。啟用 swap 會增加“吵鬧的鄰居”的風險,即頻繁使用 RAM 的 Pod 可能會導致其他 Pod 發生 swap。此外,由於 swap 允許 Kubernetes 中的工作負載使用更多的記憶體,而這無法可靠地進行核算,並且由於意外的打包配置,排程器目前不考慮 swap 記憶體使用。這增加了“吵鬧的鄰居”的風險。
啟用 swap 記憶體的節點效能取決於底層物理儲存。當使用 swap 記憶體時,在 IOPS 受限的環境中(例如具有 I/O 限制的雲 VM),效能會明顯低於固態硬碟或 NVMe 等更快的儲存介質。由於 swap 可能會導致 IO 壓力,建議為系統關鍵守護程序設定更高的 IO 延遲優先順序。請參閱下面推薦實踐部分中的相關內容。
記憶體支援的卷
在 Linux 節點上,記憶體支援的卷(例如secret
卷掛載,或帶有 medium: Memory
的emptyDir
)是透過 tmpfs
檔案系統實現的。此類卷的內容應始終保留在記憶體中,因此不應交換到磁碟。為了確保此類卷的內容保留在記憶體中,使用了 noswap
tmpfs 選項。
Linux 核心從 6.3 版本開始正式支援 noswap
選項(更多資訊可在Linux 核心版本要求中找到)。然而,不同的發行版通常也會將此掛載選項反向移植到較舊的 Linux 版本。
為了驗證節點是否支援 noswap
選項,kubelet 將執行以下操作
- 如果核心版本高於 6.3,則假定支援
noswap
選項。 - 否則,kubelet 將在啟動時嘗試使用
noswap
選項掛載一個虛擬 tmpfs。如果 kubelet 因指示未知選項的錯誤而失敗,則假定不支援noswap
,因此將不使用。將發出 kubelet 日誌條目以警告使用者記憶體支援的卷可能會交換到磁碟。如果 kubelet 成功,虛擬 tmpfs 將被刪除,並且將使用noswap
選項。- 如果不支援
noswap
選項,kubelet 將發出警告日誌條目,然後繼續執行。
- 如果不支援
請參閱上面的章節,其中包含設定未加密 swap 的示例。然而,處理加密 swap 不在 kubelet 的範圍之內;相反,它是一個通用的作業系統配置問題,應在該級別解決。管理員有責任提供加密 swap 以緩解此風險。
逐出
為啟用 swap 的節點配置記憶體逐出閾值可能很棘手。
停用 swap 後,將 kubelet 的逐出閾值配置為略低於節點記憶體容量是合理的。其理由是,我們希望 Kubernetes 在節點記憶體不足並呼叫記憶體不足(OOM)殺手之前開始逐出 Pod,因為 OOM 殺手不感知 Kubernetes,因此不考慮 QoS、Pod 優先順序或其他 Kubernetes 特定因素。
啟用 swap 後,情況變得更加複雜。在 Linux 中,vm.min_free_kbytes
引數定義了核心開始積極回收記憶體的記憶體閾值,其中包含換出頁面。如果 kubelet 的逐出閾值設定方式使得在核心開始回收記憶體之前發生逐出,可能會導致工作負載在節點記憶體壓力期間永遠無法換出。然而,將逐出閾值設定得太高可能會導致節點記憶體不足並呼叫 OOM 殺手,這也不是理想情況。
為了解決這個問題,建議將 kubelet 的逐出閾值設定得略低於 vm.min_free_kbytes
值。這樣,節點可以在 kubelet 開始逐出 Pod 之前開始換出,允許工作負載換出未使用的資料並防止發生逐出。另一方面,由於它只是略低,kubelet 可能會在節點記憶體不足之前開始逐出 Pod,從而避免 OOM 殺手。
可以透過在節點上執行以下命令來確定 vm.min_free_kbytes
的值
cat /proc/sys/vm/min_free_kbytes
未使用的 swap 空間
在 LimitedSwap
行為下,可用於 Pod 的 swap 量會根據其請求記憶體與節點總記憶體的比例自動確定(有關更多詳細資訊,請參閱下面的章節)。
這種設計意味著通常會有部分 swap 保持對 Kubernetes 工作負載的限制。例如,由於 Guaranteed QoS Pod 目前不允許使用 swap,因此與記憶體請求成比例的 swap 量將保持不被 Kubernetes 工作負載使用。
這種行為在許多 Pod 不符合 swap 條件的情況下帶來了一些風險。另一方面,它有效地保留了一些系統保留的 swap 記憶體量,可以供 Kubernetes 範圍之外的程序使用,例如系統守護程序甚至 kubelet 本身。
在 Kubernetes 叢集中使用 swap 的最佳實踐
停用系統關鍵守護程序的 swap
在測試階段和根據使用者反饋,我們觀察到系統關鍵守護程序和服務的效能可能會下降。這意味著系統守護程序,包括 kubelet,可能會比平時執行得更慢。如果遇到此問題,建議配置系統切片的 cgroup 以防止交換(即,設定 memory.swap.max=0
)。
保護系統關鍵守護程序的 I/O 延遲
Swap 會增加節點上的 I/O 負載。當記憶體壓力導致核心快速交換頁面時,依賴 I/O 操作的系統關鍵守護程序和服務可能會遇到效能下降。
為了緩解這種情況,建議 systemd 使用者在 I/O 延遲方面優先考慮系統切片。對於非 systemd 使用者,建議為系統守護程序和程序設定一個專用的 cgroup,並以相同的方式優先考慮 I/O 延遲。這可以透過為系統切片設定 io.latency
來實現,從而賦予它更高的 I/O 優先順序。有關更多資訊,請參閱cgroup 文件。
Swap 和控制平面節點
Kubernetes 專案建議執行不配置任何 swap 空間的控制平面節點。控制平面主要託管 Guaranteed QoS Pod,因此通常可以停用 swap。主要擔憂是交換控制平面上的關鍵服務可能會對效能產生負面影響。
使用專用磁碟進行 swap
Kubernetes 專案建議在啟用 swap 的節點上始終使用加密 swap。如果 swap 位於分割槽或根檔案系統上,工作負載可能會干擾需要寫入磁碟的系統程序。當它們共享同一個磁碟時,程序可能會使 swap 不堪重負,從而擾亂 kubelet、容器執行時和 systemd 的 I/O,這將影響其他工作負載。由於 swap 空間位於磁碟上,因此確保磁碟足夠快以滿足預期用例至關重要。或者,可以在單個後端裝置的不用對映區域之間配置 I/O 優先順序。
Swap 感知排程
Kubernetes 1.34 不支援以考慮 swap 記憶體使用的方式將 Pod 分配給節點。排程器通常使用基礎設施資源的“請求”來指導 Pod 放置,而 Pod 不請求 swap 空間;它們只請求 memory
。這意味著排程器在做出排程決策時不考慮 swap 記憶體。雖然這是我們正在積極努力解決的問題,但尚未實施。
為了管理員確保 Pod 不會排程到具有 swap 記憶體的節點上,除非它們明確打算使用它,管理員可以對具有可用 swap 的節點進行汙點處理以防止此問題。汙點將確保容忍 swap 的工作負載在負載下不會溢位到沒有 swap 的節點上。
選擇儲存以實現最佳效能
指定用於 swap 空間的儲存裝置對於在高記憶體使用期間保持系統響應能力至關重要。旋轉硬碟驅動器 (HDD) 不適合此任務,因為其機械特性會引入顯著的延遲,從而導致嚴重的效能下降和系統顛簸。對於現代效能需求,固態驅動器 (SSD) 等裝置可能是 swap 的適當選擇,因為其低延遲電子訪問最大限度地減少了速度下降。
Swap 行為詳情
使用 LimitedSwap 時,swap 限制是如何確定的?
Swap 記憶體的配置,包括其限制,是一個重大挑戰。它不僅容易出現配置錯誤,而且作為系統級屬性,任何配置錯誤都可能危及整個節點,而不僅僅是特定工作負載。為了減輕此風險並確保節點的健康,我們實施了帶有自動限制配置的 Swap。
使用 LimitedSwap
,不屬於可突發 QoS 分類(即 BestEffort
/Guaranteed
QoS Pod)的 Pod 被禁止使用 swap 記憶體。BestEffort
QoS Pod 表現出不可預測的記憶體消耗模式,並且缺乏有關其記憶體使用情況的資訊,因此難以確定安全的 swap 記憶體分配。相反,Guaranteed
QoS Pod 通常用於依賴工作負載指定的精確資源分配的應用程式,其中記憶體是立即可用的。為了保持上述安全性和節點健康保證,當 LimitedSwap
生效時,這些 Pod 不允許使用 swap 記憶體。此外,高優先順序 Pod 不允許使用 swap,以確保它們消耗的記憶體始終駐留在磁碟上,因此隨時可用。
在詳細說明 swap 限制的計算之前,有必要定義以下術語
nodeTotalMemory
:節點上可用的物理記憶體總量。totalPodsSwapAvailable
:節點上可供 Pod 使用的 swap 記憶體總量(部分 swap 記憶體可能保留用於系統使用)。containerMemoryRequest
:容器的記憶體請求。
Swap 限制配置為
( containerMemoryRequest
/ nodeTotalMemory
) × totalPodsSwapAvailable
換句話說,容器能夠使用的 swap 量與其記憶體請求、節點總物理記憶體以及節點上可供 Pod 使用的 swap 記憶體總量成比例。
需要注意的是,對於可突發 QoS Pod 中的容器,可以透過指定等於記憶體限制的記憶體請求來選擇不使用 swap。以這種方式配置的容器將無法訪問 swap 記憶體。
下一步
- 您可以檢視一篇關於 Kubernetes 和 swap 的部落格文章
- 有關更多資訊,請參閱原始 KEP,KEP-2400 及其設計。