Kubernetes 節(jié)點壓力驅逐

2022-05-26 10:58 更新

節(jié)點壓力驅逐

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

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

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

節(jié)點壓力驅逐不同于 API 發(fā)起的驅逐。

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

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

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

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

  • 驅逐信號
  • 驅逐條件
  • 監(jiān)控間隔

驅逐信號

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

kubelet 使用以下驅逐信號:

驅逐信號 描述
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
pid.available pid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc

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

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

kubelet 支持以下文件系統(tǒng)分區(qū):

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

kubelet 會自動發(fā)現(xiàn)這些文件系統(tǒng)并忽略其他文件系統(tǒng)。kubelet 不支持其他配置。

Note:
一些 kubelet 垃圾收集功能已被棄用,以支持驅逐。

驅逐條件

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

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

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

例如,如果一個節(jié)點的總內(nèi)存為 10Gi 并且你希望在可用內(nèi)存低于 1Gi 時觸發(fā)驅逐, 則可以將驅逐條件定義為 ?memory.available<10%? 或 ?memory.available< 1G?。 你不能同時使用二者。

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

軟驅逐條件

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

你可以既指定軟驅逐條件寬限期,又指定 Pod 終止寬限期的上限,,給 kubelet 在驅逐期間使用。 如果你指定了寬限期的上限并且 Pod 滿足軟驅逐閾條件,則 kubelet 將使用兩個寬限期中的較小者。 如果你沒有指定寬限期上限,kubelet 會立即殺死被驅逐的 Pod,不允許其體面終止。

你可以使用以下標志來配置軟驅逐條件:

  • ?eviction-soft?:一組驅逐條件,如 ?memory.available<1.5Gi?, 如果驅逐條件持續(xù)時長超過指定的寬限期,可以觸發(fā) Pod 驅逐。
  • ?eviction-soft-grace-period?:一組驅逐寬限期, 如 ?memory.available=1m30s?,定義軟驅逐條件在觸發(fā) 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 節(jié)點)

驅逐監(jiān)測間隔

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

節(jié)點條件

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

kubelet 根據(jù)下表將驅逐信號映射為節(jié)點狀況:

節(jié)點條件 驅逐信號 描述
MemoryPressure memory.available 節(jié)點上的可用內(nèi)存已滿足驅逐條件
DiskPressure nodefs.availablenodefs.inodesFreeimagefs.available 或 imagefs.inodesFree 節(jié)點的根文件系統(tǒng)或映像文件系統(tǒng)上的可用磁盤空間和 inode 已滿足驅逐條件
PIDPressure pid.available (Linux) 節(jié)點上的可用進程標識符已低于驅逐條件

kubelet 根據(jù)配置的 ?--node-status-update-frequency? 更新節(jié)點條件,默認為 ?10s?。

節(jié)點條件振蕩

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

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

回收節(jié)點級資源

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

當報告 ?DiskPressure ?節(jié)點狀況時,kubelet 會根據(jù)節(jié)點上的文件系統(tǒng)回收節(jié)點級資源。

有 imagefs

如果節(jié)點有一個專用的 ?imagefs ?文件系統(tǒng)供容器運行時使用,kubelet 會執(zhí)行以下操作:

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

沒有 imagefs

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

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

kubelet 驅逐時 Pod 的選擇

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

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

  1. Pod 的資源使用是否超過其請求
  2. Pod 優(yōu)先級
  3. Pod 相對于請求的資源使用情況

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

  1. 首先考慮資源使用量超過其請求的 ?BestEffort ?或 ?Burstable ?Pod。 這些 Pod 會根據(jù)它們的優(yōu)先級以及它們的資源使用級別超過其請求的程度被逐出。
  2. 資源使用量少于請求量的 ?Guaranteed ?Pod 和 ?Burstable ?Pod 根據(jù)其優(yōu)先級被最后驅逐。
Note:
kubelet 不使用 Pod 的 QoS 類來確定驅逐順序。 在回收內(nèi)存等資源時,你可以使用 QoS 類來估計最可能的 Pod 驅逐順序。 QoS 不適用于臨時存儲(EphemeralStorage)請求, 因此如果節(jié)點在 ?DiskPressure ?下,則上述場景將不適用。

僅當 ?Guaranteed ?Pod 中所有容器都被指定了請求和限制并且二者相等時,才保證 Pod 不被驅逐。 這些 Pod 永遠不會因為另一個 Pod 的資源消耗而被驅逐。 如果系統(tǒng)守護進程(例如 ?kubelet ?和 ?journald?) 消耗的資源比通過 ?system-reserved? 或 ?kube-reserved? 分配保留的資源多, 并且該節(jié)點只有 ?Guaranteed ?或 ?Burstable ?Pod 使用的資源少于其上剩余的請求, 那么 kubelet 必須選擇驅逐這些 Pod 中的一個以保持節(jié)點穩(wěn)定性并減少資源匱乏對其他 Pod 的影響。 在這種情況下,它會選擇首先驅逐最低優(yōu)先級的 Pod。

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

kubelet 根據(jù)節(jié)點是否具有專用的 ?imagefs ?文件系統(tǒng)對 Pod 進行不同的排序:

有 imagefs

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

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

沒有 imagefs

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

最小驅逐回收 

在某些情況下,驅逐 Pod 只會回收少量的緊俏資源。 這可能導致 kubelet 反復達到配置的驅逐條件并觸發(fā)多次驅逐。

你可以使用 ?--eviction-minimum-reclaim? 標志或 kubelet 配置文件 為每個資源配置最小回收量。 當 kubelet 注意到某個資源耗盡時,它會繼續(xù)回收該資源,直到回收到你所指定的數(shù)量為止。

例如,以下配置設置最小回收量:

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 ?的條件, 然后繼續(xù)回收至少 ?500Mi? 直到信號達到 ?1.5Gi?。

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

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

節(jié)點內(nèi)存不足行為

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

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

服務質量 oom_score_adj
Guaranteed -997
BestEffort 1000
Burstable min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)
Note:
kubelet 還將具有 ?system-node-critical? 優(yōu)先級 的 Pod 中的容器 ?oom_score_adj? 值設為 ?-997?。

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

這意味著低 QoS Pod 中相對于其調度請求消耗內(nèi)存較多的容器,將首先被殺死。

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

最佳實踐

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

可調度的資源和驅逐策略

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

考慮以下場景:

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

為此,kubelet 啟動設置如下:

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

在此配置中,?--system-reserved? 標志為系統(tǒng)預留了 ?1.5Gi? 的內(nèi)存, 即 總內(nèi)存的 10% + 驅逐條件量。

如果 Pod 使用的內(nèi)存超過其請求值或者系統(tǒng)使用的內(nèi)存超過 ?1Gi?, 則節(jié)點可以達到驅逐條件,這使得 ?memory.available? 信號低于 ?500Mi? 并觸發(fā)條件。

DaemonSet

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

已知問題 

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

kubelet 可能不會立即觀察到內(nèi)存壓力

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

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

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

active_file 內(nèi)存未被視為可用內(nèi)存 

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

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

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


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號