自互聯(lián)網(wǎng)出現(xiàn)以來 ,云計(jì)算的概念已經(jīng)提出了有 50 年。從1957 年,John McCarthy 將計(jì)算機(jī)中的分時(shí)共享概念設(shè)計(jì)成了一種工具。從此以后,這個概念的名字經(jīng)歷過數(shù)次變化:從“服務(wù)中心(service bureau)”到應(yīng)用服務(wù)提供商,到互聯(lián)網(wǎng)即服務(wù),到云計(jì)算,再到軟件定義的數(shù)據(jù)中心。
一直以來基礎(chǔ)設(shè)施是云計(jì)算的基礎(chǔ)核心,基礎(chǔ)設(shè)施服務(wù)(Infrastructure as a Service,IaaS)將IT基礎(chǔ)設(shè)施資源(計(jì)算、網(wǎng)絡(luò)與存儲)以一種彈性的服務(wù)方式對外提供。近十余年來隨著云計(jì)算不斷發(fā)展與落地,云基礎(chǔ)設(shè)施技術(shù)架構(gòu)也在不斷往前演進(jìn),從傳統(tǒng)虛擬化、基礎(chǔ)設(shè)施資源管理走向軟件定義架構(gòu)。隨著近5年來以容器和微服務(wù)為代表的云原生技術(shù)興起,云基礎(chǔ)設(shè)施架構(gòu)需要演進(jìn)以全面擁抱云原生技術(shù)。
以容器、容器編排、微服務(wù)、服務(wù)網(wǎng)格為代表的云原生技術(shù)正在日益體現(xiàn)出期在云計(jì)算領(lǐng)域的非凡價(jià)值,眾所周知,容器編排中 Kubernetes 已經(jīng)成為容器編排領(lǐng)域的事實(shí)標(biāo)準(zhǔn)。
kubernetes 可以提供所需的編排和管理功能,以便針對工作負(fù)載大規(guī)模部署容器。借助 Kubernetes 編排功能,可以快速的構(gòu)建跨多個容器的應(yīng)用服務(wù)、跨集群調(diào)度、擴(kuò)展這些容器,并長期持續(xù)管理這些容器的健康狀況。在 Kubernetes 中,調(diào)度 是指將 Pod 放置到合適的 Node上,然后對應(yīng) Node 上的 Kubelet才能夠運(yùn)行這些 pod。
那么kubernetes 是如何進(jìn)行調(diào)度的呢?我們來一起看下。
Kubernetes 是如何調(diào)度的?
kube-scheduler 是 Kubernetes 集群的默認(rèn)調(diào)度器,并且是集群 控制面的一部分。同時(shí) kube-scheduler 在設(shè)計(jì)上是允許你自己寫一個調(diào)度組件并替換原有的 kube-scheduler。
對每一個新創(chuàng)建的 Pod 或者是未被調(diào)度的 Pod,kube-scheduler 會選擇一個最優(yōu)的 Node 去運(yùn)行這個 Pod。那么kube-scheduler 是如何選擇最優(yōu)的 Node 呢?
kube-scheduler監(jiān)聽apiserver的/api/pod/,當(dāng)發(fā)現(xiàn)集群中有未得到調(diào)度的pod(即PodSpec.NodeName為空)時(shí),會查詢集群各node的信息,經(jīng)過Predicates(過濾)、Priorities(優(yōu)選器),得到最適合該pod運(yùn)行的node后,再向apiserver發(fā)送請求,將該容器綁定到選中的node上。
Kubernetes Scheduler 提供的調(diào)度流程分三步:
●過濾, 遍歷nodelist,選擇出符合要求的候選節(jié)點(diǎn),Kubernetes內(nèi)置了多種預(yù)選規(guī)則供用戶選擇。
●打分, 在選擇出符合要求的候選節(jié)點(diǎn)中,采用優(yōu)選規(guī)則計(jì)算出每個節(jié)點(diǎn)的積分,最后選擇得分最高的。
●綁定, 選出其中得分最高的 Node 來運(yùn)行 Pod。之后,調(diào)度器將這個調(diào)度決定通知給 kube-apiserver,這個過程叫做綁定。
整個過程,如圖所示:
預(yù)選的算法可以參考源碼predicates.go(https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/algorithm/predicates/predicates.go):
常用的預(yù)選策略:
●PodFitsHostPorts:如果 Pod 中定義了 hostPort 屬性,那么需要先檢查這個指定端口是否 已經(jīng)被 Node 上其他服務(wù)占用了。
●PodFitsHost:若 pod 對象擁有 hostname 屬性,則檢查 Node 名稱字符串與此屬性是否匹配。
●PodFitsResources:檢查 Node 上是否有足夠的資源(如,cpu 和內(nèi)存)來滿足 pod 的資源請求。
●PodMatchNodeSelector:檢查 Node 的 標(biāo)簽 是否能匹配 Pod 屬性上 Node 的 標(biāo)簽 值。
●NoVolumeZoneConflict:檢測 pod 請求的 Volumes 在 Node 上是否可用,因?yàn)槟承┐鎯泶嬖趨^(qū)域調(diào)度約束。
●NoDiskConflict:檢查 Pod 對象請求的存儲卷在 Node 上是否可用,若不存在沖突則通過檢查。
●MaxCSIVolumeCount:檢查 Node 上已經(jīng)掛載的 CSI 存儲卷數(shù)量是否超過了指定的最大值。
●CheckNodeMemoryPressure:如果 Node 上報(bào)了內(nèi)存資源壓力過大,而且沒有配置異常,那么 Pod 將不會被調(diào)度到這個 Node 上。
●CheckNodePIDPressure:如果 Node 上報(bào)了 PID 資源壓力過大,而且沒有配置異常,那么 Pod 將不會被調(diào)度到這個 Node 上。
●CheckNodeDiskPressure:如果 Node 上報(bào)了磁盤資源壓力過大(文件系統(tǒng)滿了或者將近滿了), 而且配置異常,那么 Pod 將不會被調(diào)度到這個 Node 上。
●CheckNodeCondition:Node 可以上報(bào)其自身的狀態(tài),如磁盤、網(wǎng)絡(luò)不可用,表明 kubelet 未準(zhǔn)備好運(yùn)行 pod。如果 Node 被設(shè)置成這種狀態(tài),那么 pod 將不會被調(diào)度到這個 Node 上。
●PodToleratesNodeTaints:檢查 pod 屬性上的 tolerations 能否容忍 Node 的 taints。
●CheckVolumeBinding:檢查 Node 上已經(jīng)綁定的和未綁定的 PVCs 能否滿足 Pod 對象的存儲卷需求。
打分策略如下:
●SelectorSpreadPriority:盡量將歸屬于同一個 Service、StatefulSet 或 ReplicaSet 的 Pod 資源分散到不同的 Node 上。
●InterPodAffinityPriority:遍歷 Pod 對象的親和性條目,并將那些能夠匹配到給定 Node 的條目的權(quán)重相加,結(jié)果值越大的 Node 得分越高。
●LeastRequestedPriority:空閑資源比例越高的 Node 得分越高。換句話說,Node 上的 Pod 越多,并且資源被占用的越多,那么這個 Node 的得分就會越少。
●MostRequestedPriority:空閑資源比例越低的 Node 得分越高。這個調(diào)度策略將會把你所有的工作負(fù)載(Pod)調(diào)度到盡量少的 Node 上。
●RequestedToCapacityRatioPriority:為 Node 上每個資源占用比例設(shè)定得分值,給資源打分函數(shù)在打分時(shí)使用。
●BalancedResourceAllocation:優(yōu)選那些使得資源利用率更為均衡的節(jié)點(diǎn)。
●NodePreferAvoidPodsPriority:這個策略將根據(jù) Node 的注解信息中是否含有 scheduler.alpha.kubernetes.io/preferAvoidPods 來 計(jì)算其優(yōu)先級。使用這個策略可以將兩個不同 Pod 運(yùn)行在不同的 Node 上。
●NodeAffinityPriority:基于 Pod 屬性中 PreferredDuringSchedulingIgnoredDuringExecution 來進(jìn)行 Node 親和性調(diào)度。你可以通過這篇文章 Pods 到 Nodes 的分派 來了解到更詳細(xì)的內(nèi)容。
●TaintTolerationPriority:基于 Pod 中對每個 Node 上污點(diǎn)容忍程度進(jìn)行優(yōu)先級評估,這個策略能夠調(diào)整待選 Node 的排名。
●ImageLocalityPriority:Node 上已經(jīng)擁有 Pod 需要的 容器鏡像 的 Node 會有較高的優(yōu)先級。
●ServiceSpreadingPriority:這個調(diào)度策略的主要目的是確保將歸屬于同一個 Service 的 Pod 調(diào)度到不同的 Node 上。如果 Node 上 沒有歸屬于同一個 Service 的 Pod,這個策略更傾向于將 Pod 調(diào)度到這類 Node 上。最終的目的:即使在一個 Node 宕機(jī)之后 Service 也具有很強(qiáng)容災(zāi)能力。
●CalculateAntiAffinityPriorityMap:這個策略主要是用來實(shí)現(xiàn)pod反親和。
●EqualPriorityMap:將所有的 Node 設(shè)置成相同的權(quán)重為 1。
自定義調(diào)度器
除了 kubernetes 自帶的調(diào)度器,考慮到實(shí)際環(huán)境中的各種復(fù)雜情況,kubernetes 的調(diào)度器采用插件化的形式實(shí)現(xiàn),可以方便用戶進(jìn)行定制或者二次開發(fā),我們可以自定義一個調(diào)度器并以插件形式和 kubernetes 進(jìn)行集成。你也可以編寫自己的調(diào)度器。通過 spec:schedulername 參數(shù)指定調(diào)度器的名字,可以為 pod 選擇某個調(diào)度器進(jìn)行調(diào)度。
kube-scheduler在啟動的時(shí)候可以通過 --policy-config-file參數(shù)來指定調(diào)度策略文件,我們可以根據(jù)我們自己的需要來組裝Predicates和Priority函數(shù)。選擇不同的過濾函數(shù)和優(yōu)先級函數(shù)、控制優(yōu)先級函數(shù)的權(quán)重、調(diào)整過濾函數(shù)的順序都會影響調(diào)度過程。
比如下面的 pod 選擇 test-my-scheduler 進(jìn)行調(diào)度,而不是默認(rèn)的 default-scheduler:
預(yù)選的算法可以參考源碼predicates.go(https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/algorithm/predicates/predicates.go):
調(diào)度器的編寫請參考 kubernetes 默認(rèn)調(diào)度器的實(shí)現(xiàn),最核心的內(nèi)容就是讀取 apiserver 中 pod 的值,根據(jù)特定的算法找到合適的 node,然后把調(diào)度結(jié)果會寫到 apiserver。
官方給出的范例: