什么是節(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-manager和kubelet實(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ū):
nodefs:節(jié)點(diǎn)的主要文件系統(tǒng),用于本地磁盤卷、emptyDir、日志存儲(chǔ)等。 例如,nodefs 包含 /var/lib/kubelet/。
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 按以下順序釋放磁盤空間:
對(duì)死亡的 Pod 和容器進(jìn)行垃圾收集
刪除未使用的鏡像
垃圾回收參數(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ū)逐順序:
Pod 的資源使用是否超過其請(qǐng)求
Pod 相對(duì)于請(qǐng)求的資源使用情況
因此,kubelet 按以下順序排列和驅(qū)逐 Pod:
首先考慮資源使用量超過其請(qǐng)求的 BestEffort 或 Burstable Pod。 這些 Pod 會(huì)根據(jù)它們的優(yōu)先級(jí)以及它們的資源使用級(jí)別超過其請(qǐng)求的程度被逐出。
資源使用量少于請(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)它。
參考文檔