k8s節點資源預留與 pod 驅逐

爲何

K8S 的節點上的資源會被 pod 和系統進程所使用,若是默認什麼都不配置,那麼節點上的所有資源都是能夠分配給pod使用的,系統進程自己沒有保障,這樣作很危險:node

  • 集羣雪崩:若是節點上調度了大量pod,且pod沒有合理的limit限制,節點資源將被耗盡,sshd、kubelet等進程OOM,節點變成 not ready狀態,pod從新繼續調度到其餘節點,新節點也被打掛,引發集羣雪崩。
  • 系統進程異常:就算 pod 設置了limit,但若是機器遇到資源不足,系統進程如 docker 沒有資源保障,會頻繁 OOM,或者進程 hang 住無響應,雖然能運行,但容器會反覆出問題

節點資源主要分爲兩類:git

  • 可壓縮資源:如CPU,即便cpu 超配,也能夠劃分時間片運行,只是運行變慢,進程不會掛。
  • 不可壓縮資源:Memory/Storage,內存不一樣於CPU,系統內存不足時,會觸發 OOM殺死進程,按照oom score 來肯定先kill誰,oom_score_adj值越高,被kill 的優先級越高。

oom 分數:
imagegithub

因此,OOM 的優先級以下:docker

BestEffort Pod > Burstable Pod > 其它進程 > Guaranteed Pod > kubelet/docker 等 > sshd 等進程

所以須要對節點的內存等資源進行配置,以保證節點核心進程運行正常。bash

怎麼作

節點資源的配置通常分爲 2 種:服務器

  1. 資源預留:爲系統進程和 k8s 進程預留資源
  2. pod 驅逐:節點資源到達必定使用量,開始驅逐 pod

image

  • Node Capacity:Node的全部硬件資源
  • kube-reserved:給kube組件預留的資源:kubelet,kube-proxy以及docker等
  • system-reserved:給system進程預留的資源
  • eviction-threshold:kubelet eviction的閾值設定
  • Allocatable:真正scheduler調度Pod時的參考值(保證Node上全部Pods的request resource不超過Allocatable)

allocatable的值即對應 describe node 時看到的allocatable容量,pod 調度的上限ssh

計算公式:節點上可配置值 = 總量 - 預留值 - 驅逐閾值

Allocatable = Capacity - Reserved(kube+system) - Eviction Threshold

以上配置均在kubelet 中添加,涉及的參數有:google

--enforce-node-allocatable=pods,kube-reserved,system-reserved
--kube-reserved-cgroup=/system.slice/kubelet.service
--system-reserved-cgroup=/system.slice
--kube-reserved=cpu=200m,memory=250Mi
--system-reserved=cpu=200m,memory=250Mi
--eviction-hard=memory.available<5%,nodefs.available<10%,imagefs.available<10%
--eviction-soft=memory.available<10%,nodefs.available<15%,imagefs.available<15%
--eviction-soft-grace-period=memory.available=2m,nodefs.available=2m,imagefs.available=2m
--eviction-max-pod-grace-period=30
--eviction-minimum-reclaim=memory.available=0Mi,nodefs.available=500Mi,imagefs.available=500Mi

配置含義

配置的含義以下:spa

(1)--enforce-node-allocatable3d

含義:指定kubelet爲哪些進程作硬限制,可選的值有:

* pods
* kube-reserved
* system-reserve

這個參數開啓並指定pods後kubelet會爲全部pod的總cgroup作資源限制(經過cgroup中的kubepods.limit_in_bytes),限制爲公式計算出的allocatable的大小。

假如想爲系統進程和k8s進程也作cgroup級別的硬限制,還能夠在限制列表中再加system-reserved和kube-reserved,同時還要分別加上--kube-reserved-cgroup和--system-reserved-cgroup以指定分別限制在哪一個cgroup裏。
配置:--enforce-node-allocatable=pods,kube-reserved,system-reserved

(2)設置k8s組件的cgroup

含義:這個參數用來指定k8s系統組件所使用的cgroup。

注意,這裏指定的cgroup及其子系統須要預先建立好,kubelet並不會爲你自動建立好。
配置:--kube-reserved-cgroup=/system.slice/kubelet.service

(3)設置系統守護進程的cgroup

含義:這個參數用來指定系統守護進程所使用的cgroup。

注意,這裏指定的cgroup及其子系統須要預先建立好,kubelet並不會爲你自動建立好。
配置:--system-reserved-cgroup=/system.slice

(4)配置 k8s組件預留資源的大小,CPU、Mem

指定爲k8s系統組件(kubelet、kube-proxy、dockerd等)預留的資源量,

如:--kube-reserved=cpu=1,memory=2Gi,ephemeral-storage=1Gi。

這裏的kube-reserved只爲非pod形式啓動的kube組件預留資源,假如組件要是以static pod(kubeadm)形式啓動的,那並不在這個kube-reserved管理並限制的cgroup中,而是在kubepod這個cgroup中。

(ephemeral storage須要kubelet開啓feature-gates,預留的是臨時存儲空間(log,EmptyDir),生產環境建議先不使用)

ephemeral-storage是kubernetes1.8開始引入的一個資源限制的對象,kubernetes 1.10版本中kubelet默認已經打開的了,到目前1.11仍是beta階段,主要是用於對本地臨時存儲使用空間大小的限制,如對pod的empty dir、/var/lib/kubelet、日誌、容器可讀寫層的使用大小的限制。

(5)配置 系統守護進程預留資源的大小(預留的值須要根據機器上容器的密度作一個合理的值)

含義:爲系統守護進程(sshd, udev等)預留的資源量,

如:--system-reserved=cpu=500m,memory=1Gi,ephemeral-storage=1Gi。

注意,除了考慮爲系統進程預留的量以外,還應該爲kernel和用戶登陸會話預留一些內存。
配置:--system-reserved=cpu=200m,memory=250Mi

(6)配置 驅逐pod的硬閾值

含義:設置進行pod驅逐的閾值,這個參數只支持內存和磁盤。

經過--eviction-hard標誌預留一些內存後,當節點上的可用內存降至保留值如下時,

kubelet 將會對pod進行驅逐。
配置:--eviction-hard=memory.available<5%,nodefs.available<10%,imagefs.available<10%

(7)配置 驅逐pod的軟閾值

--eviction-soft=memory.available<10%,nodefs.available<15%,imagefs.available<15%

(8)定義達到軟閾值以後,持續時間超過多久才進行驅逐

--eviction-soft-grace-period=memory.available=2m,nodefs.available=2m,imagefs.available=2m

(9)驅逐pod前最大等待時間=min(pod.Spec.TerminationGracePeriodSeconds, eviction-max-pod-grace-period),單位爲秒

--eviction-max-pod-grace-period=30

(10)至少回收的資源量

--eviction-minimum-reclaim=memory.available=0Mi,nodefs.available=500Mi,imagefs.available=500Mi

以上配置均爲百分比,舉例:

以2核4GB內存40GB磁盤空間的配置爲例,Allocatable是1.6 CPU,3.3Gi 內存,25Gi磁盤。當pod的總內存消耗大於3.3Gi或者磁盤消耗大於25Gi時,會根據相應策略驅逐pod。

硬驅逐與軟驅逐

硬驅逐

kubelet 利用metric的值做爲決策依據來觸發驅逐行爲,下面內容來自於 Kubelet summary API。

一旦超出閾值,就會觸發 kubelet 進行資源回收的動做(區別於軟驅逐,有寬限期),指標以下:

image

  • nodefs: 機器文件系統
  • imagesfs: Kubelet 可以利用 cAdvisor 自動發現這些文件系統,鏡像存儲空間

例如若是一個 Node 有 10Gi 內存,咱們但願在可用內存不足 1Gi 時進行驅逐,就能夠選取下面的一種方式來定義驅逐閾值:

  • memory.available<10%
  • memory.available<1Gi

能夠配置百分比或者實際值,可是操做符只能使用小於號,即<

軟驅逐

軟閾值須要和一個寬限期參數協同工做。當系統資源消耗達到軟閾值時,這一情況的持續時間超過了寬限期以前,Kubelet 不會觸發任何動做。若是沒有定義寬限期,Kubelet 會拒絕啓動。

另外還能夠定義一個 Pod 結束的寬限期。若是定義了這一寬限期,那麼 Kubelet 會使用 pod.Spec.TerminationGracePeriodSeconds 和最大寬限期這兩個值之間較小的那個(進行寬限),若是沒有指定的話,kubelet 會不留寬限當即殺死 Pod。

軟閾值的定義包括如下幾個參數:

  • eviction-soft:驅逐閾值,例如 memory.available<1.5Gi,若是知足這一條件的持續時間超過寬限期,就會觸發對 Pod 的驅逐動做。
  • eviction-soft-grace-period:驅逐寬限期,例如 memory.available=1m30s,用於定義達到軟閾值以後,持續時間超過多久才進行驅逐。
  • eviction-max-pod-grace-period:達到軟閾值以後,到驅逐一個 Pod 以前的最大寬限時間(單位是秒)

判斷週期

Housekeeping interval 參數定義一個時間間隔,Kubelet 每隔這一段就會對驅逐閾值進行評估。

  • housekeeping-interval:容器檢查的時間間隔。

節點表現

若是觸發了硬閾值,或者符合軟閾值的時間持續了與其對應的寬限期,Kubelet 就會認爲當前節點壓力太大,下面的節點狀態定義描述了這種對應關係。

image

Kubelet 會持續報告節點狀態的更新過程,這一頻率由參數 —node-status-update-frequency 指定,缺省狀況下取值爲 10s。

若是一個節點的情況在軟閾值的上下波動,可是又不會超過他的寬限期,將會致使該節點的狀態持續的在是否之間徘徊,最終會影響下降調度的決策過程。

要防止這種情況,下面的標誌能夠用來通知 Kubelet,在脫離pressure以前,必須等待。

eviction-pressure-transition-period 定義了在脫離pressure狀態以前要等待的時間

Kubelet 在把pressure狀態設置爲 False 以前,會確認在週期以內,該節點沒有達到閾值


若是達到了驅逐閾值,而且超出了寬限期,那麼 Kubelet 會開始回收超出限量的資源,直到回到閾值之內。

Kubelet 在驅逐用戶 Pod 以前,會嘗試回收節點級別的資源。若是服務器爲容器定義了獨立的 imagefs,他的回收過程會有所不一樣。

有 Imagefs

若是 nodefs 文件系統到達了驅逐閾值,kubelet 會按照下面的順序來清理空間:

  • 1.刪除死掉的 Pod/容器

若是 imagefs 文件系統到達了驅逐閾值,kubelet 會按照下面的順序來清理空間:

  • 1.刪掉全部無用鏡像

沒有 Imagefs

若是 nodefs 文件系統到達了驅逐閾值,kubelet 會按照下面的順序來清理空間。

  1. 刪除死掉的 Pod/容器
  2. 刪掉全部無用鏡像

pod驅逐策略

Kubelet 會按照下面的標準對 Pod 的驅逐行爲進行評判:

  • 根據服務質量:即BestEffort、Burstable、Guaranteed
  • 根據 Pod 調度請求的被耗盡資源的消耗量

接下來,Pod 按照下面的順序進行驅逐(QOS):

  1. BestEffort:消耗最多緊缺資源的 Pod 最早驅逐。
  2. Burstable:請求(request)最多緊缺資源的 Pod 被驅逐,若是沒有 Pod 超出他們的請求,會驅逐資源消耗量最大的 Pod。
  3. Guaranteed:請求(request)最多緊缺資源的 Pod 被驅逐,若是沒有 Pod 超出他們的請求,會驅逐資源消耗量最大的 Pod。

參考 POD的QOS:服務質量等級

Guaranteed Pod 不會由於其餘 Pod 的資源被驅逐。若是系統進程(例如 kubelet、docker、journald 等)消耗了超出 system-reserved 或者 kube-reserved 的資源,並且這一節點上只運行了 Guaranteed Pod,那麼爲了保證節點的穩定性並下降異常請求對其餘 Guaranteed Pod 的影響,必須選擇一個 Guaranteed Pod 進行驅逐。

本地磁盤是一個 BestEffort 資源。若有必要,kubelet 會在 DiskPressure 的狀況下,kubelet 會按照 QoS 進行評估。若是 Kubelet 斷定缺少 inode 資源,就會經過驅逐最低 QoS 的 Pod 的方式來回收 inodes。若是 kubelet 斷定缺少磁盤空間,就會經過在相同 QoS 的 Pods 中,選擇消耗最多磁盤空間的 Pod 進行驅逐。


有 Imagefs

  • 若是 nodefs 觸發了驅逐,Kubelet 會用 nodefs 的使用對 Pod 進行排序 – Pod 中全部容器的本地卷和日誌。
  • 若是 imagefs 觸發了驅逐,Kubelet 會根據 Pod 中全部容器的消耗的可寫入層進行排序。

沒有 Imagefs

  • 若是 nodefs 觸發了驅逐,Kubelet 會對各個 Pod 的全部容器的整體磁盤消耗進行排序 —— 本地卷 + 日誌 + 寫入層。
  • 在某些場景下,驅逐 Pod 可能只回收了不多的資源。這就致使了 kubelet 反覆觸發驅逐閾值。另外回收資源例如磁盤資源,是須要消耗時間的。
  • 要緩和這種情況,Kubelet 可以對每種資源定義 minimum-reclaim。kubelet 一旦發現了資源壓力,就會試着回收至少 minimum-reclaim 的資源,使得資源消耗量回到指望範圍。

例以下面的配置:

--eviction-hard=memory.available<500Mi,nodefs.available<1Gi,imagefs.available<100Gi

--eviction-minimum-reclaim="memory.available=0Mi,nodefs.available=500Mi,imagefs.available=2Gi"
  • 若是 memory.available 被觸發,Kubelet 會啓動回收,讓 memory.available 至少有 500Mi。
  • 若是是 nodefs.available,Kubelet 就要想法子讓 nodefs.available 回到至少 1.5Gi。
  • 而對於 imagefs.available, kubelet 就要回收到最少 102Gi。

缺省狀況下,全部資源的 eviction-minimum-reclaim 爲 0。

在節點資源緊缺的狀況下,調度器將再也不繼續向此節點部署新的 Pod

節點 OOM 時

若是節點在 Kubelet 可以回收內存以前,遭遇到了系統的 OOM (內存不足),節點就依賴 oom_killer 進行響應了。

kubelet 根據 Pod 的 QoS 爲每一個容器設置了一個 oom_score_adj 值。

image

若是 kubelet 沒法在系統 OOM 以前回收足夠的內存,oom_killer 就會根據根據內存使用比率來計算 oom_score,得出結果和 oom_score_adj 相加,最後得分最高的 Pod 會被首先驅逐。

跟 Pod 驅逐不一樣,若是一個 Pod 的容器被 OOM 殺掉,他是可能被 kubelet 根據 RestartPolicy 重啓的。

Daemonset 的處理

由於 DaemonSet 中的 Pod 會當即重建到同一個節點,因此 Kubelet 不該驅逐 DaemonSet 中的 Pod。

可是目前 Kubelet 沒法分辨一個 Pod 是否由 DaemonSet 建立。若是Kubelet 可以識別這一點,那麼就能夠先從驅逐候選列表中過濾掉 DaemonSet 的 Pod。

通常來講,強烈建議 DaemonSet 不要建立 BestEffort Pod,而是使用 Guaranteed Pod,來避免進入驅逐候選列表。

已知問題

Kubelet 沒法及時監測到內存壓力

Kubelet 目前從 cAdvisor 定時獲取內存使用情況統計。若是內存使用在這個時間段內發生了快速增加,Kubelet 就沒法觀察到 MemoryPressure,可能會觸發 OOMKiller。咱們正在嘗試將這一過程集成到 memcg 通知 API 中,來下降這一延遲,而不是讓內核首先發現這一狀況。

若是用戶不是但願得到終極使用率,而是做爲一個過量使用的衡量方式,對付這一個問題的較爲可靠的方式就是設置驅逐閾值爲 75% 容量。這樣就提升了避開 OOM 的能力,提升了驅逐的標準,有助於集羣狀態的平衡。

Kubelet 可能驅逐超出須要的更多 Pod

這也是由於狀態蒐集的時間差致使的。將來會加入功能,讓根容器的統計頻率和其餘容器分別開來(https://github.com/google/cad...)。

Kubelet 如何在 inode 耗盡的時候評價 Pod 的驅逐

目前不可能知道一個容器消耗了多少 inode。若是 Kubelet 覺察到了 inode 耗盡,他會利用 QoS 對 Pod 進行驅逐評估。在 cadvisor 中有一個 issue,來跟蹤容器的 inode 消耗,這樣咱們就能利用 inode 進行評估了。例如若是咱們知道一個容器建立了大量的 0 字節文件,就會優先驅逐這一 Pod

最佳實踐

資源預留

一、資源預留須要設置,pod 的 limit 也要設置。
二、cpu是可壓縮資源,內存、磁盤資源是不可壓縮資源。內存必定要預留,CPU能夠根據實際狀況來調整
三、預留多少合適:根據集羣規模設置階梯,以下(GKE建議):

Allocatable = Capacity - Reserved - Eviction Threshold

對於內存資源:

  • 內存少於1GB,則設置255 MiB
  • 內存大於4G,設置前4GB內存的25%
  • 接下來4GB內存的20%(最多8GB)
  • 接下來8GB內存的10%(最多16GB)
  • 接下來112GB內存的6%(最高128GB)
  • 超過128GB的任何內存的2%
  • 在1.12.0以前的版本中,內存小於1GB的節點不須要保留內存

對於 CPU 資源:

  • 第一個核的6%
  • 下一個核的1%(最多2個核)
  • 接下來2個核的0.5%(最多4個核)
  • 4個核以上的都是總數的0.25%

對於磁盤資源(不是正式特性,僅供參考):

image

效果:查看節點的可分配資源:

kubectl describe node [NODE_NAME] | grep Allocatable -B 4 -A 3

驅逐配置

--eviction-hard=memory.available<5%,nodefs.available<10%,imagefs.available<10%

--eviction-soft=memory.available<10%,nodefs.available<15%,imagefs.available<15%

--eviction-soft-grace-period=memory.available=2m,nodefs.available=2m,imagefs.available=2m

--eviction-max-pod-grace-period=30

--eviction-minimum-reclaim=memory.available=0Mi,nodefs.available=500Mi,imagefs.available=500Mi

原文連接:http://www.xuyasong.com/?p=1725

Reference

相關文章
相關標籤/搜索