對於運行各種負載(如Service、Job)的中等規模或者大規模的集群來說,出於各種原因,我們需要盡可能提高集群的資源利用率。而提高資源利用率的常規做法是采用優先級方案,即不同類型的負載對應不同的優先級,同時允許集群中的所有負載所需的資源總量超過集群可提供的資源,在這種情況下,當發生資源不足的情況時,系統可以選擇釋 放一些不重要的負載(優先級最低的),保障最重要的負載能夠獲取足夠的資源穩定運行。
在Kubernetes 1.8
版本之前,當集群的可用資源不足時,在用戶提交新的Pod
創建請求后,該Pod
會一直處於Pending
狀態,即使這個Pod
是一個很重要(很有身份)的Pod
,也只能被動等待其他Pod被刪除並釋放資源,才能有機會被調度成功。
Kubernetes 1.8
版本引入了基於Pod
優先級 搶占Pod Priority Preemption
的調度策略,此時Kubernetes
會嘗試釋放目標節點上低優先級的Pod
,以騰出空間(資源)安置高優先級的Pod
,這種調度方式被稱為“搶占式調度”。在Kubernetes 1.11
版本中,該特性升級為Beta
版本,默認開啟,在后繼的Kubernetes 1.14
版本中正式Release
。如何聲明一個負載相對其他負載“更重要”?我們可以通過以下幾個維度來定義:
- Priority,優先級
- QoS,服務質量等級
- 系統定義的其他度量指標
優先級搶占調度策略的核心行為分別是驅逐Eviction
與搶占Preemption
,這兩種行為的使用場景不同,效果相同。Eviction
是kubelet
進程的行為,即當一個Node
發生資源不足under resource pressure
的情況時,該節點上的kubelet
進程會執行驅逐動作,此時Kubelet
會綜合考慮Pod
的優先級、資源申請量與實際使用量等信息來計算哪些Pod
需要被驅逐;當同樣優先級的Pod
需要被驅逐時,實際使用的資源量超過申請量最大倍數的高耗能Pod
會被首先驅逐。對於QoS
等級為Best Effort
的Pod
來說,由於沒有定義資源申請CPU/Memory Request
,所以它們實際使用的資源可能非常大。Preemption
則是Scheduler
執行的行為,當一個新的Pod
因為資源無法滿足而不能被調度時,Scheduler
可能(有權決定)選擇驅逐部分低優先級的Pod
實例來滿足此Pod
的調度目標,這就是Preemption
機制。
需要注意的是,Scheduler
可能會驅逐Node A
上的一個Pod
以滿足Node B
上的一個新Pod
的調度任務。比如下面的這個例子:
一個低優先級的Pod A在Node A(屬於機架R)上運行,此時有一個高優先級的 Pod B等待調度,目標節點是同屬機架R的Node B,他們中的一個或全部都定義了antiaffinity規則,不允許在同一個機架上運行,此時Scheduler只好“丟車保帥”,驅逐低優 先級的Pod A以滿足高優先級的Pod B的調度。
Pod
優先級調度示例如下
首先,由集群管理員創建PriorityClasses
,PriorityClass
不屬於任何命名空間:
---
apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."
上述YAML
文件定義了一個名為high-priority
的優先級類別,優先級為100000
,數字越大,優先級越高,超過一億的數字被系統保留,用於指派給系統組件。
可以在任意Pod
中引用上述Pod
優先級類別:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority
如果發生了需要搶占的調度,高優先級Pod
就可能搶占節點N
,並將其低優先級Pod
驅逐出節點N
,高優先級Pod
的status
信息中的nominatedNodeName
字段會記錄目標節點N
的名稱。需要注意,高優先級Pod
仍然無法保證最終被調度到節點N
上,在節點N
上低優先級Pod
被驅逐的過程中,如果有新的節點滿足高優先級Pod
的需求,就會把它調度到新的Node
上。而如果在等待低優先級的Pod
退出的過程中,又出現了優先級更高的Pod
,調度器將會調度這個更高優先級的Pod
到節點N
上,並重新調度之前等待的高優先級Pod
。
優先級搶占的調度方式可能會導致調度陷入“死循環”狀態。當Kubernetes
集群配置了多個調度器Scheduler
時,這一行為可能就會發生,比如下面這個例子:
Scheduler A為了調度一個(批)Pod,特地驅逐了一些Pod,因此在集群中有了空 余的空間可以用來調度,此時Scheduler B恰好搶在Scheduler A之前調度了一個新的 Pod,消耗了相應的資源,因此,當Scheduler A清理完資源后正式發起Pod的調度時, 卻發現資源不足,被目標節點的kubelet進程拒絕了調度請求!這種情況的確無解,因 此最好的做法是讓多個Scheduler相互協作來共同實現一個目標。
最后要指出一點:使用優先級搶占的調度策略可能會導致某些Pod
永遠無法被成功調度。因此優先級調度不但增加了系統的復雜性,還可能帶來額外不穩定的因素。因此,一旦發生資源緊張的局面,首先要考 慮的是集群擴容,如果無法擴容,則再考慮有監管的優先級調度特性, 比如結合基於Namespace
的資源配額限制來約束任意優先級搶占行為。
文章參考來源:《kubernetes權威指南-第4版》