【K8s概念】節點壓力驅逐


參考:https://kubernetes.io/zh/docs/concepts/scheduling-eviction/node-pressure-eviction/

節點壓力驅逐是 kubelet 主動終止 Pod 以回收節點上資源的過程。

kubelet 監控集群節點的 CPU、內存、磁盤空間和文件系統的 inode 等資源。 當這些資源中的一個或者多個達到特定的消耗水平, kubelet 可以主動地使節點上一個或者多個 Pod 失效,以回收資源防止飢餓。

在節點壓力驅逐期間,kubelet 將所選 Pod 的 PodPhase 設置為 Failed。這將終止 Pod。

節點壓力驅逐不同於 API 發起的驅逐。

kubelet 並不理會你配置的 PodDisruptionBudget 或者是 Pod 的 terminationGracePeriodSeconds。 如果你使用了軟驅逐條件,kubelet 會考慮你所配置的 eviction-max-pod-grace-period。 如果你使用了硬驅逐條件,它使用 0s 寬限期來終止 Pod。

如果 Pod 是由替換失敗 Pod 的工作負載資源 (例如 StatefulSet 或者 Deployment)管理, 則控制平面或 kube-controller-manager 會創建新的 Pod 來代替被驅逐的 Pod。

說明:kubelet 在終止最終用戶 Pod 之前會嘗試回收節點級資源。 例如,它會在磁盤資源不足時刪除未使用的容器鏡像。

kubelet 使用各種參數來做出驅逐決定,如下所示:

  • 驅逐信號
  • 驅逐條件
  • 監控間隔

驅逐信號

驅逐信號是特定資源在特定時間點的當前狀態。 kubelet 使用驅逐信號,通過將信號與驅逐條件進行比較來做出驅逐決定, 驅逐條件是節點上應該可用資源的最小量。

kubelet 使用以下驅逐信號:

在上表中,描述列顯示了 kubelet 如何獲取信號的值。每個信號支持百分比值或者是字面值。 kubelet 計算相對於與信號有關的總量的百分比值。

memory.available 的值來自 cgroupfs,而不是像 free -m 這樣的工具。 這很重要,因為 free -m 在容器中不起作用,如果用戶使用 節點可分配資源 這一功能特性,資源不足的判定是基於 CGroup 層次結構中的用戶 Pod 所處的局部及 CGroup 根節點作出的。 這個腳本 重現了 kubelet 為計算 memory.available 而執行的相同步驟。 kubelet 在其計算中排除了 inactive_file(即非活動 LRU 列表上基於文件來虛擬的內存的字節數), 因為它假定在壓力下內存是可回收的。

kubelet 支持以下文件系統分區:

1.nodefs:節點的主要文件系統,用於本地磁盤卷、emptyDir、日志存儲等。 例如,nodefs 包含 /var/lib/kubelet/。
2.imagefs:可選文件系統,供容器運行時存儲容器鏡像和容器可寫層。

kubelet 會自動發現這些文件系統並忽略其他文件系統。kubelet 不支持其他配置。

驅逐條件

你可以為 kubelet 指定自定義驅逐條件,以便在作出驅逐決定時使用。

驅逐條件的形式為 [eviction-signal][operator][quantity],其中:

  • eviction-signal 是要使用的驅逐信號。
  • operator 是你想要的關系運算符, 比如 <(小於)。
  • quantity 是驅逐條件數量,例如 1Gi。 quantity 的值必須與 Kubernetes 使用的數量表示相匹配。 你可以使用文字值或百分比(%)。

例如,如果一個節點的總內存為 10Gi 並且你希望在可用內存低於 1Gi 時觸發驅逐, 則可以將驅逐條件定義為 memory.available<10% 或 memory.available< 1G。 你不能同時使用二者。

你可以配置軟和硬驅逐條件。

軟驅逐條件

軟驅逐條件將驅逐條件與管理員所必須指定的寬限期配對。 在超過寬限期之前,kubelet 不會驅逐 Pod。 如果沒有指定的寬限期,kubelet 會在啟動時返回錯誤。

你可以既指定軟驅逐條件寬限期,又指定 Pod 終止寬限期的上限,,給 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
nodefs.available<10%
imagefs.available<15%
nodefs.inodesFree<5%(Linux 節點)

驅逐監測間隔

kubelet 根據其配置的 housekeeping-interval(默認為 10s)評估驅逐條件。

節點條件

kubelet 報告節點狀況以反映節點處於壓力之下,因為滿足硬或軟驅逐條件,與配置的寬限期無關。

kubelet 根據下表將驅逐信號映射為節點狀況:

kubelet 根據配置的 --node-status-update-frequency 更新節點條件,默認為 10s。

節點條件振盪

在某些情況下,節點在軟驅逐條件上下振盪,而沒有保持定義的寬限期。 這會導致報告的節點條件在 true 和 false 之間不斷切換,從而導致錯誤的驅逐決策。

為了防止振盪,你可以使用 eviction-pressure-transition-period 標志, 該標志控制 kubelet 在將節點條件轉換為不同狀態之前必須等待的時間。 過渡期的默認值為 5m。

回收節點級資源

kubelet 在驅逐最終用戶 Pod 之前會先嘗試回收節點級資源。

當報告 DiskPressure 節點狀況時,kubelet 會根據節點上的文件系統回收節點級資源。

有 imagefs

如果節點有一個專用的 imagefs 文件系統供容器運行時使用,kubelet 會執行以下操作:

  • 如果 nodefs 文件系統滿足驅逐條件,kubelet 垃圾收集死亡 Pod 和容器。
  • 如果 imagefs 文件系統滿足驅逐條件,kubelet 將刪除所有未使用的鏡像。

沒有 imagefs

如果節點只有一個滿足驅逐條件的 nodefs 文件系統, kubelet 按以下順序釋放磁盤空間:

1.對死亡的 Pod 和容器進行垃圾收集
2.刪除未使用的鏡像

kubelet 驅逐時 Pod 的選擇

如果 kubelet 回收節點級資源的嘗試沒有使驅逐信號低於條件, 則 kubelet 開始驅逐最終用戶 Pod。

kubelet 使用以下參數來確定 Pod 驅逐順序:

1.Pod 的資源使用是否超過其請求
2.Pod 優先級
3.Pod 相對於請求的資源使用情況

因此,kubelet 按以下順序排列和驅逐 Pod:

1.首先考慮資源使用量超過其請求的 BestEffort 或 Burstable Pod。 這些 Pod 會根據它們的優先級以及它們的資源使用級別超過其請求的程度被逐出。
2.資源使用量少於請求量的 Guaranteed Pod 和 Burstable Pod 根據其優先級被最后驅逐。

當 kubelet 因 inode 或 PID 不足而驅逐 pod 時, 它使用優先級來確定驅逐順序,因為 inode 和 PID 沒有請求。

kubelet 根據節點是否具有專用的 imagefs 文件系統對 Pod 進行不同的排序:

有 imagefs

如果 nodefs 觸發驅逐, kubelet 會根據 nodefs 使用情況(本地卷 + 所有容器的日志)對 Pod 進行排序。

如果 imagefs 觸發驅逐,kubelet 會根據所有容器的可寫層使用情況對 Pod 進行排序。

沒有 imagefs

如果 nodefs 觸發驅逐, kubelet 會根據磁盤總用量(本地卷 + 日志和所有容器的可寫層)對 Pod 進行排序。

最小驅逐回收

在某些情況下,驅逐 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 會回收資源,直到信號達到 1Gi 的條件, 然后繼續回收至少 500Mi 直到信號達到 1.5Gi。

類似地,kubelet 會回收 imagefs 資源,直到 imagefs.available 信號達到 102Gi。

對於所有資源,默認的 eviction-minimum-reclaim 為 0。

節點內存不足行為

如果節點在 kubelet 能夠回收內存之前遇到內存不足(OOM)事件, 則節點依賴 oom_killer 來響應。

kubelet 根據 Pod 的服務質量(QoS)為每個容器設置一個 oom_score_adj 值。

說明:kubelet 還將具有 system-node-critical 優先級 的 Pod 中的容器 oom_score_adj 值設為 -997。

如果 kubelet 在節點遇到 OOM 之前無法回收內存, 則 oom_killer 根據它在節點上使用的內存百分比計算 oom_score, 然后加上 oom_score_adj 得到每個容器有效的 oom_score。 然后它會殺死得分最高的容器。

這意味着低 QoS Pod 中相對於其調度請求消耗內存較多的容器,將首先被殺死。

與 Pod 驅逐不同,如果容器被 OOM 殺死, kubelet 可以根據其 RestartPolicy 重新啟動它。

最佳實踐

以下部分描述了驅逐配置的最佳實踐。

可調度的資源和驅逐策略

當你為 kubelet 配置驅逐策略時, 你應該確保調度程序不會在 Pod 觸發驅逐時對其進行調度,因為這類 Pod 會立即引起內存壓力。

考慮以下場景:

  • 節點內存容量:10Gi
  • 操作員希望為系統守護進程(內核、kubelet 等)保留 10% 的內存容量
  • 操作員希望驅逐內存利用率為 95% 的Pod,以減少系統 OOM 的概率。

為此,kubelet 啟動設置如下:

--eviction-hard=memory.available<500Mi
--system-reserved=memory=1.5Gi

在此配置中,--system-reserved 標志為系統預留了 1.5Gi 的內存, 即 總內存的 10% + 驅逐條件量。

如果 Pod 使用的內存超過其請求值或者系統使用的內存超過 1Gi, 則節點可以達到驅逐條件,這使得 memory.available 信號低於 500Mi 並觸發條件。

DaemonSet

Pod 優先級是做出驅逐決定的主要因素。 如果你不希望 kubelet 驅逐屬於 DaemonSet 的 Pod, 請在 Pod 規約中為這些 Pod 提供足夠高的 priorityClass。 你還可以使用優先級較低的 priorityClass 或默認配置, 僅在有足夠資源時才運行 DaemonSet Pod。

已知問題

以下部分描述了與資源不足處理相關的已知問題。

kubelet 可能不會立即觀察到內存壓力

默認情況下,kubelet 輪詢 cAdvisor 以定期收集內存使用情況統計信息。 如果該輪詢時間窗口內內存使用量迅速增加,kubelet 可能無法足夠快地觀察到 MemoryPressure, 但是 OOMKiller 仍將被調用。

你可以使用 --kernel-memcg-notification 標志在 kubelet 上啟用 memcg 通知 API,以便在超過條件時立即收到通知。

如果你不是追求極端利用率,而是要采取合理的過量使用措施, 則解決此問題的可行方法是使用 --kube-reserved 和 --system-reserved 標志為系統分配內存。

active_file 內存未被視為可用內存

在 Linux 上,內核跟蹤活動 LRU 列表上的基於文件所虛擬的內存字節數作為 active_file 統計信息。 kubelet 將 active_file 內存區域視為不可回收。 對於大量使用塊設備形式的本地存儲(包括臨時本地存儲)的工作負載, 文件和塊數據的內核級緩存意味着許多最近訪問的緩存頁面可能被計為 active_file。 如果這些內核塊緩沖區中在活動 LRU 列表上有足夠多, kubelet 很容易將其視為資源用量過量並為節點設置內存壓力污點,從而觸發 Pod 驅逐。

更多細節請參見 https://github.com/kubernetes/kubernetes/issues/43916

你可以通過為可能執行 I/O 密集型活動的容器設置相同的內存限制和內存請求來應對該行為。 你將需要估計或測量該容器的最佳內存限制值。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM