節點壓力驅逐
節點壓力驅逐是 kubelet 主動終止 Pod,以回收節點上的資源的程序。
Kubernetes v1.31 [beta]
(預設啟用:true)注意
分割镜像檔案系統 功能,啟用對containerfs
檔案系統的支援,新增了幾個新的驅逐訊號、臨界值和度量。若要使用 containerfs
,Kubernetes 發行版本 v1.32 需要啟用 KubeletSeparateDiskGC
功能閘道。目前,只有 CRI-O(v1.29 或更高版本)提供 containerfs
檔案系統支援。kubelet 監控叢集節點上的記憶體、磁碟空間和檔案系統 inode 等資源。當其中一個或多個資源達到特定的消耗水平時,kubelet 可以主動使節點上的一個或多個 Pod 失敗,以回收資源並防止資源耗盡。
在節點壓力驅逐期間,kubelet 會將選定 Pod 的階段設定為 Failed
,並終止 Pod。
節點壓力驅逐與API 起始的驅逐不同。
kubelet 不會尊重您配置的 Pod 中斷預算 或 Pod 的 terminationGracePeriodSeconds
。如果您使用軟性驅逐臨界值,kubelet 會尊重您配置的 eviction-max-pod-grace-period
。如果您使用硬性驅逐臨界值,kubelet 會使用 0 秒
的寬限期(立即關閉)來終止。
自我修復行為
kubelet 嘗試在終止終端用戶 Pod 之前回收節點層級資源。例如,當磁碟資源耗盡時,它會移除未使用的容器镜像。
如果 Pod 由工作負載管理物件(例如 StatefulSet 或 Deployment)管理,該管理物件會取代失敗的 Pod,則控制平面 (kube-controller-manager
) 會建立新的 Pod 以取代被驅逐的 Pod。
靜態 Pod 的自我修復
如果您在資源壓力下的節點上執行靜態 Pod,則 kubelet 可能會驅逐該靜態 Pod。然後,kubelet 會嘗試建立替換,因為靜態 Pod 始終代表在該節點上執行 Pod 的意圖。
kubelet 在建立替換時會考量靜態 Pod 的優先順序。如果靜態 Pod 清單指定了低優先順序,並且叢集控制平面內定義了較高優先順序的 Pod,而且節點處於資源壓力下,則 kubelet 可能無法為該靜態 Pod 騰出空間。即使節點上有資源壓力,kubelet 仍會繼續嘗試執行所有靜態 Pod。
驅逐訊號與臨界值
kubelet 使用各種參數來制定驅逐決策,例如以下
- 驅逐訊號
- 驅逐臨界值
- 監控間隔
驅逐訊號
驅逐訊號是特定時間點特定資源的目前狀態。kubelet 透過將訊號與驅逐臨界值(即資源在節點上應可用的最小量)進行比較,來制定驅逐決策。
kubelet 使用以下驅逐訊號
驅逐訊號 | 描述 | 僅限 Linux |
---|---|---|
memory.available | memory.available := node.status.capacity[memory] - node.stats.memory.workingSet | |
nodefs.available | nodefs.available := node.stats.fs.available | |
nodefs.inodesFree | nodefs.inodesFree := node.stats.fs.inodesFree | • |
imagefs.available | imagefs.available := node.stats.runtime.imagefs.available | |
imagefs.inodesFree | imagefs.inodesFree := node.stats.runtime.imagefs.inodesFree | • |
containerfs.available | containerfs.available := node.stats.runtime.containerfs.available | |
containerfs.inodesFree | containerfs.inodesFree := node.stats.runtime.containerfs.inodesFree | • |
pid.available | pid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc | • |
在此表格中,「描述」欄位顯示 kubelet 如何取得訊號的值。每個訊號都支援百分比值或文字值。kubelet 會根據與訊號關聯的總容量來計算百分比值。
記憶體訊號
在 Linux 節點上,memory.available
的值是從 cgroupfs 而非像 free -m
這樣的工具衍生而來。這很重要,因為 free -m
在容器中無法運作,而且如果使用者使用節點可分配資源功能,則資源不足的決策會在 cgroup 階層以及根節點的最終使用者 Pod 部分在本機進行。此腳本或cgroupv2 腳本重現了 kubelet 執行以計算 memory.available
的相同步驟集。kubelet 從其計算中排除 inactive_file(非活動 LRU 列表上以檔案為後盾的記憶體位元組數),因為它假設記憶體在壓力下是可回收的。
在 Windows 節點上,memory.available
的值是從節點的全域記憶體提交層級衍生而來(透過 GetPerformanceInfo()
系統呼叫查詢),方法是從節點的 CommitLimit
中減去節點的全域 CommitTotal
。請注意,如果節點的分頁檔大小變更,CommitLimit
就可能會變更!
檔案系統訊號
kubelet 識別三個特定的檔案系統識別碼,這些識別碼可以與驅逐訊號(<identifier>.inodesFree
或 <identifier>.available
)一起使用
nodefs
:節點的主要檔案系統,用於本機磁碟區、非記憶體支援的 emptyDir 磁碟區、日誌儲存、臨時儲存等等。例如,nodefs
包含/var/lib/kubelet
。imagefs
:一個選用的檔案系統,容器執行階段可以使用它來儲存容器映像(即唯讀層)和容器可寫入層。containerfs
:一個選用的檔案系統,容器執行階段可以使用它來儲存可寫入層。與主要檔案系統(請參閱nodefs
)類似,它用於儲存本機磁碟區、非記憶體支援的 emptyDir 磁碟區、日誌儲存和臨時儲存,但容器映像除外。當使用containerfs
時,可以分割imagefs
檔案系統,使其僅儲存映像(唯讀層),而不再儲存其他內容。
因此,kubelet 通常允許容器檔案系統有三個選項
所有內容都在單一
nodefs
上,也稱為「rootfs」或簡稱為「root」,而且沒有專用的映像檔案系統。容器儲存(請參閱
nodefs
)位於專用磁碟上,而imagefs
(可寫入和唯讀層)與根檔案系統分開。這通常稱為「分割磁碟」(或「獨立磁碟」)檔案系統。容器檔案系統
containerfs
(與nodefs
加上可寫入層相同)位於根目錄上,而容器映像(唯讀層)儲存在獨立的imagefs
上。這通常稱為「分割映像」檔案系統。
kubelet 將嘗試直接從底層容器執行階段自動探索這些檔案系統及其目前的組態,並將忽略其他本機節點檔案系統。
kubelet 不支援其他容器檔案系統或儲存組態,而且目前不支援映像和容器的多個檔案系統。
已棄用的 kubelet 垃圾收集功能
某些 kubelet 垃圾收集功能已被棄用,改用驅逐
現有旗標 | 理由 |
---|---|
--maximum-dead-containers | 一旦舊日誌儲存在容器環境之外,即棄用 |
--maximum-dead-containers-per-container | 一旦舊日誌儲存在容器環境之外,即棄用 |
--minimum-container-ttl-duration | 一旦舊日誌儲存在容器環境之外,即棄用 |
驅逐臨界值
您可以指定自訂驅逐閾值,供 kubelet 在做出驅逐決策時使用。您可以設定軟性和硬性驅逐閾值。
驅逐閾值的格式為 [驅逐訊號][運算子][數量]
,其中
例如,如果節點的總記憶體為 10GiB,而且您希望在可用記憶體降至 1GiB 以下時觸發驅逐,您可以將驅逐閾值定義為 memory.available<10%
或 memory.available<1Gi
(您不能同時使用兩者)。
軟性驅逐閾值
軟性驅逐閾值將驅逐閾值與管理者指定的必要寬限期配對。kubelet 在寬限期超過之前不會驅逐 Pod。如果您未指定寬限期,kubelet 會在啟動時傳回錯誤。
您可以為 kubelet 在驅逐期間使用,同時指定軟性驅逐閾值寬限期和最大允許 Pod 終止寬限期。如果您指定了最大允許寬限期,並且符合軟性驅逐閾值,則 kubelet 會使用這兩個寬限期中較短的一個。如果您未指定最大允許寬限期,則 kubelet 會立即終止驅逐的 Pod,而不會進行正常終止。
您可以使用下列旗標來設定軟性驅逐閾值
eviction-soft
:一組驅逐閾值,例如memory.available<1.5Gi
,如果在指定的寬限期內保持超出閾值,可能會觸發 Pod 驅逐。eviction-soft-grace-period
:一組驅逐寬限期,例如memory.available=1m30s
,用於定義軟性驅逐閾值必須保持多久才能觸發 Pod 驅逐。eviction-max-pod-grace-period
:在回應符合軟性驅逐閾值時終止 Pod 時,允許使用的最大寬限期(以秒為單位)。
硬性驅逐閾值
硬性驅逐閾值沒有寬限期。當符合硬性驅逐閾值時,kubelet 會立即終止 Pod,而不會進行正常終止,以回收資源不足的資源。
您可以使用 eviction-hard
旗標來設定一組硬性驅逐閾值,例如 memory.available<1Gi
。
kubelet 具有下列預設硬性驅逐閾值
memory.available<100Mi
(Linux 節點)memory.available<500Mi
(Windows 節點)nodefs.available<10%
imagefs.available<15%
nodefs.inodesFree<5%
(Linux 節點)imagefs.inodesFree<5%
(Linux 節點)
只有在未變更任何參數的情況下,才會設定這些硬性驅逐閾值的預設值。如果您變更任何參數的值,則其他參數的值將不會繼承為預設值,並且將設定為零。為了提供自訂值,您應該分別提供所有閾值。
containerfs.available
和 containerfs.inodesFree
(Linux 節點)預設驅逐閾值將設定如下
如果單一檔案系統用於所有內容,則
containerfs
閾值會設定為與nodefs
相同。如果為映像和容器都設定了獨立的檔案系統,則
containerfs
閾值會設定為與imagefs
相同。
目前不支援設定與 containersfs
相關的閾值的自訂覆寫,如果嘗試這樣做,將會發出警告;因此,任何提供的自訂值都將被忽略。
驅逐監控間隔
kubelet 會根據其設定的 housekeeping-interval
(預設為 10 秒
)來評估驅逐閾值。
節點狀況
kubelet 會報告節點狀況,以反映節點處於壓力之下,因為已符合硬性或軟性驅逐閾值,而與設定的寬限期無關。
kubelet 將驅逐訊號對應到節點狀況,如下所示
節點狀況 | 驅逐訊號 | 描述 |
---|---|---|
MemoryPressure | memory.available | 節點上的可用記憶體已滿足驅逐閾值 |
DiskPressure | nodefs.available 、nodefs.inodesFree 、imagefs.available 、imagefs.inodesFree 、containerfs.available 或 containerfs.inodesFree | 節點的根檔案系統、映像檔案系統或容器檔案系統上的可用磁碟空間和 inode 已滿足驅逐閾值 |
PIDPressure | pid.available | (Linux)節點上的可用程序識別碼已降至驅逐閾值以下 |
控制平面也會對應這些節點狀況到污點。
kubelet 會根據設定的 --node-status-update-frequency
(預設為 10 秒
)來更新節點狀況。
節點狀況震盪
在某些情況下,節點會在軟性驅逐閾值之上和之下震盪,而沒有保持定義的寬限期。這會導致回報的節點狀況在 true
和 false
之間不斷切換,從而導致不良的驅逐決策。
為了防止震盪,您可以使用 eviction-pressure-transition-period
旗標,該旗標控制 kubelet 在將節點狀況轉換為不同狀態之前必須等待多長時間。轉換期間的預設值為 5 分鐘
。
回收節點層級資源
kubelet 會嘗試在驅逐終端使用者 Pod 之前回收節點層級資源。
當回報 DiskPressure
節點狀況時,kubelet 會根據節點上的檔案系統回收節點層級資源。
沒有 imagefs
或 containerfs
如果節點只有一個符合驅逐閾值的 nodefs
檔案系統,則 kubelet 會依下列順序釋放磁碟空間
- 垃圾收集已終止的 Pod 和容器。
- 刪除未使用的映像。
使用 imagefs
如果節點具有專用的 imagefs
檔案系統供容器執行階段使用,則 kubelet 會執行下列動作
如果
nodefs
檔案系統符合驅逐閾值,則 kubelet 會垃圾收集已終止的 Pod 和容器。如果
imagefs
檔案系統符合驅逐閾值,則 kubelet 會刪除所有未使用的映像。
使用 imagefs
和 containerfs
如果節點具有專用的 containerfs
以及為容器執行階段設定的 imagefs
檔案系統,則 kubelet 將嘗試依下列方式回收資源
如果
containerfs
檔案系統符合驅逐閾值,則 kubelet 會垃圾收集已終止的 Pod 和容器。如果
imagefs
檔案系統符合驅逐閾值,則 kubelet 會刪除所有未使用的映像。
Pod 選擇進行 kubelet 驅逐
如果 kubelet 回收節點層級資源的嘗試未能使驅逐訊號低於閾值,則 kubelet 會開始驅逐終端使用者 Pod。
kubelet 使用下列參數來判斷 Pod 驅逐順序
- Pod 的資源使用量是否超過請求
- Pod 優先順序
- Pod 的資源使用量相對於請求
因此,kubelet 會依下列順序對 Pod 進行排名和驅逐
使用量超過請求的
BestEffort
或Burstable
Pod。這些 Pod 會根據其優先順序以及其使用量超出請求的程度來驅逐。使用量低於請求的
Guaranteed
Pod 和Burstable
Pod 最後驅逐,根據其優先順序。
注意
kubelet 不會使用 Pod 的QoS 類別來判斷驅逐順序。您可以使用 QoS 類別來估計回收記憶體等資源時最可能的 Pod 驅逐順序。QoS 分類不適用於 EphemeralStorage 請求,因此如果節點處於DiskPressure
等狀況下,則上述情境將不適用。只有在為所有容器指定請求和限制且它們相等時,才能保證 Guaranteed
Pod。這些 Pod 永遠不會因為另一個 Pod 的資源消耗而被驅逐。如果系統精靈(例如 kubelet
和 journald
)消耗的資源超過透過 system-reserved
或 kube-reserved
配置保留的資源,並且節點只有 Guaranteed
或 Burstable
Pod 使用的資源少於其剩餘請求,則 kubelet 必須選擇驅逐其中一個 Pod,以保持節點穩定性並限制資源匱乏對其他 Pod 的影響。在這種情況下,它將選擇首先驅逐優先順序最低的 Pod。
如果您正在執行靜態 Pod,並且想要避免在資源壓力下將其驅逐,請直接為該 Pod 設定 priority
欄位。靜態 Pod 不支援 priorityClassName
欄位。
當 kubelet 回應 inode 或程序 ID 匱乏而驅逐 Pod 時,它會使用 Pod 的相對優先順序來判斷驅逐順序,因為 inode 和 PID 沒有請求。
kubelet 會根據節點是否具有專用的 imagefs
或 containerfs
檔案系統,以不同的方式對 Pod 進行排序
沒有 imagefs
或 containerfs
(nodefs
和 imagefs
使用相同的檔案系統)
- 如果
nodefs
觸發驅逐,則 kubelet 會根據 Pod 的總磁碟使用量(本機磁碟區 + 日誌和所有容器的可寫入層
)對 Pod 進行排序。
使用 imagefs
(nodefs
和 imagefs
檔案系統是分開的)
如果
nodefs
觸發驅逐,則 kubelet 會根據nodefs
使用量(本機磁碟區 + 所有容器的日誌
)對 Pod 進行排序。如果
imagefs
觸發驅逐,則 kubelet 會根據所有容器的可寫入層使用量對 Pod 進行排序。
使用 imagesfs
和 containerfs
(imagefs
和 containerfs
已分割)
如果
containerfs
觸發驅逐,則 kubelet 會根據containerfs
使用量(本機磁碟區 + 日誌和所有容器的可寫入層
)對 Pod 進行排序。如果
imagefs
觸發驅逐,則 kubelet 會根據映像儲存
排名對 Pod 進行排序,該排名表示給定映像的磁碟使用量。
最小驅逐回收量
注意
從 Kubernetes v1.32 開始,您無法為containerfs.available
指標設定自訂值。此特定指標的組態將自動設定為反映為 nodefs
或 imagefs
設定的值,具體取決於組態。在某些情況下,Pod 驅逐僅回收少量資源不足的資源。這可能會導致 kubelet 反覆達到設定的驅逐閾值並觸發多次驅逐。
您可以使用 --eviction-minimum-reclaim
旗標或kubelet 組態檔來設定每個資源的最小回收量。當 kubelet 注意到資源不足時,它會繼續回收該資源,直到回收您指定的數量。
例如,下列組態設定了最小回收量
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
memory.available: "500Mi"
nodefs.available: "1Gi"
imagefs.available: "100Gi"
evictionMinimumReclaim:
memory.available: "0Mi"
nodefs.available: "500Mi"
imagefs.available: "2Gi"
在此範例中,如果 nodefs.available
訊號符合驅逐閾值,則 kubelet 會回收資源,直到訊號達到 1GiB 的閾值,然後繼續回收 500MiB 的最小量,直到可用的 nodefs 儲存值達到 1.5GiB。
同樣地,kubelet 會嘗試回收 imagefs
資源,直到 imagefs.available
值達到 102Gi
,代表 102 GiB 的可用容器映像儲存空間。如果 kubelet 可以回收的儲存空間量少於 2GiB,kubelet 則不會回收任何東西。
所有資源的預設 eviction-minimum-reclaim
皆為 0
。
節點記憶體不足行為
如果節點在 kubelet 能夠回收記憶體之前發生記憶體不足 (OOM) 事件,則節點會依賴 oom_killer 來回應。
kubelet 會根據 Pod 的服務品質 (QoS) 為每個容器設定 oom_score_adj
值。
服務品質 | oom_score_adj |
---|---|
Guaranteed | -997 |
BestEffort | 1000 |
Burstable | min(max(2, 1000 - (1000 × memoryRequestBytes) / machineMemoryCapacityBytes), 999) |
如果 kubelet 在節點經歷 OOM 之前無法回收記憶體,oom_killer
會根據容器在節點上使用的記憶體百分比計算 oom_score
,然後加上 oom_score_adj
以獲得每個容器的有效 oom_score
。然後,它會終止分數最高的容器。
這表示服務品質 (QoS) 較低的 Pod 中,相對於其排程請求消耗大量記憶體的容器會優先被終止。
與 Pod 驅逐不同,如果容器因 OOM 而終止,kubelet 可以根據其 restartPolicy
重新啟動它。
良好實務
以下章節描述驅逐配置的良好實務。
可排程資源和驅逐策略
當您使用驅逐策略配置 kubelet 時,您應確保排程器不會排程那些會立即引起記憶體壓力,進而觸發驅逐的 Pod。
考慮以下情境
- 節點記憶體容量:10GiB
- 運營商想要為系統常駐程式 (核心、
kubelet
等) 保留 10% 的記憶體容量 - 運營商想要在記憶體使用率達到 95% 時驅逐 Pod,以減少系統 OOM 事件的發生。
為了使此配置生效,kubelet 的啟動方式如下
--eviction-hard=memory.available<500Mi
--system-reserved=memory=1.5Gi
在此配置中,--system-reserved
標誌為系統保留 1.5GiB 的記憶體,即 總記憶體的 10% + 驅逐閾值量
。
如果 Pod 使用的記憶體超過其請求量,或者系統使用的記憶體超過 1GiB,節點可能會達到驅逐閾值,這會導致 memory.available
信號降至 500MiB 以下並觸發閾值。
DaemonSets 和節點壓力驅逐
Pod 優先順序是制定驅逐決策的主要因素。如果您不希望 kubelet 驅逐屬於 DaemonSet 的 Pod,請透過在 Pod 規格中指定合適的 priorityClassName
為這些 Pod 提供足夠高的優先順序。您也可以使用較低的優先順序或預設優先順序,以便僅在有足夠資源時才允許該 DaemonSet 的 Pod 執行。
已知問題
以下章節描述與資源不足處理相關的已知問題。
kubelet 可能無法立即觀察到記憶體壓力
預設情況下,kubelet 會定期輪詢 cAdvisor 以收集記憶體使用統計數據。如果記憶體使用量在該時間範圍內快速增加,kubelet 可能無法及時觀察到 MemoryPressure
,並且仍然會調用 OOM killer。
您可以使用 --kernel-memcg-notification
標誌在 kubelet 上啟用 memcg
通知 API,以便在閾值被跨越時立即收到通知。
如果您不追求極端的使用率,而是合理的過度承諾量度,那麼針對此問題一個可行的解決方案是使用 --kube-reserved
和 --system-reserved
標誌為系統分配記憶體。
active_file 記憶體不被視為可用記憶體
在 Linux 上,核心會將最近最少使用 (LRU) 列表上 active 的檔案支持記憶體的位元組數追蹤為 active_file
統計數據。kubelet 將 active_file
記憶體區域視為不可回收。對於大量使用區塊支持的本機儲存 (包括臨時本機儲存) 的工作負載,檔案和區塊資料的核心層級快取意味著許多最近存取的快取頁面很可能被計為 active_file
。如果 active LRU 列表上有足夠的這些核心區塊緩衝區,kubelet 很可能將此視為高資源使用率,並將節點標記為正在經歷記憶體壓力,從而觸發 Pod 驅逐。
如需更多詳細資訊,請參閱 https://github.com/kubernetes/kubernetes/issues/43916
您可以透過為可能執行密集 I/O 活動的容器設定相同的記憶體限制和記憶體請求來繞過該行為。您將需要估計或測量該容器的最佳記憶體限制值。
接下來
- 了解關於 API 啟動的驅逐
- 了解關於 Pod 優先順序和搶佔
- 了解關於 PodDisruptionBudgets
- 了解關於 服務品質 (QoS)
- 查看 驅逐 API