調度器功能
默認調度器的主要職責,就是為一個新創建出來的Pod尋找一個最合適的節點(Node)
調度器對一個 Pod 調度成功,實際上就是將它的 spec.nodeName 字段填上調度結果的節點名字
預選節點
從集群所有的節點中,根據調度算法挑選出所有可以運行該 Pod 的節點默認調度器會首先調用一組叫作 Predicate 的調度算法,來檢查每個Node
優選節點
從預選的結果中,再根據調度算法挑選一個最符合條件的節點作為最終結果.再調用一組叫作 Priority 的調度算法,來給上一步得到的結果里的每個Node打分.最終的調度結果,就是得分最高的那個Node
調度器實現機制
調度器實現的核心就是由兩個相互獨立運行的控制循環
第一個控制循環,我們可以稱之為Informer Path.當一個待調度Pod(它的nodeName字段是空的)被創建出來之后,調度器就會通過Pod Informer的Handler,將這個待調度Pod添加進調度隊列.Kubernetes 的調度隊列是一個 PriorityQueue(優先級隊列)
第二個控制循環,是調度器負責Pod調度的主循環,我們可以稱之為 Scheduling Path.scheduling Path的主要邏輯,就是不斷地從調度隊列里出隊一個Pod.然后,調用 Predicates 算法進行“過濾”.這一步“過濾”得到的一組 Node,就是所有可以運行這個 Pod 的宿主機列表.當然Predicates算法需要的Node信息,都是從Scheduler Cache 里直接拿到的,這是調度器保證算法執行效率的主要手段之一.接下來,調度器就會再調用 Priorities 算法為上述列表里的 Node 打分,分數從0到10.得分最高的 Node,就會作為這次調度的結果.
Kubernetes 的默認調度器還要負責對調度器緩存(即:scheduler cache)進行更新.事實上Kubernetes 調度部分進行性能優化的一個最根本原則,就是盡最大可能將集群信息 Cache 化,以便從根本上提高Predicate和 Priority調度算法的執行效率
為了不在關鍵調度路徑里遠程訪問APIServer,Kubernetes的默認調度器在Bind階段,只會更新Scheduler Cache里的Pod和Node的信息.這種基於“樂觀”假設的 API 對象更新方式,在Kubernetes里被稱作Assume
默認調度器可擴展性設計
默認調度器的可擴展機制,在 Kubernetes里面叫作 Scheduler Framework.這個設計的主要目的,就是在調度器生命周期的各個關鍵點上,為用戶暴露出可以進行擴展和實現的接口,從而實現由用戶自定義調度器的能力
每一個綠色的箭頭都是一個可以插入自定義邏輯的接口.比如,上面的 Queue 部分,就意味着你可以在這一部分提供一個自己的調度隊列的實現,從而控制每個Pod 開始被調度(出隊)的時機
Predicates部分,則意味着你可以提供自己的過濾算法實現,根據自己的需求,來決定選擇哪些機器
上述這些可插拔式邏輯,都是標准的Go語言插件機制(Go plugin 機制),也就是說,你需要在編譯的時候選擇把哪些插件編譯進去
調度器的默認調度策略
Predicates預選機制()
GeneralPredicates策略
PodFitsResources 計算的就是宿主機的CPU和內存資源等是否夠用.PodFitsResources檢查的只是Pod的requests字段
PodFitsHost 檢查的是宿主機的名字是否跟Pod的spec.nodeName一致
PodFitsHostPorts 檢查的是Pod申請的宿主機端口(spec.nodePort)是不是跟已經被使用的端口有沖突
PodMatchNodeSelector檢查的是,Pod的nodeSelector或者nodeAffinity指定的節點,是否與待考察節點匹配,等等
kubelet在啟動Pod前,會執行一個Admit操作來進行二次確認.這里二次確認的規則,就是執行一遍 GeneralPredicates
Volumes相關的策略
NoDiskConflict 檢查的條件,是多個Pod聲明掛載的持久化Volume是否有沖突
maxPDVolumeCountPredicate檢查的條件,則是一個節點上某種類型的持久化Volume是不是已經超過了一定數目.如果是的話,那么聲明使用該類型持久化Volume的 Pod 就不能再調度到這個節點了
VolumeZonePredicate則是檢查持久化Volume的Zone(高可用域)標簽,是否與待考察節點的 Zone 標簽相匹配
VolumeBindingPredicate的規則 它負責檢查的,是該Pod對應的PV的nodeAffinity字段,是否跟某個節點的標簽相匹配
在Predicates階段Kubernetes就必須能夠根據Pod的Volume屬性來進行調度
宿主機相關的過濾策略
PodToleratesNodeTaints負責檢查的就是我們前面經常用到的Node的“污點”機制.只有當Pod的Toleration字段與Node的Taint字段能夠匹配的時候,這個Pod才能被調度到該節點上
NodeMemoryPressurePredicate檢查的是當前節點的內存是不是已經不夠充足,如果是的話,那么待調度Pod就不能被調度到該節點上
pod相關過濾策略
跟GeneralPredicates大多數是重合的.而比較特殊的是PodAffinityPredicate.這個規則的作用是檢查待調度Pod與Node上的已有Pod之間的親密(affinity)和反親密(anti-affinity)關系
在為每個Node執行Predicates時調度器會按照固定的順序來進行檢查.這個順序是按照Predicates本身的含義來確定的.比如宿主機相關的Predicates會被放在相對靠前的位置進行檢查.要不然的話在一台資源已經嚴重不足的宿主機上,上來就開始計算PodAffinityPredicate是沒有實際意義的
Priorities優選機制
Priorities階段的工作就是為這些節點打分.這里打分的范圍是 0-10 分,得分最高的節點就是最后被Pod綁定的最佳節點
Priorities里最常用到的一個打分規則
LeastRequestedPriority=score = (cpu((capacity-sum(requested))10/capacity) + memory((capacity-sum(requested))10/capacity))/2
這個算法實際上就是在選擇空閑資源(CPU 和 Memory)最多的宿主機
BalancedResourceAllocation=score =10-variance(cpuFraction,memoryFraction,volumeFraction)*10
選擇的其實是調度完成所有節點里各種資源分配最均衡的那個節點,從而避免一個節點上CPU被大量分配而 Memory大量剩余情況
Priorities里有一個叫ImageLocalityPriority的策略,如果待調度Pod需要使用的鏡像很大並且已經存在於某些 Node上那么這些Node的得分就高
在實際的執行過程中,調度器里關於集群和Po 的信息都已經緩存化,所以這些算法的執行過程還是比較快的.Kubernetes調度器里其實還有一些默認不會開啟的策略.可以通過為 kube-scheduler指定一個配置文件或者創建一個ConfigMap ,來配置哪些規則需要開啟、哪些規則需要關閉.並且可以通過為Priorities設置權重來控制調度器的調度行為
Predicates和Priorities的區別
Predicates是為Pod過濾不符合條件的節點 返回的結果是一個滿足Pod運行條件的節點列表
Priorities是為Predicates后的節點列表中的節點打分 返回的結果是為這些節點打得0-10的分數
調度實現方式
1.手動給Pod指定節點名稱(nodeName屬性)
2.podAffinity規則
讓某些Pod分布在同一組節點上或者是分布在同一個節點上 使用topologyKey字段的值來給節點進行分組 每個組可以包含多個節點也可以只有一個節點