Kubernetes驅(qū)逐機(jī)制

什么是節(jié)點(diǎn)驅(qū)逐?

節(jié)點(diǎn)壓力驅(qū)逐是 kubelet 主動(dòng)終止 Pod 以回收節(jié)點(diǎn)上資源的過程。

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

在節(jié)點(diǎn)壓力驅(qū)逐期間,kubelet 將所選 Pod 的 PodPhase 設(shè)置為 Failed。這將終止 Pod。

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

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

說明:

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

兩種驅(qū)逐機(jī)制

目前kubernetes中存在兩種eviction機(jī)制,分別由kube-controller-managerkubelet實(shí)現(xiàn)。

kube-controller-manager驅(qū)逐機(jī)制

kube-controller-manager主要由多個(gè)控制器構(gòu)成,而eviction的功能主要由node controller這個(gè)控制器實(shí)現(xiàn)。該Eviction會(huì)周期性檢查所有節(jié)點(diǎn)狀態(tài),當(dāng)節(jié)點(diǎn)處于NotReady狀態(tài)超過一段時(shí)間后,驅(qū)逐該節(jié)點(diǎn)上所有pod。

kube-controller-manager提供了以下啟動(dòng)參數(shù)控制驅(qū)逐:

  • pod-eviction-timeout:即當(dāng)節(jié)點(diǎn)宕機(jī)該時(shí)間間隔后,開始驅(qū)逐機(jī)制,驅(qū)逐宕機(jī)節(jié)點(diǎn)上的Pod,默認(rèn)為5min。

  • node-eviction-rate:驅(qū)逐速率,即驅(qū)逐Node的速率,由令牌桶流控算法實(shí)現(xiàn),默認(rèn)為0.1,即每秒驅(qū)逐0.1個(gè)節(jié)點(diǎn),注意這里不是驅(qū)趕Pod的速率,而是驅(qū)逐節(jié)點(diǎn)的速率。相當(dāng)于每隔10s,清空一個(gè)節(jié)點(diǎn)。

  • secondary-node-eviction-rate:二級(jí)驅(qū)逐速率,當(dāng)集群中宕機(jī)節(jié)點(diǎn)過多時(shí),相應(yīng)的驅(qū)逐速率也降低,默認(rèn)為0.01。

  • unhealthy-zone-threshold:不健康z(mì)one閾值,會(huì)影響什么時(shí)候開啟二級(jí)驅(qū)逐速率,默認(rèn)為0.55,即當(dāng)該zone中節(jié)點(diǎn)宕機(jī)數(shù)目超過55%,而認(rèn)為該zone不健康。

  • large-cluster-size-threshold:大集群閾值,當(dāng)該zone的節(jié)點(diǎn)多余該閾值時(shí),則認(rèn)為該zone是一個(gè)大集群。大集群節(jié)點(diǎn)宕機(jī)數(shù)目超過55%時(shí),則將驅(qū)趕速率降為0.01,假如是小集群,則將速率直接降為0。

kubelet驅(qū)逐機(jī)制

如果節(jié)點(diǎn)處于資源壓力,那么kubelet就會(huì)執(zhí)行驅(qū)逐策略。驅(qū)逐會(huì)考慮Pod的優(yōu)先級(jí),資源使用和資源申請(qǐng)。當(dāng)優(yōu)先級(jí)相同時(shí),資源使用/資源申請(qǐng)最大的Pod會(huì)被首先驅(qū)逐。

kube-controller-manager的驅(qū)逐機(jī)制是粗粒度的,即驅(qū)趕一個(gè)節(jié)點(diǎn)上的所有pod,而kubelet則是細(xì)粒度的,它驅(qū)趕的是節(jié)點(diǎn)上的某些Pod,驅(qū)趕哪些Pod與Pod的Qos機(jī)制有關(guān)。該Eviction會(huì)周期性檢查本節(jié)點(diǎn)內(nèi)存、磁盤等資源,當(dāng)資源不足時(shí),按照優(yōu)先級(jí)驅(qū)逐部分pod。

驅(qū)逐閾值分為軟驅(qū)逐閾值(Soft Eviction Thresholds)和強(qiáng)制驅(qū)逐(Hard Eviction Thresholds)兩種機(jī)制,如下:

  • 軟驅(qū)逐閾值:當(dāng)node的內(nèi)存/磁盤空間達(dá)到一定的閾值后,kubelet不會(huì)馬上回收資源,如果改善到低于閾值就不進(jìn)行驅(qū)逐,若這段時(shí)間一直高于閾值就進(jìn)行驅(qū)逐。

  • 強(qiáng)制驅(qū)逐:強(qiáng)制驅(qū)逐機(jī)制則簡(jiǎn)單的多,一旦達(dá)到閾值,直接把pod從本地驅(qū)逐。

kubelet提供了以下參數(shù)控制eviction:

  • eviction-soft:軟驅(qū)逐閾值設(shè)置,具有一系列閾值,比如memory.available<1.5Gi時(shí),它不會(huì)立即執(zhí)行pod eviction,而會(huì)等待eviction-soft-grace-period時(shí)間,假如該時(shí)間過后,依然還是達(dá)到了eviction-soft,則觸發(fā)一次pod eviction。

  • eviction-soft-grace-period:默認(rèn)為90秒,當(dāng)eviction-soft時(shí),終止Pod的grace的時(shí)間,即軟驅(qū)逐寬限期,軟驅(qū)逐信號(hào)與驅(qū)逐處理之間的時(shí)間差。

  • eviction-max-pod-grace-period:最大驅(qū)逐pod寬限期,停止信號(hào)與kill之間的時(shí)間差。

  • eviction-pressure-transition-period:默認(rèn)為5分鐘,脫離pressure condition的時(shí)間,超過閾值時(shí),節(jié)點(diǎn)會(huì)被設(shè)置為memory pressure或者disk pressure,然后開啟pod eviction。

  • eviction-minimum-reclaim:表示每一次eviction必須至少回收多少資源。

  • eviction-hard:強(qiáng)制驅(qū)逐設(shè)置,也具有一系列的閾值,比如memory.available<1Gi,即當(dāng)節(jié)點(diǎn)可用內(nèi)存低于1Gi時(shí),會(huì)立即觸發(fā)一次pod eviction。

本節(jié)我們重點(diǎn)介紹kubelet的驅(qū)逐機(jī)制。

驅(qū)逐信號(hào)

驅(qū)逐信號(hào)是特定資源在特定時(shí)間點(diǎn)的當(dāng)前狀態(tài)。 kubelet 使用驅(qū)逐信號(hào),通過將信號(hào)與驅(qū)逐條件進(jìn)行比較來做出驅(qū)逐決定, 驅(qū)逐條件是節(jié)點(diǎn)上應(yīng)該可用資源的最小量。

kubelet 使用以下驅(qū)逐信號(hào):

驅(qū)逐信號(hào) Node Condition 描述
memory.available MemoryPressue memory.available := node.status.capacity[memory] - node.stats.memory.workingSet
nodefs.available DiskPressure nodefs.available := node.stats.fs.available
nodefs.inodesFree DiskPressure nodefs.inodesFree := node.stats.fs.inodesFree
imagefs.available DiskPressure imagefs.available := node.stats.runtime.imagefs.available
imagefs.inodesFree DiskPressure imagefs.inodesFree := node.stats.runtime.imagefs.inodesFree
pid.available PIDPressure pid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc

在上表中,描述列顯示了 kubelet 如何獲取信號(hào)的值。每個(gè)信號(hào)支持百分比值或者是字面值。 kubelet 計(jì)算相對(duì)于與信號(hào)有關(guān)的總量的百分比值。

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

  1. nodefs:節(jié)點(diǎn)的主要文件系統(tǒng),用于本地磁盤卷、emptyDir、日志存儲(chǔ)等。 例如,nodefs 包含 /var/lib/kubelet/。

  2. imagefs:可選文件系統(tǒng),供容器運(yùn)行時(shí)存儲(chǔ)容器鏡像和容器可寫層。

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

驅(qū)逐條件

你可以為 kubelet 指定自定義驅(qū)逐條件,以便在作出驅(qū)逐決定時(shí)使用。

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

  • eviction-signal 是要使用的驅(qū)逐信號(hào)。

  • operator 是你想要的關(guān)系運(yùn)算符, 比如 <(小于)。

  • quantity 是驅(qū)逐條件數(shù)量,例如 1Gi。 quantity 的值必須與 Kubernetes 使用的數(shù)量表示相匹配。 你可以使用文字值或百分比(%)。

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

默認(rèn)驅(qū)逐條件

下面是 kubelet 默認(rèn)的關(guān)于節(jié)點(diǎn)存儲(chǔ)的驅(qū)逐觸發(fā)條件:

nodefs.available<10%(容器 volume 使用的文件系統(tǒng)的可用空間,包括文件系統(tǒng)剩余大小和 inode 數(shù)量)

imagefs.available<15%(容器鏡像使用的文件系統(tǒng)的可用空間,包括文件系統(tǒng)剩余大小和 inode 數(shù)量)

當(dāng) imagefs 使用量達(dá)到閾值時(shí),kubelet 會(huì)嘗試刪除不使用的鏡像來清理磁盤空間。

// DefaultEvictionHard includes default options for hard eviction.
var DefaultEvictionHard = map[string]string{
    "memory.available":  "100Mi",
    "nodefs.available":  "10%",
    "nodefs.inodesFree": "5%",
    "imagefs.available": "15%",
}

驅(qū)逐類型

軟驅(qū)逐

軟驅(qū)逐條件

軟驅(qū)逐條件將驅(qū)逐條件與管理員所必須指定的寬限期配對(duì)。 在超過寬限期之前,kubelet 不會(huì)驅(qū)逐 Pod。 如果沒有指定的寬限期,kubelet 會(huì)在啟動(dòng)時(shí)返回錯(cuò)誤。

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

你可以使用以下標(biāo)志來配置軟驅(qū)逐條件:

  • eviction-soft:一組驅(qū)逐條件,如 memory.available<1.5Gi, 如果驅(qū)逐條件持續(xù)時(shí)長(zhǎng)超過指定的寬限期,可以觸發(fā) Pod 驅(qū)逐。

  • eviction-soft-grace-period:一組驅(qū)逐寬限期, 如 memory.available=1m30s,定義軟驅(qū)逐條件在觸發(fā) Pod 驅(qū)逐之前必須保持多長(zhǎng)時(shí)間。

  • eviction-max-pod-grace-period:在滿足軟驅(qū)逐條件而終止 Pod 時(shí)使用的最大允許寬限期(以秒為單位)。

硬驅(qū)逐

硬驅(qū)逐條件

硬驅(qū)逐條件沒有寬限期。當(dāng)達(dá)到硬驅(qū)逐條件時(shí), kubelet 會(huì)立即殺死 pod,而不會(huì)正常終止以回收緊缺的資源。

你可以使用 eviction-hard 標(biāo)志來配置一組硬驅(qū)逐條件, 例如 memory.available<1Gi。

kubelet 具有以下默認(rèn)硬驅(qū)逐條件:

  • memory.available<100Mi
  • nodefs.available<10%
  • imagefs.available<15%
  • nodefs.inodesFree<5%(Linux 節(jié)點(diǎn))

驅(qū)逐過程

驅(qū)逐監(jiān)測(cè)間隔

kubelet 根據(jù)其配置的 housekeeping-interval(默認(rèn)為 10s)評(píng)估驅(qū)逐條件。

回收節(jié)點(diǎn)資源

kubelet 在驅(qū)逐最終用戶 Pod 之前會(huì)先嘗試回收節(jié)點(diǎn)級(jí)資源。

當(dāng)報(bào)告 DiskPressure 節(jié)點(diǎn)狀況時(shí),kubelet 會(huì)根據(jù)節(jié)點(diǎn)上的文件系統(tǒng)回收節(jié)點(diǎn)級(jí)資源。

有 imagefs

如果節(jié)點(diǎn)有一個(gè)專用的 imagefs 文件系統(tǒng)供容器運(yùn)行時(shí)使用,kubelet 會(huì)執(zhí)行以下操作:

  • 如果 nodefs 文件系統(tǒng)滿足驅(qū)逐條件,kubelet 垃圾收集死亡 Pod 和容器。

  • 如果 imagefs 文件系統(tǒng)滿足驅(qū)逐條件,kubelet 將刪除所有未使用的鏡像。

沒有 imagefs

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

  1. 對(duì)死亡的 Pod 和容器進(jìn)行垃圾收集

  2. 刪除未使用的鏡像

垃圾回收參數(shù)

除了驅(qū)逐之外,Kubelet 還支持一系列的容器和鏡像垃圾回收選項(xiàng),它們未來將會(huì)被驅(qū)逐替代:

垃圾回收參數(shù) 驅(qū)逐參數(shù) 解釋
--image-gc-high-threshold --eviction-hard 或 --eviction-soft 現(xiàn)存的驅(qū)逐回收信號(hào)可以觸發(fā)鏡像垃圾回收
--image-gc-low-threshold --eviction-minimum-reclaim 驅(qū)逐回收實(shí)現(xiàn)相同行為
--minimum-image-ttl-duration 由于驅(qū)逐不包括TTL配置,所以它還會(huì)繼續(xù)支持
--maximum-dead-containers 一旦舊日志存儲(chǔ)在容器上下文之外,就會(huì)被棄用
--maximum-dead-containers-per-container 一旦舊日志存儲(chǔ)在容器上下文之外,就會(huì)被棄用
--minimum-container-ttl-duration 一旦舊日志存儲(chǔ)在容器上下文之外,就會(huì)被棄用
--low-diskspace-threshold-mb --eviction-hard or eviction-soft 驅(qū)逐回收將磁盤閾值泛化到其他資源
--outofdisk-transition-frequency --eviction-pressure-transition-period 驅(qū)逐回收將磁盤壓力轉(zhuǎn)換到其他資源

Pod驅(qū)逐順序

如果 kubelet 回收節(jié)點(diǎn)級(jí)資源的嘗試沒有使驅(qū)逐信號(hào)低于條件, 則 kubelet 開始驅(qū)逐最終用戶 Pod。

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

  1. Pod 的資源使用是否超過其請(qǐng)求

  2. Pod 優(yōu)先級(jí)

  3. Pod 相對(duì)于請(qǐng)求的資源使用情況

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

  1. 首先考慮資源使用量超過其請(qǐng)求的 BestEffort 或 Burstable Pod。 這些 Pod 會(huì)根據(jù)它們的優(yōu)先級(jí)以及它們的資源使用級(jí)別超過其請(qǐng)求的程度被逐出。

  2. 資源使用量少于請(qǐng)求量的 Guaranteed Pod 和 Burstable Pod 根據(jù)其優(yōu)先級(jí)被最后驅(qū)逐。

最小驅(qū)逐回收

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

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

例如,以下配置設(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"  # 最少回收 500Mi
  imagefs.available: "2Gi"   # 最少回收 2Gi

在這個(gè)例子中,如果 nodefs.available 信號(hào)滿足驅(qū)逐條件, kubelet 會(huì)回收資源,直到信號(hào)達(dá)到 1Gi 的條件, 然后繼續(xù)回收至少 500Mi 直到信號(hào)達(dá)到 1.5Gi。

類似地,kubelet 會(huì)回收 imagefs 資源,直到 imagefs.available 信號(hào)達(dá)到 102Gi。

對(duì)于所有資源,默認(rèn)的 eviction-minimum-reclaim 為 0。

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

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

kubelet 根據(jù) Pod 的服務(wù)質(zhì)量(QoS)為每個(gè)容器設(shè)置一個(gè) oom_score_adj 值。

服務(wù)質(zhì)量 oom_score_adj
Guaranteed -998
BestEffort 1000
Burstable min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

說明:

kubelet 還將具有 system-node-critical 優(yōu)先級(jí) 的 Pod 中的容器 oom_score_adj 值設(shè)為 -997。

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

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

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

參考文檔

Kubernetes中 Pod 是怎樣被驅(qū)逐的?

[Kubernetes] Eviction Manager工作原理分析

深入k8s:資源控制Qos和eviction及其源碼分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容