1. 程式人生 > >Kubernetes Eviction Manager工作機制分析

Kubernetes Eviction Manager工作機制分析

摘要:為了極限的壓榨資源,很多時候Kubernetes叢集會執行一些Best-Effort Task,這樣就會存在資源超配的情況,Kubernetes是如何控制Node上資源的使用,在壓榨資源使用的同時又能保證Node的穩定性?本文就為你介紹其背後執行機制。我的下一篇博文,會對Kubelet Eviction Manager進行原始碼分析,感興趣的同學可以關注。

研究過Kubernetes Resource QoS的同學,肯定會有一個疑問:QoS中會通過Pod QoS和OOM Killer進行資源的回收,當發生資源緊缺的時候。那為什麼Kubernetes會再搞一個Kubelet Eviction機制,來做幾乎同樣的事呢?

首先,我們來談一下kubelet通過OOM Killer來回收資源的缺點:

  • System OOM events本來就是對資源敏感的,它會stall這個Node直到完成了OOM Killing Process。
  • 當OOM Killer幹掉某些containers之後,kubernetes Scheduler可能很快又會排程一個新的Pod到該Node上或者container 直接在node上restart,馬上又會觸發該Node上的OOM Killer啟動OOM Killing Process,事情可能會沒完沒了的進行,這可不妙啊。

我們再來看看Kubelet Eviction有何不同:

  • Kubelet通過pro-actively監控並阻止Node上資源的耗盡,一旦觸發Eviction Signals,就會直接Fail一個或者多個Pod以回收資源,而不是通過Linux OOM Killer這樣本身耗資源的元件進行回收。
  • 這樣的Eviction Signals的可配置的,可以做到Pro-actively。
  • 另外,被Evicted Pods會在其他Node上重新排程,而不會再次觸發本Node上的再次Eviction。

下面,我們具體來研究一下Kubelet Eviction Policy的工作機制。

  • kubelet預先監控本節點的資源使用,並且阻止資源被耗盡,這樣保證node的穩定性。
  • kubelet會預先Fail N(>= 1)個Pod以回收出現緊缺的資源。
  • kubelet會Fail一個Node時,會將Pod內所有Containners都kill掉,並把PodPhase設為Failed。
  • kubelet通過事先人為設定Eviction Thresholds來觸發Eviction動作以回收資源。

Eviction Signals

支援如下Eviction Signals:

Eviction Signal Description
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

- kubelet目前支援一下兩種filesystem,其中imagefs為可選的。Kubelet通過cAdvisor來自動發現這些filesystem。
- nodefs - Kubelet用來儲存volume,logs等資料。
- imagefs - 容器執行時(dockerd/rkt等)用來存放映象和容器的Writable Layer。

Eviction Thresholds

前面也提到,kubelet通過事先人為設定Eviction Thresholds來觸發Eviction動作以回收資源。

  • Eviction Thresholds的形式為:<eviction-signal><operator><quantity>
  • quantity支援絕對值和相對百分比兩種形式,比如:
    • memory.available<10%
    • memory.available<1Gi

Soft Eviction Thresholds

Soft Eviction Thresholds是什麼意思?
它指的是,當Eviction Signal中值達到Soft Eviction Thresholds配置的值時,並不會馬上觸發Kubelet去Evict Pods,而是會等待一個使用者配置的grace period之後,再觸發。相關的配置有三個,如下:

  • eviction-soft - (e.g. memory.available<1.5Gi) 觸發Soft Eviction的Evication Signal閾值。
  • eviction-soft-grace-period - (e.g. memory.available=1m30s) 當Eviction Signal的值達到配置eviction-soft值後,需要等待grace period,注意這期間,每10s會重新獲取監控資料並維護Threshold的值。如果grace period最後一次監控資料仍然觸發了閾值,才會再觸發Evict Pods。這個引數就是配置這個grace period的。
  • eviction-max-pod-grace-period - (e.g. memory.available=30s) 這個是配置Evict Pods時,Pod Termination的Max Grace Period。如果待Evict的Pod指定了pod.Spec.TerminationGracePeriodSeconds,則取min(eviction-max-pod-grace-period, pod.Spec.TerminationGracePeriodSeconds)作為Pod Termination真正的Grace Period。

因此,從kubelet監控到的Eviction Signal達到指定的Soft Eviction Thresholds開始,到Pod真正被Kill,總共所需要的時間為:
sum(eviction-soft-grace-period + min(eviction-max-pod-grace-period, pod.Spec.TerminationGracePeriodSeconds))

Hard Eviction Thresholds

理解了Soft Eviction Thresholds,那麼Hard Eviction Thresholds就很簡單了,它是指:當Eviction Signal中值達到Hard Eviction Thresholds配置的值時,會立刻觸發Kubelet去Evict Pods,並且也不會有Pod Termination Grace Period,而是立刻kill Pods,即使待Evict的Pod指定了pod.Spec.TerminationGracePeriodSeconds

總之,Hard Eviction Thresholds就是來硬的,一旦觸發,kubelet立刻馬上kill相關的pods。

因此,kubelet關於Hard Eviction Thresholds的配置也只有一個:

  • eviction-hard - (e.g. memory.available<1Gi) 這個值,要設定的比eviction-soft更低才有意義。

Eviction Monitoring Interval

kubelet會通過監控Eviction Signal的值,當達到配置的閾值時,就會觸發Evict Pods。
kubelet對應的監控週期,就通過cAdvisor的housekeeping-interval配置的,預設10s。

Node Conditions

Hard Eviction ThresholdsSoft Eviction Thresholds被觸及後,Kubelet會將對應的Eviction Signals對映到對應的Node Conditions,其對映關係如下:

Node Condition Eviction Signal Description
MemoryPressure memory.available Available memory on the node has satisfied an eviction threshold
DiskPressure nodefs.available, nodefs.inodesFree, imagefs.available, or imagefs.inodesFree Available disk space and inodes on either the node’s root filesystem or image filesystem has satisfied an eviction threshold

kubelet映射了Node Condition之後,會繼續按照--node-status-update-frequency(default 10s)配置的時間間隔,週期性的與kube-apiserver進行node status updates。

Oscillation of node conditions

想象一下,如果一個Node上監控到的Soft Eviction Signals的值,一直在eviction-soft水平線上下波動,那麼Kubelet就會將該Node對應的Node Condition在true和false頻繁切換。這可不是什麼好事,它可能會帶來kube-scheduler做出錯誤的排程決定。kubelet是怎麼處理這種情況的呢?

很簡單,Kubelet通過新增引數eviction-pressure-transition-period(default 5m0s)配置,使Kubelet在解除由Evicion Signal對映的Node Pressure之前,必須等待這麼長的時間。

因此,邏輯就變成這樣了:

  • Soft Evction Singal高於Soft Eviction Thresholds時,Kubelet還是會立刻設定對應的MemoryPressure Or DiskPressure為True。
  • 當MemoryPressure Or DiskPressure為True的前提下,發生了Soft Evction Singal低於Soft Eviction Thresholds的情況,則需要等待eviction-pressure-transition-period(default 5m0s)配置的這麼長時間,才會將condition pressure切換回False。

一句話總結:Node Condition Pressure成為True容易,切換回False則要等eviction-pressure-transition-period

Eviction of Pods

Kubelet的Eviction流程概括如下:

  • 在每一個監控週期內,如果Eviction Thresholds被觸及,則:
    • 獲取候選Pod
    • Fail the Pod
    • 等待該Pod被Terminated
      如果該Pod由於種種原因沒有被成功Terminated,Kubelet將會再選一個Pod進行Fail Operation。其中,Fail Pod的時候,Kubelet是通過呼叫容器執行時的KillPod介面,如果介面返回True,則認為Fail Pod成功,否則視為失敗。

Eviction Strategy

kubelet根據Pod的QoS Class實現了一套預設的Evication策略,內容見我的另外一篇博文Kubernetes Resource QoS機制解讀中介紹的“如何根據不同的QoS回收Resource”,這裡不再贅述。

下面給出Eviction Strategy的圖解:

這裡寫圖片描述

Minimum eviction reclaim

有些情況下,eviction pods可能只能回收一小部分的資源就能使得Evication Signal的值低於Thresholds。但是,可能隨著資源使用的波動或者新的排程Pod使得在該Node上很快又會觸發evict pods的動作,eviction畢竟是耗時的動作,所以應該儘量避免這種情況的發生。

Kubelet是通過--eviction-minimum-reclaim(e.g. memory.available=0Mi,nodefs.available=500Mi,imagefs.available=2Gi)來控制每次Evict Pods後,Node上對應的Resource不僅要比Eviction Thresholds低,還要保證最少比Eviction Thresholds低--eviction-minimum-reclaim中配置的數量。

Node OOM Behavior

正常情況下,但Node上資源利用率很高時,Node的資源回收是會通過Kubelet Eviction觸發完成。但是存在這麼一種情況,Kubelet配置的Soft/Hard memory.available還沒觸發,卻先觸發了Node上linux kernel oom_killer,這時回收記憶體資源的請求就被kernel oom_killer處理了,而不會經過Kubelet Eviction去完成。

我的博文Kubernetes Resource QoS機制解讀中介紹過,Kubelet根據Pod QoS給每個container都設定了oom_score_adj,整理如下:

Quality of Service oom_score_adj
Guaranteed -998
BestEffort 1000
Burstable min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

oom_killer根據container使用的記憶體佔Node總記憶體的百分比計算得到該container的oom_score,然後再將該oom_sore和前面對應的oom_score_adj相加作為最終的oom_score,Node上所有containers的最終oom_score進行排名,將oom_score得分最高的container kill掉。通過如此方式進行資源回收。

oom_killer這樣做的目標就是幹掉QoS低的又消耗最多記憶體(相對request值)的容器首先被kill掉,如此回收記憶體。

不同於Kubelet Evict Pods的是,Node OOM Behavior存在一個缺點:如果Pod中某個容器被oom_killer幹掉之後,會根據該容器的RestartPolicy決定是否restart這個容器。如果這是個有問題的容器,restart之後,可能又很快消耗大量記憶體進而觸發了再次Node OOM Behavior,如此迴圈反覆,該Node沒有真正的回收到記憶體資源。

Scheduler

前面提到,Kubelet會定期的將Node Condition傳給kube-apiserver並存於etcd。kube-scheduler watch到Node Condition Pressure之後,會根據以下策略,阻止更多Pods Bind到該Node。

Node Condition Scheduler Behavior
MemoryPressure No new BestEffort pods are scheduled to the node.
DiskPressure No new pods are scheduled to the node.

總結

  • Kubelet通過Eviction Signal來記錄監控到的Node節點使用情況。
  • Eviction Signal支援:memory.available, nodefs.available, nodefs.inodesFree, imagefs.available, imagefs.inodesFree。
  • 通過設定Hard Eviction ThresholdsSoft Eviction Thresholds相關引數來觸發Kubelet進行Evict Pods的操作。
  • Evict Pods的時候根據Pod QoS和資源使用情況挑選Pods進行Kill。
  • Kubelet通過eviction-pressure-transition-period防止Node Condition來回切換引起scheduler做出錯誤的排程決定。
  • Kubelet通過--eviction-minimum-reclaim來保證每次進行資源回收後,Node的最少可用資源,以避免頻繁被觸發Evict Pods操作。
  • 當Node Condition為MemoryPressure時,Scheduler不會排程新的QoS Class為BestEffort的Pods到該Node。
  • 當Node Condition為DiskPressure時,Scheduler不會排程任何新的Pods到該Node。