本文原始地址:https://farmer-hutao.github.io/k8s-source-code-analysis/core/scheduler/desigh.html
github項目地址:https://github.com/farmer-hutao/k8s-source-code-analysis
1. 概述
我們先整體了解一下Scheduler的設計原理,然后再看這些過程是如何用代碼實現的。關於調度器的設計在官網有介紹,我下面結合官網給的說明,簡化掉不影響理解的復雜部分,和大家介紹一下Scheduler的工作過程。
英文還可以的小伙伴們可以看一下官網的介紹先:scheduler.md
官網有一段描述如下:
The Kubernetes scheduler runs as a process alongside the other master components such as the API server. Its interface to the API server is to watch for Pods with an empty PodSpec.NodeName, and for each Pod, it posts a binding indicating where the Pod should be scheduled.
簡單翻譯一下,也就是說Scheduler是一個跑在其他組件邊上的獨立程序,對接Apiserver尋找PodSpec.NodeName為空的Pod,然后用post的方式發送一個api調用,指定這些pod應該跑在哪個node上。
通俗地說,就是scheduler是相對獨立的一個組件,主動訪問api server,尋找等待調度的pod,然后通過一系列調度算法尋找哪個node適合跑這個pod,然后將這個pod和node的綁定關系發給api server,從而完成了調度的過程。
2. 源碼層級
從高level看,scheduler的源碼可以分為3層:
cmd/kube-scheduler/scheduler.go
: main() 函數入口位置,在scheduler過程開始被調用前的一系列初始化工作。pkg/scheduler/scheduler.go
: 調度框架的整體邏輯,在具體的調度算法之上的框架性的代碼。pkg/scheduler/core/generic_scheduler.go
: 具體的計算哪些node適合跑哪些pod的算法。
3. 調度算法
調度過程整體如下圖所示(官文里這個圖沒對齊,逼瘋強迫症了!!!當然由於中文顯示的問題,下圖有中文的行也沒法完全對齊,這個地方讓我很抓狂。。。):
對於一個給定的pod
+---------------------------------------------+
| 可用於調度的nodes如下: | | +--------+ +--------+ +--------+ | | | node 1 | | node 2 | | node 3 | | | +--------+ +--------+ +--------+ | +----------------------+----------------------+ | v +----------------------+----------------------+ 初步過濾: node 3 資源不足 +----------------------+----------------------+ | v +----------------------+----------------------+ | 剩下的nodes: | | +--------+ +--------+ | | | node 1 | | node 2 | | | +--------+ +--------+ | +----------------------+----------------------+ | v +----------------------+----------------------+ 優先級算法計算結果: node 1: 分數=2 node 2: 分數=5 +----------------------+----------------------+ | v 選擇分值最高的節點 = node 2
Scheduler為每個pod尋找一個適合其運行的node,大體分成三步:
- 通過一系列的“predicates”過濾掉不能運行pod的node,比如一個pod需要500M的內存,有些節點剩余內存只有100M了,就會被剔除;
- 通過一系列的“priority functions”給剩下的node排一個等級,分出三六九等,尋找能夠運行pod的若干node中最合適的一個node;
- 得分最高的一個node,也就是被“priority functions”選中的node勝出了,獲得了跑對應pod的資格。
4. Predicates 和 priorities 策略
Predicates是一些用於過濾不合適node的策略 . Priorities是一些用於區分node排名(分數)的策略(作用在通過predicates過濾的node上). K8s默認內建了一些predicates 和 priorities 策略,官方文檔介紹地址: scheduler_algorithm.md. Predicates 和 priorities 的代碼分別在:
- pkg/scheduler/algorithm/predicates/predicates.go
- pkg/scheduler/algorithm/priorities.
5. Scheduler 的拓展性
我們可以選擇哪些預置策略生效,也可以添加自己的策略。幾個月前我司有個奇葩調度需求,當時我就是通過增加一個priorities策略,然后重新編譯了一個Scheduler來實現的需求。
6. 調度策略的修改
默認調度策略是通過defaultPredicates()
和 defaultPriorities()函數
定義的,源碼在 pkg/scheduler/algorithmprovider/defaults/defaults.go
,我們可以通過命令行flag --policy-config-file
來覆蓋默認行為。所以我們可以通過配置文件的方式或者修改pkg/scheduler/algorithm/predicates/predicates.go
/pkg/scheduler/algorithm/priorities
,然后注冊到defaultPredicates()
/defaultPriorities()
來實現。配置文件類似下面這個樣子:
{ "kind" : "Policy", "apiVersion" : "v1", "predicates" : [ {"name" : "PodFitsHostPorts"}, {"name" : "PodFitsResources"}, {"name" : "NoDiskConflict"}, {"name" : "NoVolumeZoneConflict"}, {"name" : "MatchNodeSelector"}, {"name" : "HostName"} ], "priorities" : [ {"name" : "LeastRequestedPriority", "weight" : 1}, {"name" : "BalancedResourceAllocation", "weight" : 1}, {"name" : "ServiceSpreadingPriority", "weight" : 1}, {"name" : "EqualPriority", "weight" : 1} ], "hardPodAffinitySymmetricWeight" : 10, "alwaysCheckAllPredicates" : false }
ok,看到這里大伙應該在流程上對Scheduler的原理有個感性的認識了,下一節我們就開始看一下Scheduler源碼是怎么寫的。