轉(zhuǎn)載請(qǐng)注明出處即可悦冀。
所使用源碼k8s源碼為release-1.18
以下內(nèi)容所提到的概念盡量至少找到一處出處(書籍或維基百科)
一心包、調(diào)度與調(diào)度器
日出而作,日入而息----《莊子·讓王》
概念定義
首先我們先看下百度百科中對(duì)調(diào)度的定義。
調(diào)度畏梆,常用作動(dòng)詞,意為調(diào)動(dòng);安排人力奠涌、車輛宪巨。用作名詞時(shí),可以指擔(dān)負(fù)指揮調(diào)派人力溜畅、工作捏卓、車輛等工作的人、調(diào)度員慈格,也可以當(dāng)人講的一類稱呼怠晴。
根據(jù)這個(gè)描述我們大概能知曉調(diào)度的具體作用了。但是在實(shí)際的生活中浴捆,調(diào)度涵蓋的范圍遠(yuǎn)遠(yuǎn)超過了人力和車輛的安排蒜田。比如,在碼頭裝卸集裝箱选泻,通過什么樣的方式才能讓現(xiàn)場(chǎng)的工作人員和機(jī)器配合在符合安全紅線的基準(zhǔn)上達(dá)到裝卸速度最快冲粤。又比如在技術(shù)領(lǐng)域,操作系統(tǒng)如何分配CPU時(shí)間页眯,達(dá)到進(jìn)程間盡可能公平共享CPU時(shí)間梯捕,還要考慮不同任務(wù)的優(yōu)先級(jí)格郁。
根據(jù)現(xiàn)實(shí)中的生產(chǎn)生活以及技術(shù)領(lǐng)域的相關(guān)調(diào)度概念的抽象怔毛。我們可以總結(jié)出調(diào)度的一個(gè)模型和概念。
調(diào)度模型
調(diào)度: 某個(gè)被調(diào)度"對(duì)象"薄货,根據(jù)對(duì)象的狀態(tài)或目標(biāo)的狀態(tài)碌奉,通過規(guī)則(動(dòng)態(tài)規(guī)則或靜態(tài)規(guī)則)選擇某一個(gè)目標(biāo)的過程锣笨。
調(diào)度器: 或者說調(diào)度員,如圖中的橙色框道批,承擔(dān)的就是負(fù)責(zé)調(diào)度的工作错英。
根據(jù)上面的兩個(gè)示例來理解下這個(gè)模型。
在集裝箱的這個(gè)例子中隆豹,對(duì)象指的就是集裝箱椭岩,規(guī)則有安全規(guī)則。也有根據(jù)目前的狀態(tài)璃赡,比如輪船的大小判哥,天氣狀況,現(xiàn)成工作人員數(shù)量等等碉考,計(jì)算出的某個(gè)崗位需要在什么時(shí)間塌计,或者在什么條件下做什么樣的事情。最后達(dá)成裝卸的目標(biāo)侯谁。 在這個(gè)例子中我們也可以看到锌仅,調(diào)度器實(shí)際并不需要自己完成或做什么具體的任務(wù)(策略與執(zhí)行分離)章钾,依賴港口的機(jī)械以及工作人員可以達(dá)成相關(guān)的目標(biāo)。
在進(jìn)程調(diào)度中热芹,進(jìn)程是被調(diào)度的對(duì)象贱傀,規(guī)則是指硬編碼到kernel/sched包下(或者早期的kernel/sched.c)下的代碼,狀態(tài)是指當(dāng)前進(jìn)程的狀態(tài)比如進(jìn)程的優(yōu)先級(jí)等伊脓。目標(biāo)在這里可以描述為要獲取的資源府寒,既cpu時(shí)間。
調(diào)度的目標(biāo)
在完全回到技術(shù)領(lǐng)域之前报腔,我們?cè)倏聪聻槭裁匆姓{(diào)度株搔,以及調(diào)度的目標(biāo)是什么。
首先纯蛾,在集裝箱和進(jìn)程調(diào)度的例子中纤房,如果沒有調(diào)度,所有的對(duì)象安裝不違反部分規(guī)則的情況下茅撞,也是可以運(yùn)行一會(huì)的。但最終會(huì)從有序逐漸向無序擴(kuò)散巨朦,直到無法繼續(xù)進(jìn)行米丘。在這里可以想下紅綠燈,如果某個(gè)十字路口的紅綠燈壞了糊啡,隨著車流量的增多拄查,在沒有干預(yù)的情況下會(huì)怎么樣。
調(diào)度的目標(biāo)中除了協(xié)調(diào)對(duì)象能達(dá)成某種目標(biāo)外棚蓄,還需要進(jìn)行提升質(zhì)量(安全)堕扶,降低成本和提高效率。提高效率又分為兩個(gè)方面梭依,一個(gè)是調(diào)度器自身的效率提升稍算,提高決策效率,不應(yīng)該由于調(diào)度器本身的效率低下役拴,而影響整個(gè)事件的推進(jìn)糊探。另一個(gè)是調(diào)度規(guī)則的調(diào)整和優(yōu)化,提升被協(xié)調(diào)對(duì)象達(dá)成目標(biāo)的效率河闰。
如果是一個(gè)技術(shù)leader的話科平,可能會(huì)發(fā)現(xiàn)調(diào)度器的工作和自身的工作有些類似。但調(diào)度器考量的因素要比和人打交道少的多^_^
姜性。
二瞪慧、技術(shù)領(lǐng)域的資源調(diào)度
上個(gè)小節(jié)描述了調(diào)度的基本模型,那么我們回到技術(shù)領(lǐng)域部念。我們?cè)谶@里專注于資源調(diào)度弃酌。定時(shí)任務(wù)調(diào)度等氨菇,不在這里來描述。
資源調(diào)度的模型和上面調(diào)度模型是一樣的矢腻。只是被調(diào)度對(duì)象和目標(biāo)有所約束门驾。被調(diào)度的對(duì)象只有任務(wù)(task or work),而目標(biāo)只是資源多柑。
在技術(shù)領(lǐng)域中的資源調(diào)度奶是,其實(shí)涵蓋的范圍很廣,在操作系統(tǒng)(Linux調(diào)度器)竣灌、編程語言(goroutine調(diào)度)和一些應(yīng)用(k8s scheduler)中都存在著調(diào)度器進(jìn)行資源調(diào)度聂沙。除了這些在部分業(yè)務(wù)系統(tǒng)和基礎(chǔ)架構(gòu)中的系統(tǒng)也多多少少會(huì)有些資源調(diào)度影子在。打個(gè)比方初嘹,如果比較熟悉運(yùn)營(yíng)平臺(tái)的讀者及汉,會(huì)發(fā)現(xiàn)這里和運(yùn)營(yíng)系統(tǒng)中常用的事件驅(qū)動(dòng)架構(gòu)比較相似。但是它們分別有自己的考量點(diǎn)屯烦,資源調(diào)度需要考慮合理的利用資源坷随,甚至是在總體資源不足的情況保障頭部應(yīng)用的穩(wěn)定運(yùn)行。而運(yùn)營(yíng)系統(tǒng)則需要考慮最后的留存等驻龟。
在技術(shù)中温眉,更常見的資源調(diào)度其實(shí)是負(fù)載均衡,如果一個(gè)請(qǐng)求(或長(zhǎng)連接)看做是一個(gè)任務(wù)翁狐,那么負(fù)載均衡器之后的節(jié)點(diǎn)就是請(qǐng)求可以訪問的資源类溢。那么負(fù)載均衡所采用的算法(或者干脆隨機(jī))就是調(diào)度的規(guī)則。負(fù)載均衡的算法有輪詢露懒,加權(quán)輪詢闯冷,哈希,最小鏈接懈词,加權(quán)最小鏈接蛇耀,負(fù)載最輕,綜合利用率等坎弯。其中的綜合利用率就已經(jīng)相對(duì)接近k8s的調(diào)度邏輯了(部分相似蒂窒,由于考慮的問題不同還是有很大的差異)。
在云計(jì)算中的資源調(diào)度可能是最復(fù)雜的荞怒,如果完全按照申請(qǐng)的資源供給用戶的話洒琢,是無法做到利益最大化的。但是也確實(shí)存在用戶無法使用全部所申請(qǐng)的資源造成資源浪費(fèi)褐桌。那么如果能預(yù)測(cè)到用戶的資源使用情況(比如大部分個(gè)人用戶)衰抑,就可以在相對(duì)將其QoS和SLA的情況下,進(jìn)行資源超配荧嵌。但是為了降低某個(gè)虛機(jī)對(duì)整體的影響呛踊,可以綁定到具體的某個(gè)cpu上砾淌,降低整體風(fēng)險(xiǎn)(倉壁模式)。這里的調(diào)度器所使用的目標(biāo)函數(shù)谭网,除了解決用戶需求的服務(wù)資源調(diào)度問題汪厨,需要根據(jù)用戶的特定偏好程度選擇服務(wù)資源,同時(shí)要滿足用戶對(duì)服務(wù)時(shí)間愉择、成本劫乱、服務(wù)質(zhì)量以及滿意度的要求這些意外,還需要考慮SLA的違約率锥涕,整體的違約率需要控制在一定的范圍內(nèi)衷戈。至于某個(gè)用戶被違約的概率,就好像保險(xiǎn)一樣层坠,不出問題有概率殖妇,出了問題就只能100%的抗下來了。所以云計(jì)算廠商也會(huì)影響(說服)用戶去進(jìn)行多節(jié)點(diǎn)的部署破花,可能用戶覺得這是基于可用性的考慮谦趣,當(dāng)然在用戶角度這個(gè)是絕對(duì)正確的,在系統(tǒng)重要到一定程度座每,多地多活前鹅,甚至國(guó)際化后的全球部署都是需要考慮的。但這也其實(shí)幫助云計(jì)算廠商降低了整體的SLA違約率尺栖。
三嫡纠、資源調(diào)度中的問題
我們都知道kubernetes的本質(zhì)是一個(gè)容器編排系統(tǒng)烦租,那么對(duì)于這種編排系統(tǒng)的調(diào)度器延赌。需要考量以下問題
(1) "充分"利用集群資源
充分是加了引號(hào)的,這里的充分并不是要把某個(gè)節(jié)點(diǎn)叉橱,或某群節(jié)點(diǎn)的資源完全榨干挫以。而是要確保集群中大部分節(jié)點(diǎn)的資源(每個(gè)節(jié)點(diǎn)的資源不一定都是一樣的)消耗維持在一個(gè)相對(duì)穩(wěn)定的水平線,并且要在水位下以下窃祝。
(2) 服務(wù)分級(jí)
由于服務(wù)的重要性不同掐松,一級(jí)服務(wù)如果宕機(jī),那么每秒都在對(duì)企業(yè)的實(shí)際收入造成損失粪小,并且還影響了公司的聲譽(yù)大磺。所以根據(jù)服務(wù)重要性的不同,隔離到不同的節(jié)點(diǎn)部署探膊,并為一級(jí)服務(wù)預(yù)留更多的資源杠愧。當(dāng)然這里只是描述了不同級(jí)別服務(wù)的分離,實(shí)際的情況下逞壁,還需要根據(jù)不同的事業(yè)群流济,業(yè)務(wù)線锐锣,以及流量大小等一層一層的進(jìn)行隔離。不重要的小流量服務(wù)绳瘟,其實(shí)可以進(jìn)行一定的超配雕憔。
(3) 彈性擴(kuò)展
這里不用多說就是要預(yù)留部分資源作為動(dòng)態(tài)擴(kuò)展。防止流量突增無法快速擴(kuò)充資源糖声。
(4) 混合部署
比如CPU密集型應(yīng)用和IO密集型應(yīng)用混合部署斤彼,重復(fù)利用節(jié)點(diǎn)資源。
(5) 獨(dú)特資源分配
部分機(jī)器學(xué)習(xí)深度學(xué)習(xí)任務(wù)需要使用GPU來進(jìn)行模型計(jì)算姨丈。所以在分配節(jié)點(diǎn)時(shí)畅卓,需要優(yōu)先往帶有GPU節(jié)點(diǎn)進(jìn)行部署,當(dāng)然如果在GPU資源不夠用的情況下蟋恬,也應(yīng)該能處理無GPU的只能通過CPU來運(yùn)算的情況翁潘。
(6) 調(diào)度器規(guī)則擴(kuò)展
因?yàn)槊總€(gè)公司的實(shí)際情況不同,部分調(diào)度需求無法通過默認(rèn)的規(guī)則來實(shí)現(xiàn)歼争,那么支持?jǐn)U展拜马,或者支持多個(gè)調(diào)度器也是一個(gè)重要的功能。
(7) 調(diào)度任務(wù)的可靠性
調(diào)度任務(wù)必須要執(zhí)行沐绒,不能在調(diào)度器決策之前就丟失俩莽。所以這里需要使用調(diào)度隊(duì)列來暫存待調(diào)度的任務(wù)。
當(dāng)然這里并沒有把所有的問題都列完全乔遮,不同公司的不同環(huán)境扮超,以及不同的調(diào)度需求,會(huì)遇到不同的問題蹋肮。我們目前只需要了解有這些問題即可出刷,在未來修改(或擴(kuò)展)調(diào)度器邏輯時(shí),可以在仔細(xì)研究分析坯辩。下面我們來看下k8s的調(diào)度實(shí)現(xiàn)馁龟。
四、Kubernetes的資源調(diào)度
(1) Kubernetes的架構(gòu)
在這里簡(jiǎn)單回顧下k8s的架構(gòu)
API服務(wù)器: 其他控制平面的組件都會(huì)和它交互漆魔,提供了各類資源對(duì)象(Pod, Service等)CRUD的功能坷檩。
Scheculer: 是這篇文章的主角,主要用于為pod選擇最合適的node改抡。
Controller Manager: 執(zhí)行集群級(jí)別的功能矢炼,如復(fù)制組件,持續(xù)追蹤工作節(jié)點(diǎn)阿纤,處理節(jié)點(diǎn)失敗等句灌。
Etcd: 分布式存儲(chǔ),持久化存儲(chǔ)集群的配置阵赠。API服務(wù)器是唯一和etcd通信的服務(wù)涯塔。
kubelet: 與API服務(wù)通信肌稻,管理所在節(jié)點(diǎn)的容器。
kube-proxy: 負(fù)責(zé)組件之間的負(fù)載均衡
pod, service, node等基礎(chǔ)概念不在贅述匕荸。
(2) Kubernetes Scheduler簡(jiǎn)介
Scheduler的核心作用是將pod根據(jù)部分"規(guī)則"來部署到合適的node上爹谭。并且具體的部署Scheduler并沒有實(shí)現(xiàn),而是調(diào)用了API服務(wù)器(api server)榛搔。
Scheduler分為兩個(gè)過程 調(diào)度 和 綁定诺凡,調(diào)度又分為兩個(gè)核心過程過濾和打分。
調(diào)度過程的過濾是指践惑,在眾多node中腹泌,選擇符合pod調(diào)度需求的node。如果沒有可以調(diào)度的node尔觉,會(huì)返回空凉袱。調(diào)度器會(huì)一直等待之到有符合的node為止。而打分是指在符合條件的node列表中根據(jù)部分規(guī)則對(duì)node進(jìn)行打分侦铜,然后選擇分?jǐn)?shù)最高的node专甩。如果最高分?jǐn)?shù)的node存在多個(gè),會(huì)隨機(jī)選擇一個(gè)钉稍。早期版本具體的調(diào)度策略由過濾的 謂詞(Predicates) 和打分的 優(yōu)先級(jí)(Priorities) 來實(shí)現(xiàn)涤躲。并且Scheduler在k8s 1.15 alpha版本增加了framework提供了在調(diào)度和綁定過程中的擴(kuò)展點(diǎn)。
(3) Scheduler Frameworks簡(jiǎn)介
框架源于Scheduler Frameworks提案并且為自定義調(diào)度器提供了很多的擴(kuò)展點(diǎn)贡未。下圖就是在調(diào)度過程和綁定過程中的擴(kuò)展點(diǎn)种樱。
我們可以看到在過濾,打分俊卤,以及綁定階段都可以編寫對(duì)應(yīng)的插件并進(jìn)行注冊(cè)嫩挤,然后"干預(yù)"默認(rèn)調(diào)度策略(規(guī)則)。具體每個(gè)階段的意義瘾蛋,請(qǐng)查看這篇官網(wǎng)文檔scheduling-framework俐镐。
(4) Scheduler源碼簡(jiǎn)析
切入點(diǎn)可以從兩個(gè)方向入手, 一是cmd/kube-scheduler/scheduler.go
的main方法矫限。在這里可以發(fā)現(xiàn)Scheduler其實(shí)是一個(gè)cobra
的應(yīng)用哺哼。另一個(gè)切入點(diǎn)是pkg/scheduler/scheduler.go
的79行Scheduler struct
。
首先看下scheduler的創(chuàng)建 pkg/scheduler/scheduler.go 235行
在New scheduler的方法中叼风,首先先去構(gòu)造了默認(rèn)的選項(xiàng)取董,并提供了默認(rèn)的算法提供者名稱
DefaultProvider
,然后調(diào)用了pkg/scheduler/factory.go 219行的createFromProvider方法
无宿,并通過algorithmprovider.NewRegistry()
來注冊(cè)默認(rèn)插件茵汰。然后在pkg/scheduler/algorithmprovider/registry.go 53行
這里,獲取了默認(rèn)的插件配置孽鸡。
具體在各個(gè)階段注冊(cè)的插件如下所示(注釋里寫了插件的作用)蹂午,可以看到在打分階段栏豺,除了插件本身的名稱以外,還增加了權(quán)重豆胸,評(píng)分可以簡(jiǎn)單理解為combinedScores[host] += score * weight
是分?jǐn)?shù)乘以權(quán)重然后求和所得(最后還需要控制到一定的范圍1-100)奥洼。
func getDefaultConfig() *schedulerapi.Plugins {
return &schedulerapi.Plugins{
QueueSort: &schedulerapi.PluginSet{
Enabled: []schedulerapi.Plugin{
/*
`QueueSort`是指在`SchedulerQueue`中的pod排序,
優(yōu)先級(jí)越高的pod就有越優(yōu)先來調(diào)度和綁定晚胡。
默認(rèn)實(shí)現(xiàn)的是`PrioritySort`插件灵奖。
實(shí)現(xiàn)邏輯是通過pod.Spec.Priority數(shù)值來判斷pod的優(yōu)先級(jí),如果數(shù)值相同則通過時(shí)間戳來判斷
*/
{Name: queuesort.Name},
},
},
PreFilter: &schedulerapi.PluginSet{
Enabled: []schedulerapi.Plugin{
// 判斷node的資源是否充足
{Name: noderesources.FitName},
// 判斷node的端口是否被占用
{Name: nodeports.Name},
// pod間親和性過濾
{Name: interpodaffinity.Name},
},
},
Filter: &schedulerapi.PluginSet{
Enabled: []schedulerapi.Plugin{
// pod.Spec.Priority.Unschedulable 如果設(shè)置為true估盘,則不會(huì)進(jìn)行調(diào)度
{Name: nodeunschedulable.Name},
// 判斷node的資源是否充足
{Name: noderesources.FitName},
// 指定調(diào)度到具體的node, 實(shí)際生產(chǎn)環(huán)境不會(huì)這么做
{Name: nodename.Name},
// 判斷node的端口是否被占用
{Name: nodeports.Name},
// 判斷node和pod的親和性, 里面的matchNodeselector實(shí)現(xiàn)了對(duì)label匹配
{Name: nodeaffinity.Name},
// 檢查pod的volume卷配置瓷患,可能node沒有可用的磁盤
{Name: volumerestrictions.Name},
// Taint和toleration相互配合,可以用來避免 pod 被分配到不合適的節(jié)點(diǎn)上遣妥,和nodeaffinity正好相反
{Name: tainttoleration.Name},
// 檢查存儲(chǔ)限制都和具體的云服務(wù)廠商有關(guān)
{Name: nodevolumelimits.EBSName},
{Name: nodevolumelimits.GCEPDName},
{Name: nodevolumelimits.CSIName},
{Name: nodevolumelimits.AzureDiskName},
{Name: volumebinding.Name},
{Name: volumezone.Name},
// pod間親和性過濾
{Name: interpodaffinity.Name},
},
},
PreScore: &schedulerapi.PluginSet{
Enabled: []schedulerapi.Plugin{
{Name: interpodaffinity.Name},
// pod的拓?fù)鋽U(kuò)展約束 https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-topology-spread-constraints/
{Name: defaultpodtopologyspread.Name},
{Name: tainttoleration.Name},
},
},
Score: &schedulerapi.PluginSet{
Enabled: []schedulerapi.Plugin{
// CPU和內(nèi)存配比合適的node得分更高
{Name: noderesources.BalancedAllocationName, Weight: 1},
// 已經(jīng)下載過image的得分更改擅编,這樣可以加快pod的啟動(dòng)速度
{Name: imagelocality.Name, Weight: 1},
{Name: interpodaffinity.Name, Weight: 1},
// 資源被占用越少的得分越高
{Name: noderesources.LeastAllocatedName, Weight: 1},
{Name: nodeaffinity.Name, Weight: 1},
// 會(huì)根據(jù)注解scheduler.alpha.kubernetes.io/preferAvoidPods來判斷優(yōu)先級(jí),
// 但忽略了ReplicationController和ReplicaSet箫踩,會(huì)默認(rèn)返回最大值100
{Name: nodepreferavoidpods.Name, Weight: 10000},
// pod的拓?fù)鋽U(kuò)展約束, prescore那里提到過了
{Name: defaultpodtopologyspread.Name, Weight: 1},
{Name: tainttoleration.Name, Weight: 1},
},
},
Bind: &schedulerapi.PluginSet{
Enabled: []schedulerapi.Plugin{
// 默認(rèn)綁定邏輯
{Name: defaultbinder.Name},
},
},
}
}
其實(shí)這里并沒有把所有的核心源碼都全部說完沙咏。比如SchedulerQueue和SchedulerCache等,還有scheduler自身的選舉邏輯班套。并且如果想看下調(diào)度和綁定階段(過程)的具體實(shí)現(xiàn)肢藐,可以從scheduleOne
方法來入手閱讀。
五吱韭、結(jié)束語
本文從調(diào)度的概念入手吆豹,逐步過渡到k8s中的調(diào)度器實(shí)現(xiàn)簡(jiǎn)介。下篇文件打算寫一個(gè)Scheduler extender理盆,也有可能還寫一篇Spring源碼解析的痘煤。
參考
《云計(jì)算市場(chǎng)交易與資源調(diào)度機(jī)制》
《云計(jì)算:資源調(diào)度管理》
《Kubernetes in Action》
《深入Linux內(nèi)核架構(gòu)》
k8s-release-1.18
調(diào)度-維基百科
k8s調(diào)度框架
k8s調(diào)度器性能調(diào)優(yōu)
kube-scheduler介紹
scheduler調(diào)度框架
Scheduler Frameworks提案