一. 簡(jiǎn)介
在 Kubernetes 項(xiàng)目中,默認(rèn)調(diào)度器(default scheduler)的主要職責(zé)河泳,就是為一個(gè)新創(chuàng)建出來(lái)的 Pod年栓,尋找一個(gè)最合適的節(jié)點(diǎn)(Node)。
而這里“最合適”的含義纸兔,包括三層:
- 從集群所有的節(jié)點(diǎn)中否副,根據(jù)調(diào)度算法挑選出所有可以運(yùn)行該 Pod 的節(jié)點(diǎn)。
- 從第一步的結(jié)果中负甸,再根據(jù)調(diào)度算法挑選一個(gè)最符合條件的節(jié)點(diǎn)作為最終結(jié)果痹届。
- 當(dāng)一個(gè)高優(yōu)先級(jí)的 Pod 調(diào)度失敗后,該 Pod 并不會(huì)被“擱置”蚕捉,而是會(huì)“擠走”某個(gè) Node 上的一些低優(yōu)先級(jí)的 Pod 柴淘。
所以在具體的調(diào)度流程中秘通,默認(rèn)調(diào)度器會(huì)首先調(diào)用一組叫作 Predicate
的調(diào)度算法敛熬,來(lái)檢查每個(gè) Node。然后话原,再調(diào)用一組叫作 Priority
的調(diào)度算法诲锹,來(lái)給上一步得到的結(jié)果里的每個(gè) Node 打分。最終的調(diào)度結(jié)果黄虱,就是得分最高的那個(gè) Node庸诱。如果出現(xiàn)資源競(jìng)爭(zhēng),那就需要通過(guò) Preemption
的搶占機(jī)制來(lái)進(jìn)行調(diào)度默勾。
二. Predicates
2.1 定義
Predicates
在調(diào)度過(guò)程中的作用聚谁,可以理解為 Filter
滞诺,即:它按照調(diào)度策略,從當(dāng)前集群的所有節(jié)點(diǎn)中朵耕,“過(guò)濾”出一系列符合條件的節(jié)點(diǎn)淋叶。這些節(jié)點(diǎn),都是可以運(yùn)行待調(diào)度 Pod 的宿主機(jī)处嫌。
2.2 過(guò)濾規(guī)則
2.2.1 GeneralPredicates
GeneralPredicates
負(fù)責(zé)的是最基礎(chǔ)的調(diào)度策略斟湃。比如,PodFitsResources
計(jì)算的就是宿主機(jī)的 CPU 和內(nèi)存資源等是否夠用注暗。
PodFitsResources
檢查的只是 Pod 的 requests 字段。Kubernetes 的調(diào)度器并沒(méi)有為 GPU 等硬件資源定義具體的資源類型赚楚,而是統(tǒng)一用一種名叫 Extended Resource
的骗卜、Key-Value
格式的擴(kuò)展字段來(lái)描述的。
像上面這樣一組GeneralPredicates
勇皇,正是 Kubernetes 考察一個(gè) Pod 能不能運(yùn)行在一個(gè) Node 上最基本的過(guò)濾條件焚刺。所以,GeneralPredicates
也會(huì)被其他組件(比如 kubelet
)直接調(diào)用兄淫。
2.2.2 Volume 相關(guān)的過(guò)濾規(guī)則
這一組過(guò)濾規(guī)則負(fù)責(zé)的是跟容器持久化 Volume 相關(guān)的調(diào)度策略蔓姚。其中,NoDiskConflict
檢查的條件泄私,是多個(gè) Pod 聲明掛載的持久化 Volume 是否有沖突备闲。比如,AWS EBS 類型的 Volume咧纠,是不允許被兩個(gè) Pod 同時(shí)使用的泻骤。所以,當(dāng)一個(gè)名叫 A 的 EBS Volume 已經(jīng)被掛載在了某個(gè)節(jié)點(diǎn)上時(shí)演痒,另一個(gè)同樣聲明使用這個(gè) A Volume 的 Pod符欠,就不能被調(diào)度到這個(gè)節(jié)點(diǎn)上了。
2.2.3 宿主機(jī)相關(guān)的過(guò)濾規(guī)則
這一組規(guī)則主要考察待調(diào)度 Pod 是否滿足 Node 本身的某些條件诊沪。比如,PodToleratesNodeTaints晕粪,負(fù)責(zé)檢查的就是我們前面經(jīng)常用到的 Node 的“污點(diǎn)”機(jī)制渐裸。只有當(dāng) Pod 的 Toleration 字段與 Node 的 Taint 字段能夠匹配的時(shí)候,這個(gè) Pod 才能被調(diào)度到該節(jié)點(diǎn)上尚氛。
2.2.4 Pod 相關(guān)的過(guò)濾規(guī)則
這一組規(guī)則跟 GeneralPredicates
大多數(shù)是重合的洞渤。比較特殊的,是 PodAffinityPredicate
讯柔。這個(gè)規(guī)則的作用护昧,是檢查待調(diào)度 Pod 與 Node 上的已有 Pod 之間的親密(affinity)和反親密(anti-affinity)關(guān)系。
2.3 小結(jié)
在具體執(zhí)行的時(shí)候捣炬, 當(dāng)開(kāi)始調(diào)度一個(gè) Pod 時(shí)怠晴,Kubernetes 調(diào)度器會(huì)同時(shí)啟動(dòng) 16 個(gè) Goroutine
,來(lái)并發(fā)地為集群里的所有 Node 計(jì)算 Predicates,最后返回可以運(yùn)行這個(gè) Pod 的宿主機(jī)列表冲粤。
同時(shí)页眯,為每個(gè) Node 執(zhí)行 Predicates
時(shí),調(diào)度器會(huì)按照固定的順序來(lái)進(jìn)行檢查傀顾。這個(gè)順序碌奉,是按照 Predicates 本身的含義來(lái)確定的寒砖。
三. Priorities
3.1 定義
在 Predicates 階段完成了節(jié)點(diǎn)的“過(guò)濾”之后嫉拐,Priorities
階段的工作就是為這些節(jié)點(diǎn)打分婉徘。這里打分的范圍是 0-10 分,得分最高的節(jié)點(diǎn)就是最后被 Pod 綁定的最佳節(jié)點(diǎn)盖呼。
3.2 打分規(guī)則
3.2.1 LeastRequestedPriority
LeastRequestedPriority
是 Priorities 里最常用到的一個(gè)打分規(guī)則几晤。
公式如下:
score = (cpu((capacity-sum(requested))10/capacity) + memory((capacity-sum(requested))10/capacity))/2
可以看到,這個(gè)算法實(shí)際上就是在選擇空閑資源(CPU 和 Memory)最多的宿主機(jī)章钾。
3.2.2 BalancedResourceAllocation
公式如下:
score = 10 - variance(cpuFraction,memoryFraction,volumeFraction)*10
其中热芹,每種資源的 Fraction
的定義是 :Pod 請(qǐng)求的資源 / 節(jié)點(diǎn)上的可用資源
。而 variance 算法的作用府寒,則是計(jì)算每?jī)煞N資源 Fraction 之間的“距離”报腔。而最后選擇的,則是資源 Fraction 差距最小的節(jié)點(diǎn)纤房。
可以看出翻诉,BalancedResourceAllocation
選擇的碰煌,其實(shí)是調(diào)度完成后,所有節(jié)點(diǎn)里各種資源分配最均衡的那個(gè)節(jié)點(diǎn)芦圾,從而避免一個(gè)節(jié)點(diǎn)上 CPU 被大量分配、而 Memory 大量剩余的情況洪乍。
3.2.3 ImageLocalityPriority
ImageLocalityPriority
是在 Kubernetes v1.12 里新開(kāi)啟的調(diào)度規(guī)則,即:如果待調(diào)度 Pod 需要使用的鏡像很大役拴,并且已經(jīng)存在于某些 Node 上钾埂,那么這些 Node 的得分就會(huì)比較高褥紫。
為了避免這個(gè)算法引發(fā)調(diào)度堆疊,調(diào)度器在計(jì)算得分的時(shí)候還會(huì)根據(jù)鏡像的分布進(jìn)行優(yōu)化部念,即:如果大鏡像分布的節(jié)點(diǎn)數(shù)目很少氨菇,那么這些節(jié)點(diǎn)的權(quán)重就會(huì)被調(diào)低,從而“對(duì)沖”掉引起調(diào)度堆疊的風(fēng)險(xiǎn)乌询。
3.2.4 Others
NodeAffinityPriority
豌研、 TaintTolerationPriority
和 InterPodAffinityPriority
這三種 Priority。
它們與 PodMatchNodeSelector
鬼佣、PodToleratesNodeTaints
和 PodAffinityPredicate
這三個(gè) Predicate
的含義和計(jì)算方法是類似的霜浴。但是作為 Priority,一個(gè) Node 滿足上述規(guī)則的字段數(shù)目越多房铭,它的得分就會(huì)越高温眉。
四. Preemption
4.1 場(chǎng)景
當(dāng)一個(gè)高優(yōu)先級(jí)的 Pod 調(diào)度失敗后翁狐,該 Pod 并不會(huì)被“擱置”,而是會(huì)“擠走”某個(gè) Node 上的一些低優(yōu)先級(jí)的 Pod 。這樣就可以保證這個(gè)高優(yōu)先級(jí) Pod 的調(diào)度成功砂心,這就需要用到 Preemption
搶占機(jī)制蛇耀。
4.2 原理
當(dāng)上述搶占過(guò)程發(fā)生時(shí),搶占者并不會(huì)立刻被調(diào)度到被搶占的 Node 上译暂。事實(shí)上撩炊,調(diào)度器只會(huì)將搶占者的spec.nominatedNodeName
字段拧咳,設(shè)置為被搶占的 Node 的名字,所以這就是主要方法祭衩。
4.3 案例
4.3.1 PriorityClass 設(shè)置
apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
name: high-priority
value: 999999999
globalDefault: false
description: "This is a high-priority config."
上面這個(gè) YAML 文件阅签,定義的是一個(gè)名叫 high-priority
的 PriorityClass
,其中 value 的值是 999999999
劫乱。Kubernetes 規(guī)定锥涕,優(yōu)先級(jí)是一個(gè)32 bit
的整數(shù),最大值不超過(guò)1000000000(10 億层坠,1 billion)
,并且值越大代表優(yōu)先級(jí)越高谦趣。而超出 10 億的值座每,其實(shí)是被 Kubernetes 保留下來(lái)分配給系統(tǒng) Pod 使用的峭梳。這樣做的目的,就是保證系統(tǒng) Pod 不會(huì)被用戶搶占掉。
4.3.2 Pod 配置
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority
這個(gè) Pod 通過(guò) priorityClassName
字段口四,聲明了要使用名叫 high-priority
的 PriorityClass
蔓彩。當(dāng)這個(gè) Pod 被提交給 Kubernetes 之后驳概,Kubernetes 的 PriorityAdmissionController
就會(huì)自動(dòng)將這個(gè) Pod 的 spec.priority
字段設(shè)置為 999999999
。
4.4 隊(duì)列
Kubernetes 調(diào)度器實(shí)現(xiàn)搶占算法的一個(gè)最重要的設(shè)計(jì)探膊,就是在調(diào)度隊(duì)列的實(shí)現(xiàn)里待榔,使用了兩個(gè)不同的隊(duì)列。
4.4.1 activeQ
凡是在 activeQ
里的 Pod腌闯,都是下一個(gè)調(diào)度周期需要調(diào)度的對(duì)象雕憔。所以斤彼,當(dāng)在 Kubernetes 集群里新創(chuàng)建一個(gè) Pod 的時(shí)候,調(diào)度器會(huì)將這個(gè) Pod 入隊(duì)到 activeQ
里面嘲玫。調(diào)度器不斷從隊(duì)列里出隊(duì)(Pop)一個(gè) Pod 進(jìn)行調(diào)度并扇,實(shí)際上都是從 activeQ 里出隊(duì)的。
4.4.2 unschedulableQ
這是專門用來(lái)存放調(diào)度失敗的 Pod土陪,類似 Rabbitmq 使用的死信隊(duì)列肴熏。當(dāng)一個(gè) unschedulableQ
里的 Pod 被更新之后,調(diào)度器會(huì)自動(dòng)把這個(gè) Pod 移動(dòng)到 activeQ
里取刃。
4.4 搶占流程
4.4.1 調(diào)度器為“搶占者”尋找“被搶占者”的流程
- 第一步出刷,調(diào)度器會(huì)檢查這次失敗事件的原因馁龟,來(lái)確認(rèn)搶占是不是可以幫助搶占者找到一個(gè)新節(jié)點(diǎn)。這是因?yàn)橛泻芏?
Predicates
的失敗是不能通過(guò)搶占來(lái)解決的却音。比如矢炼,PodFitsHost
算法(負(fù)責(zé)的是,檢查 Pod 的nodeSelector
與 Node 的名字是否匹配)夷陋,這種情況下胰锌,除非 Node 的名字發(fā)生變化资昧,否則即使刪除再多的 Pod,搶占者也不可能調(diào)度成功格带。 - 第二步撤缴,如果確定搶占可以發(fā)生,那么調(diào)度器就會(huì)把自己緩存的所有節(jié)點(diǎn)信息復(fù)制一份叽唱,然后使用這個(gè)副本來(lái)模擬搶占過(guò)程屈呕。
當(dāng)遍歷完所有的節(jié)點(diǎn)之后,調(diào)度器會(huì)在上述模擬產(chǎn)生的所有搶占結(jié)果里做一個(gè)選擇尔觉,找出最佳結(jié)果凉袱。而這一步的判斷原則,就是盡量減少搶占對(duì)整個(gè)系統(tǒng)的影響侦铜。
4.4.2 調(diào)度器就真正開(kāi)始搶占
- 第一步专甩,調(diào)度器會(huì)檢“被搶占者”列表,清理這些 Pod 所攜帶的
nominatedNodeName
字段涤躲。 - 第二步,調(diào)度器會(huì)把搶占者的
nominatedNodeName
贡未,設(shè)置為被搶占的 Node 的名字种樱。 - 第三步蒙袍,調(diào)度器會(huì)開(kāi)啟一個(gè)
Goroutine
,同步地刪除“被搶占者”嫩挤。
所以接下來(lái)害幅,調(diào)度器就會(huì)通過(guò)正常的調(diào)度流程把搶占者調(diào)度成功,在這個(gè)正常的調(diào)度流程里岂昭,是一切皆有可能的以现。
4.5 特殊場(chǎng)景
在為某一對(duì) Pod 和 Node 執(zhí)行 Predicates 算法的時(shí)候,如果待檢查的 Node 是一個(gè)即將被搶占的節(jié)點(diǎn)约啊,即:調(diào)度隊(duì)列里有 nominatedNodeName
字段值是該 Node 名字的 Pod 存在(“潛在的搶占者”)邑遏。那么,調(diào)度器就會(huì)對(duì)這個(gè) Node 恰矩,將同樣的 Predicates 算法運(yùn)行兩遍记盒。
4.5.1 流程
- 第一遍, 調(diào)度器會(huì)假設(shè)上述“潛在的搶占者”已經(jīng)運(yùn)行在這個(gè)節(jié)點(diǎn)上外傅,然后執(zhí)行 Predicates 算法纪吮。
- 第二遍, 調(diào)度器會(huì)正常執(zhí)行 Predicates 算法栏豺,不考慮任何“潛在的搶占者”彬碱。而只有這兩遍 Predicates 算法都能通過(guò)時(shí),這個(gè) Pod 和 Node 才會(huì)被認(rèn)為是可以綁定(bind)的奥洼。
4.5.3 原因
第一遍 Predicates 算法的原因:是由于
InterPodAntiAffinity
規(guī)則的存在巷疼。
由于InterPodAntiAffinity
規(guī)則關(guān)心待考察節(jié)點(diǎn)上所有 Pod 之間的互斥關(guān)系,所以我們?cè)趫?zhí)行調(diào)度算法時(shí)必須考慮灵奖,如果搶占者已經(jīng)存在于待考察 Node 上時(shí)嚼沿,待調(diào)度 Pod 還能不能調(diào)度成功。
我們?cè)谶@一步只需要考慮那些優(yōu)先級(jí)等于或者大于待調(diào)度 Pod 的搶占者瓷患。畢竟對(duì)于其他較低優(yōu)先級(jí) Pod 來(lái)說(shuō)骡尽,待調(diào)度 Pod 總是可以通過(guò)搶占運(yùn)行在待考察 Node 上。第二遍 Predicates 算法的原因:因?yàn)椤皾撛诘膿屨颊摺弊詈蟛灰欢〞?huì)運(yùn)行在待考察的 Node 上擅编。原因是攀细,Kubernetes 調(diào)度器并不保證搶占者一定會(huì)運(yùn)行在當(dāng)初選定的被搶占的 Node 上。
五. 總結(jié)
- 調(diào)度過(guò)程如下:
待調(diào)度Pod被提交到 apiServer -> 更新到 Etcd -> 調(diào)度器 Watch Etcd 感知到有需要調(diào)度的Pod(Informer) -> 取出待調(diào)度Pod的信息 -> Predicates: 挑選出可以運(yùn)行該P(yáng)od的所有Node -> Priorities:給所有Node打分 -> 將Pod綁定到得分最高的Node上 -> 將Pod信息更新回 Etcd -> node的 kubelet 感知到 Etcd 中有自己node需要拉起的Pod -> 取出該P(yáng)od信息爱态,做基本的二次檢測(cè)(端口谭贪,資源等) -> 在node 上拉起該pod
- 搶占過(guò)程如下:
Preemption:確定要發(fā)生搶占 -> 調(diào)度器將所有節(jié)點(diǎn)信息復(fù)制一份,開(kāi)始模擬搶占 -> 檢查副本里的每一個(gè)節(jié)點(diǎn)锦担,然后從該節(jié)點(diǎn)上逐個(gè)刪除低優(yōu)先級(jí)Pod俭识,直到滿足搶占者能運(yùn)行 -> 找到一個(gè)能運(yùn)行搶占者Pod的node -> 記錄下這個(gè)Node名字和被刪除Pod的列表 -> 模擬搶占結(jié)束 -> 開(kāi)始真正搶占 -> 刪除被搶占者的Pod,將搶占者調(diào)度到Node上
歡迎收藏個(gè)人博客: Wyatt's Blog 洞渔,非常感謝~
Reference
https://time.geekbang.org/column/article/70519?utm_campaign=guanwang&utm_source=baidu-ad&utm_medium=ppzq-pc&utm_content=title&utm_term=baidu-ad-ppzq-title
https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/
https://dominik-tornow.medium.com/the-kubernetes-scheduler-cd429abac02f
https://alibaba-cloud.medium.com/getting-started-with-kubernetes-scheduling-and-resource-management-4d819c901b8c