Kubernetes 1.32 中為 Linux 使用者帶來的全新 Swap 功能
Swap 是 Linux 的一項基本且無價的功能。它提供了許多好處,例如透過交換未使用的資料來有效地增加節點的記憶體,防止節點因系統級記憶體峰值而崩潰,防止 Pod 在達到記憶體限制時崩潰,以及更多。因此,Kubernetes 專案中的節點特別興趣小組(node special interest group)為支援 Linux 節點上的 swap 付出了巨大的努力。
1.22 版本引入了對在 Linux 節點上按節點配置 Kubernetes 工作負載的 swap 記憶體使用量的 Alpha 支援。後來,在 1.28 版本中,Linux 節點上的 swap 支援已升級為 Beta 版,並附帶了許多新改進。在後續的 Kubernetes 版本中,還進行了更多改進,為不久的將來全面可用(GA)鋪平了道路。
在 1.22 版本之前,Kubernetes 不支援 Linux 系統上的 swap 記憶體。這是因為當涉及 swap 記憶體時,很難保證和計算 pod 的記憶體利用率。因此,在 Kubernetes 的初始設計中,swap 支援被認為超出範圍,並且 kubelet 的預設行為是如果節點上檢測到 swap 記憶體,則啟動失敗。
在 1.22 版本中,Linux 的 swap 功能最初以 Alpha 階段引入。這為 Linux 使用者提供了首次嘗試使用 swap 功能的機會。然而,作為一個 Alpha 版本,它並未完全開發,僅在有限的環境中部分工作。
在 1.28 版本中,Linux 節點上的 swap 支援已升級為 Beta。Beta 版本是向前邁出的巨大一步。它不僅修復了大量錯誤並使 swap 穩定執行,還帶來了 cgroup v2 支援,引入了各種測試,包括節點級壓力等複雜場景。它還帶來了許多令人興奮的新功能,例如 LimitedSwap
行為(為容器設定自動計算的 swap 限制)、OpenMetrics 儀器支援(透過 /metrics/resource
端點)以及用於 VerticalPodAutoscaler 的 Summary API(透過 /stats/summary
端點),等等。
今天,我們正在進行更多改進,為 GA 鋪平道路。目前,重點特別放在確保節點穩定性、增強除錯能力、解決使用者反饋、完善功能並使其穩定。例如,為了提高穩定性,高優先順序 Pod 中的容器無法訪問 swap,這確保了它們所需的記憶體已準備就緒。此外,UnlimitedSwap
行為已被刪除,因為它可能會損害節點的健康。還引入了防止 swap 洩露的 Secret 內容保護(有關更多資訊,請參閱相關的安全風險部分)。
總而言之,與之前的版本相比,kubelet 對啟用 swap 執行的支援更穩定、更健壯、更使用者友好,並解決了許多已知的功能不足。儘管如此,NodeSwap 功能提供了基本的 swap 支援,而這僅僅是開始。在不久的將來,計劃增加更多功能來增強 swap 的功能,例如改進驅逐、擴充套件 API、增加可定製性等等!
我該如何使用它?
為了使 kubelet 能夠在啟用了 swap 的節點上初始化,必須在 kubelet 的配置設定中將 failSwapOn
欄位設定為 false
,或者停用已棄用的 --fail-swap-on
命令列標誌。
可以配置 memorySwap.swapBehavior
選項來定義節點使用 swap 記憶體的方式。例如,
# this fragment goes into the kubelet's configuration file
memorySwap:
swapBehavior: LimitedSwap
當前可用的 swapBehavior
配置選項是:
NoSwap
(預設):Kubernetes 工作負載不能使用 swap。但是,Kubernetes 範圍之外的程序,例如系統守護程序(如 kubelet 本身!)可以使用 swap。這種行為有利於保護節點免受系統級記憶體峰值的影響,但不能保護工作負載本身免受此類峰值的影響。LimitedSwap
:Kubernetes 工作負載可以使用 swap 記憶體,但有一些限制。Pod 可用的 swap 量是根據請求的記憶體與節點總記憶體的比例自動確定的。只有位於 Burstable 服務質量(QoS)層級的非高優先順序 Pod 才允許使用 swap。有關更多詳細資訊,請參閱下面的部分。
如果未指定 memorySwap
的配置,則預設情況下,kubelet 將應用與 NoSwap
設定相同的行為。
在 Linux 節點上,Kubernetes 只支援使用 cgroup v2 的主機執行 swap。在 cgroup v1 系統上,所有 Kubernetes 工作負載不允許使用 swap 記憶體。
使用 kubeadm 安裝一個啟用交換的叢集
開始之前
此演示要求安裝 kubeadm 工具,並遵循kubeadm 安裝指南中的步驟。如果節點上已啟用 swap,則叢集建立可以繼續。如果未啟用 swap,請參閱提供的啟用 swap 的說明。
建立交換檔案並開啟交換
我將演示建立 4GiB 的 swap,包括加密和未加密的情況。
設定未加密 swap
可以按以下方式設定未加密的 swap 檔案。
# Allocate storage and restrict access
fallocate --length 4GiB /swapfile
chmod 600 /swapfile
# Format the swap space
mkswap /swapfile
# Activate the swap space for paging
swapon /swapfile
設定加密 swap
可以按以下方式設定加密的 swap 檔案。請注意,此示例使用 cryptsetup
二進位制檔案(在大多數 Linux 發行版中都可用)。
# Allocate storage and restrict access
fallocate --length 4GiB /swapfile
chmod 600 /swapfile
# Create an encrypted device backed by the allocated storage
cryptsetup --type plain --cipher aes-xts-plain64 --key-size 256 -d /dev/urandom open /swapfile cryptswap
# Format the swap space
mkswap /dev/mapper/cryptswap
# Activate the swap space for paging
swapon /dev/mapper/cryptswap
驗證 swap 是否已啟用
可以使用 swapon -s
命令或 free
命令來驗證 swap 是否已啟用。
> swapon -s
Filename Type Size Used Priority
/dev/dm-0 partition 4194300 0 -2
> free -h
total used free shared buff/cache available
Mem: 3.8Gi 1.3Gi 249Mi 25Mi 2.5Gi 2.5Gi
Swap: 4.0Gi 0B 4.0Gi
啟動時啟用 swap
設定 swap 後,要在啟動時啟動 swap 檔案,您可以設定一個 systemd 單元來啟用(加密)swap,或者將類似 /swapfile swap swap defaults 0 0
的行新增到 /etc/fstab
。
設定使用啟用 swap 的節點的 Kubernetes 叢集
為使事情更清楚,這裡有一個用於 swap 啟用叢集的示例 kubeadm 配置檔案 kubeadm-config.yaml
。
---
apiVersion: "kubeadm.k8s.io/v1beta3"
kind: InitConfiguration
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
failSwapOn: false
memorySwap:
swapBehavior: LimitedSwap
然後使用 kubeadm init --config kubeadm-config.yaml
建立一個單節點叢集。在 init 過程中,會出現一個警告,提示節點上啟用了 swap,並且 kubelet 的 failSwapOn
設定為 true。我們計劃在未來的版本中刪除此警告。
使用 LimitedSwap 時如何確定交換限制?
Swap 記憶體的配置(包括其限制)是一個重大挑戰。它不僅容易配置錯誤,而且作為一個系統級屬性,任何配置錯誤都可能潛在地損害整個節點,而不僅僅是特定的工作負載。為了降低此風險並確保節點的健康,我們已實現 Swap 並自動配置限制。
在 LimitedSwap
模式下,不允許不屬於 Burstable QoS 分類(即 BestEffort
/Guaranteed
QoS Pods)的 Pod 使用 swap 記憶體。BestEffort
QoS Pods 的記憶體消耗模式不可預測,並且缺乏關於其記憶體使用量的資訊,因此難以確定安全的 swap 記憶體分配。相反,Guaranteed
QoS Pods 通常用於依賴於工作負載精確分配的資源的應用,其記憶體可立即使用。為了維護上述的安全性和節點健康保證,當 LimitedSwap
生效時,這些 Pod 不允許使用 swap 記憶體。此外,高優先順序 Pod 不允許使用 swap,以確保它們消耗的記憶體始終駐留在磁碟上,從而隨時可用。
在詳細說明交換限制的計算之前,有必要定義以下術語:
nodeTotalMemory
:節點上可用的物理記憶體總量。totalPodsSwapAvailable
:節點上可供 Pod 使用的交換記憶體總量(一些交換記憶體可能為系統使用而保留)。containerMemoryRequest
:容器的記憶體請求。
Swap 限制配置為:(containerMemoryRequest / nodeTotalMemory) × totalPodsSwapAvailable
換句話說,一個容器能夠使用的交換量與其記憶體請求、節點的總物理記憶體以及節點上可供 Pod 使用的總交換記憶體量成正比。
需要注意的是,對於 Burstable QoS Pods 中的容器,可以透過將記憶體請求指定為等於記憶體限制來選擇不使用交換。以這種方式配置的容器將無法訪問交換記憶體。
它是如何工作的?
有多種可能的方式可以設想節點上的 swap 使用。當節點上已配置並可用 swap 時,kubelet 可以配置為
- 它可以在交換開啟的情況下啟動。
- 預設情況下,它將指示容器執行時介面(Container Runtime Interface)為 Kubernetes 工作負載分配零交換記憶體。
節點上的 Swap 配置透過 KubeletConfiguration 中的 memorySwap
向叢集管理員公開。作為叢集管理員,您可以透過設定 memorySwap.swapBehavior
來指定節點在存在 swap 記憶體時的行為。
kubelet 使用 CRI(容器執行時介面)API,並指示容器執行時以適當的方式配置特定的 cgroup v2 引數(例如 memory.swap.max
),以啟用容器所需的 swap 配置。對於使用控制組的執行時,容器執行時負責將這些設定寫入容器級別的 cgroup。
如何監控 swap?
節點和容器級別的指標統計資訊
Kubelet 現在收集節點和容器級別的指標統計資訊,這些資訊可以透過 /metrics/resource
(主要用於 Prometheus 等監控工具)和 /stats/summary
(主要用於 Autoscalers)kubelet HTTP 端點訪問。這使得可以直接查詢 kubelet 的客戶端可以在使用 LimitedSwap
時監控 swap 使用量和剩餘 swap 記憶體。此外,已向 cadvisor 添加了 machine_swap_bytes
指標,用於顯示機器的總物理 swap 容量。有關更多資訊,請參閱此頁面。
Node Feature Discovery (NFD)
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
在此示例中,swap 配置在節點 k8s-worker1
和 k8s-worker2
上,但在 k8s-worker3
上沒有。
注意事項
在系統上可用 swap 會降低可預測性。雖然 swap 可以透過提供更多可用的 RAM 來提高效能,但將資料交換回記憶體是一項繁重的工作,有時會比正常情況慢幾個數量級,這可能導致意外的效能迴歸。此外,swap 改變了系統在記憶體壓力下的行為。啟用 swap 會增加“嘈雜鄰居”的風險,即頻繁使用 RAM 的 Pod 可能會導致其他 Pod 進行 swap。此外,由於 swap 允許 Kubernetes 中工作負載使用不可預測地計數的記憶體,並且由於意外的打包配置,排程器目前不考慮 swap 記憶體使用量。這增加了“嘈雜鄰居”的風險。
啟用了 swap 記憶體的節點的效能取決於底層的物理儲存。當使用 swap 記憶體時,在 IOPS(每秒輸入輸出操作)受限的環境(例如具有 I/O 節流的雲虛擬機器)中,效能會比固態驅動器或 NVMe 等更快的儲存介質差得多。由於 swap 可能會導致 I/O 壓力,因此建議為系統關鍵守護程序提供更高的 I/O 延遲優先順序。請參閱下方推薦實踐部分的相關部分。
記憶體支援的卷
在 Linux 節點上,記憶體支援的卷(例如 secret
卷掛載,或具有 medium: Memory
的 emptyDir
)是透過 tmpfs
檔案系統實現的。這些卷的內容應始終保留在記憶體中,因此不應交換到磁碟。為確保這些卷的內容保留在記憶體中,正在使用 noswap
tmpfs 選項。
Linux 核心正式支援從 6.3 版本開始的 noswap
選項(有關更多資訊,請參閱Linux Kernel Version Requirements)。但是,不同的發行版通常會選擇將此掛載選項反向移植到更舊的 Linux 版本。
為了驗證節點是否支援 noswap
選項,kubelet 將執行以下操作:
- 如果核心版本高於 6.3,則假定
noswap
選項受支援。 - 否則,kubelet 將在啟動時嘗試使用
noswap
選項掛載一個 dummy tmpfs。如果 kubelet 因未知選項錯誤而失敗,則假定noswap
不受支援,因此將不使用。將發出一個 kubelet 日誌條目,警告使用者記憶體支援的卷可能會交換到磁碟。如果 kubelet 成功,將刪除 dummy tmpfs 並使用noswap
選項。- 如果
noswap
選項不受支援,kubelet 將發出警告日誌條目,然後繼續執行。
- 如果
強烈建議加密 swap 空間。請參閱上面的部分,其中有一個設定未加密 swap 的示例。然而,處理加密 swap 不在 kubelet 的範圍之內;而是作業系統級別的通用配置,應在該級別處理。管理員有責任配置加密 swap 以減輕此風險。
在 Kubernetes 叢集中使用 swap 的最佳實踐
為系統關鍵守護程序停用 swap
在測試階段和根據使用者反饋,觀察到系統關鍵守護程序和服務的效能可能會下降。這意味著系統守護程序(包括 kubelet)的執行速度可能會比平時慢。如果遇到此問題,建議配置系統切片(system slice)的 cgroup 以防止 swap(即,設定 memory.swap.max=0
)。
保護系統關鍵守護程序免受 I/O 延遲的影響
Swap 會增加節點的 I/O 負載。當記憶體壓力導致核心快速地將頁面 in 和 out 交換時,依賴 I/O 操作的系統關鍵守護程序和服務可能會經歷效能下降。
為了緩解這種情況,建議 systemd 使用者在 I/O 延遲方面優先考慮系統切片。對於非 systemd 使用者,建議為系統守護程序和程序設定專用的 cgroup,並以相同的方式優先 I/O 延遲。這可以透過為系統切片設定 io.latency
來實現,從而使其獲得更高的 I/O 優先順序。有關更多資訊,請參閱cgroup 的文件。
Swap 和控制平面節點
Kubernetes 專案建議在執行控制平面節點時不要配置任何 swap 空間。控制平面主要託管 Guaranteed QoS Pods,因此 swap 通常可以停用。主要顧慮是控制平面上的關鍵服務進行 swap 可能會對效能產生負面影響。
使用專用磁碟進行 swap
建議為 swap 分割槽使用單獨的、加密的磁碟。如果 swap 位於分割槽或根檔案系統上,工作負載可能會干擾需要寫入磁碟的系統程序。當它們共享同一磁碟時,程序可能會使 swap 飽和,從而破壞 kubelet、容器執行時和 systemd 的 I/O,這將影響其他工作負載。由於 swap 空間位於磁碟上,因此確保磁碟足夠快以滿足預期用例至關重要。或者,可以配置單個後端裝置的不同對映區域之間的 I/O 優先順序。
展望未來
正如您所見,swap 功能在最近得到了極大的改進,為功能 GA 鋪平了道路。然而,這僅僅是開始。這是一個基礎性的實現,標誌著增強 swap 功能的開端。
在不久的將來,計劃增加更多功能來進一步改進 swap 的能力,包括更好的驅逐機制、擴充套件的 API 支援、更高的可定製性、更好的除錯能力等等!
我如何瞭解更多資訊?
您可以查閱當前文件以瞭解如何在 Kubernetes 中使用 swap。
我如何參與?
您的反饋始終受到歡迎!SIG Node 定期開會,並可透過 Slack(頻道 #sig-node)或 SIG 的郵件列表聯絡。還有一個專門討論 swap 的 Slack 頻道,名為 #sig-node-swap。
如果您想幫忙或有進一步的問題,請隨時聯絡我,Itamar Holder(Slack 和 GitHub 上為@iholder101)。