kube-scheduler簡介node
調度是容器編排的重要環節,須要通過嚴格的監控和控制,現實生產一般對調度有各種限制,譬如某些服務必須在業務獨享的機器上運行,或者從災備的角度考慮儘可能把服務調度到不一樣機器,這些需求在Kubernetes集羣依靠調度組件kube-scheduler知足。nginx
kube-scheduler是Kubernetes中的關鍵模塊,扮演管家的角色聽從一套機制爲Pod提供調度服務,例如基於資源的公平調度、調度Pod到指定節點、或者通訊頻繁的Pod調度到同一節點等。容器調度自己是一件比較複雜的事,由於要確保如下幾個目標:redis
公平性:在調度Pod時須要公平的進行決策,每一個節點都有被分配資源的機會,調度器須要對不一樣節點的使用做出平衡決策。算法
資源高效利用:最大化羣集全部資源的利用率,使有限的CPU、內存等資源服務儘量更多的Pod。centos
效率問題:能快速的完成對大批量Pod的調度工做,在集羣規模擴增的狀況下,依然保證調度過程的性能。api
靈活性:在實際運做中,用戶每每但願Pod的調度策略是可控的,從而處理大量複雜的實際問題。所以平臺要容許多個調度器並行工做,同時支持自定義調度器。緩存
爲達到上述目標,kube-scheduler經過結合Node資源、負載狀況、數據位置等各類因素進行調度判斷,確保在知足場景需求的同時將Pod分配到最優節點。顯然,kube-scheduler影響着Kubernetes集羣的可用性與性能,Pod數量越多集羣的調度能力越重要,尤爲達到了數千級節點數時,優秀的調度能力將顯著提高容器平臺性能。服務器
調度流程架構
kube-scheduler的根本工做任務是根據各類調度算法將Pod綁定(bind)到最合適的工做節點,整個調度流程分爲兩個階段:預選策略(Predicates)和優選策略(Priorities)。app
預選(Predicates):輸入是全部節點,輸出是知足預選條件的節點。kube-scheduler根據預選策略過濾掉不知足策略的Nodes。例如,若是某節點的資源不足或者不知足預選策略的條件如「Node的label必須與Pod的Selector一致」時則沒法經過預選。
優選(Priorities):輸入是預選階段篩選出的節點,優選會根據優先策略爲經過預選的Nodes進行打分排名,選擇得分最高的Node。例如,資源越富裕、負載越小的Node可能具備越高的排名。
通俗點說,調度的過程就是在回答兩個問題:1. 候選有哪些?2. 其中最適合的是哪一個?
值得一提的是,若是在預選階段沒有節點知足條件,Pod會一直處在Pending狀態直到出現知足的節點,在此期間調度器會不斷的進行重試。
到這裏咱們能夠對Pod的整個啓動流程進行總結:
資源管控中心Controller Manager建立新的Pod,將該Pod加入待調度的Pod列表。
kube-scheduler經過API Server提供的接口監聽Pods,獲取待調度pod,通過預選和優選兩個階段對各個Node節點打分排序,爲待調度Pod列表中每一個對象選擇一個最優的Node。
kube-scheduler將Pod與Node的綁定寫入etcd(元數據管理服務)。
節點代理服務kubelet經過API Server監聽到kube-scheduler產生的綁定信息,得到Pod列表,下載Image並啓動容器,而後由kubelet負責拉起Pod。
到此爲止就完成了Pod的調度與啓動。
預選和優選算法介紹
kube-scheduler支持多種預選和優選算法,開發者能夠根據當前場景下的重要要素進行選擇。
預選策略(Predicates)
基於存儲卷數量的判斷
MaxEBSVolumeCount:確保已掛載的EBS存儲卷數量不超過設置的最大值(默認39),調度器會檢查直接或及間接使用這種類型存儲的PVC,累加總數,若是卷數目超過設最大值限制,則不能調度新Pod到這個節點上。
MaxGCEPDVolumeCount:同上,確保已掛載的GCE存儲卷數量不超過預設的最大值(默認16)。
MaxAzureDiskVolumeCount:同上,確保已掛載的Azure存儲卷不超過設置的最大值(默認16)。
基於資源壓力狀態的判斷
CheckNodeMemoryPressure:判斷節點是否已經進入到內存壓力狀態,若是是則只容許調度內存爲0標記的Pod。
CheckNodeDiskPressure:判斷節點是否已經進入到磁盤壓力狀態,若是是,則不能調度新的Pod。
基於卷衝突的判斷
NoDiskConflict:卷衝突判斷,即若是該節點已經掛載了某個卷,其它一樣使用相同卷的Pod將不能再調度到該節點。
NoVolumeZoneConflict:對於給定的某塊區域,判斷若是在此區域的節點上部署Pod是否存在卷衝突。
NoVolumeNodeConflict:對於某個指定節點,檢查若是在此節點上部署Pod是否存在卷衝突。
基於約束關係的判斷
MatchNodeSelector:檢查節點標籤(label)是否匹配Pod指定的nodeSelector,是則經過預選。
MatchInterPodAffinity:根據Pod之間的親和性作判斷。
PodToleratesNodeTaints:排斥性關係,即判斷Pod不容許被調度到哪些節點。這裏涉及到兩個概念Taints(污點)和Toleration(容忍)。Node能夠定義一或多個Taint,Pod能夠定義一或多個Toleration,對於具備某個Taint的節點,只有遇到能容忍它的(即帶有對應Toleration的)Pod,才容許Pod被調度到此節點,從而避免Pod被分配到不合適的節點。
基於適合性的判斷
PodFitsResources:檢查節點是否有足夠資源(如CPU、內存、GPU等)知足Pod的運行需求。
PodFitsHostPorts:檢查Pod容器所需的HostPort是否已被節點上其它容器或服務佔用。若是已被佔用,則禁止Pod調度到該節點。
PodFitsHost:檢查Pod指定的NodeName是否匹配當前節點。
優選策略(Priorities)
優選過程會根據優選策略對每一個候選節點進行打分,最終把Pod調度到分值最高的節點。kube-scheduler用一組優先級函數處理每一個經過預選的節點,每一個函數返回0-10的分數,各個函數有不一樣權重,最終得分是全部優先級函數的加權和,即節點得分的計算公式爲:
優選的優先級函數包括:
LeastRequestedPriority(默認權重1):儘可能將Pod調度到計算資源佔用比較小的Node上,這裏涉及兩種計算資源:內存和CPU。計算公式以下:其中,capacity表示該節點的現有容量,requested表示Pod所請求的容量。
BalancedResourceAllocation(默認權重1):CPU和內存使用率越接近的節點權重越高。該策略均衡了節點CPU和內存的配比,儘可能選擇在部署Pod後各項資源更均衡的機器。該函數不能單獨使用,必須和LeastRequestedPriority同時使用,由於若是請求的資源(CPU或者內存)大於節點的capacity,那麼該節點永遠不會被調度到。計算公式以下:
SelectorSpreadPriority(默認權重1):把屬於同一個Service或者ReplicationController的Pod,儘可能分散在不一樣的節點上,若是指定了區域,則儘可能把Pod分散在該區域的不一樣節點。一般來講節點上已運行的Pod越少,節點分數越高。計算公式以下,是基於節點的計算和基於區域的計算的加權和,其中,maxPriority表明係數,默認爲10,maxCount爲節點最多容許運行的Pod數量,nodeCount爲該節點已經存在的Pod數量,maxCountByZone爲該區域最多容許的Pod數量,zoneCount爲區域內已經運行的Pod數量。
NodeAffinityPriority(默認權重1):儘可能調度到標籤匹配Pod屬性要求的節點,判斷行爲與預選中的MatchNodeSelector類似,將來可能會徹底將其取代。
該函數提供兩種選擇器:requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution。前者是強要求,指定將Pod調度到節點上必須知足的全部規則;後者是弱要求,即儘量調度到知足特定限制的節點,但容許不知足。計算公式以下,在節點知足requiredDuringSchedulingIgnoredDuringExecution後,該節點會累加preferredDuringSchedulingIgnoredDuringExecution中全部知足條件的規則的權值weight_i,CountWeight爲preferredDuringSchedulingIgnoredDuringExecution中全部規則的權值總和:
InterPodAffinityPriority(默認權重1):疊加該節點已調度的每一個Pod的權重,權重可配置文件裏設定,一般親和性越強的Pod權重越高,若是Pod知足要求則將加到權重和,具備最高權重和的節點是最優的。提供兩種選擇器:requiredDuringSchedulingIgnoredDuringExecution(保證所選的主機必須知足全部Pod對主機的規則要求)、preferredDuringSchedulingIgnoredDuringExecution(調度器會盡可能但不保證知足NodeSelector的全部要求)。
計算公式以下,其中,weight_i爲節點上符合親和性的每一個Pod的權重,sumCount爲節點上符合親和性的Pod權重和,maxCount爲節點上全部Pod的權重和,minCount爲節點上最小的Pod權重,maxPriority是係數,默認爲10。
NodePreferAvoidPodsPriority(默認權重10000):避免將ReplicationController或ReplicaSet調度到節點。若是Pod由RC或者RS的Controller調度,則得分爲0,不對最終加權得分產生影響;若是不是,則總分爲100000,意味着覆蓋其餘策略,直接決定最優節點。
TaintTolerationPriority(默認權重1):Pod與Node的排斥性判斷。經過Pod的tolerationList與節點Taint進行匹配,配對失敗的項越少得分越高,相似於Predicates策略中的PodToleratesNodeTaints。計算公式以下:其中,totalTaints表示Taint總個數,intolerableTaints表示配對不成功的個數。
ImageLocalityPriority(默認權重1):儘可能調度到Pod所需鏡像的節點。檢查Node是否存在Pod所需鏡像:若是不存在,返回0分;若是存在,則鏡像越大得分越高。計算公式以下:其中,sumSize表示該節點上存在的Pod所需鏡像大小總和,maxImgSize表示Pod所需鏡像總大小,minImgSize表示Pod所需最小鏡像的尺寸。
EqualPriority(默認權重1):給予全部節點相等權重,通常僅用於測試。
MostRequestedPriority(默認權重1):適用於動態伸縮集羣環境,會優先調度Pod到使用率最高的節點,方便在伸縮集羣時,先騰出空閒機器,從而進行停機處理。
案例演示
下面將經過四個案例來介紹如何在實戰中對以上的調度策略進行選擇。
場景一:調度到基於SSD的節點
某架構使用分佈式緩存Redis,對磁盤讀寫敏感,開發者但願調度到配置SSD的節點,可用預選策略MatchNodeSelector解決,對目標Node設置label,Redis的spec添加nodeSelector。
假設Redis的YAML文件以下:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: redis-master
labels:
name: redis
namespace: default
spec:
replicas: 4
template:
metadata:
labels:
name: redis
spec:
containers:
- name: master
image: 172.16.1.41:5000/redis:3.0.5
resources:
requests:
cpu: 100m
memory: 100Mi
節點transwarp配置了SSD,所以添加「disk=ssd」標籤。
kubectl label node transwarp disk=ssd
經過更新Redis服務的YAML文件字段添加nodeSelector標籤「disk=ssd」來匹配節點transwarp。
kubectl patch deploy redis-master -p '{"spec":{"template":{"spec":{"nodeSelector:"{"disk":"ssd"}}}}}'
場景二:只容許調度到CentOS節點
某系統中有各類異構系統機器,好比SLES、RHEL、CentOS7.二、CentOS7.3等,如今有一個線上任務只能運行在CentOS系統,可是不區分CentOS系統版本。固然咱們可使用MatchNodeSelector來實現匹配,然而若是以這種方式實現須要備註多個標籤,所以建議用NodeAffinityPriority解決。
咱們在該服務的YAML文件中將NodeAffinityPriority選擇器的指定爲requiredDuringSchedulingIgnoredDuringExecution,表示強要求,指定Pod必須被調度到key爲「operation system」,值爲「centos 7.2」或「centos 7.3」的節點。
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: operation system
operator: In
values:
- centos 7.2
- centos 7.3
若是須要優先調度到CentOS 7.2的節點,還能夠增長一個由preferredDuringSchedulingIgnoredDuringExecution決定的調度策略,優先調度到具備「another-node-label-key-system」標籤,且值爲「another-node-label-value-centos7.2」的節點。
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key-system
operator: In
values:
- another-node-label-value-centos7.2
該線上服務的YAML文件具體內容以下:
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: operation system
operator: In
values:
- centos 7.2
- centos 7.3
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key-system
operator: In
values:
- another-node-label-value-centos7.2
containers:
- name: with-node-affinity
image: gcr.io/google_containers/pause:2.0
注意:若是同時指定了nodeSelector和nodeAffinity,目標節點必須同時知足這兩個規則;nodeSelectorTerms能夠指定多個,知足任意一個便可知足;matchExpressions也能夠指定多個,要求必須都知足。
場景三:調度到同一分區
現有公有云組件API服務,須要與認證服務器頻繁通訊,所以要儘量的協同部署在雲提供商的同一個分區中。
該場景的需求是使API服務和認證服務運行在同一分區,轉化到更通用的視角就是把Pod優先調度到更具Pod親和性的節點,故可用優選策略InterPodAffinityPriority解決。
首先咱們對API服務添加標籤「security=cloud」,它的其中一段配置以下:
apiVersion: v1
kind: Pod
metadata:
name: pod-flag
labels:
security: "cloud"
spec:
containers:
- name: nginx
image: nginx
爲了使認證服務分配到相同分區,須要在其YAML文件中添加requiredDuringSchedulingIgnoredDuringExecution字段去親和具備「security=cloud」標籤的Pod,同時添加topologyKey: failure-domain.beta.kubernetes.io/zone字段來限制拓撲域範圍,表示把部署限制在同一個分區。
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- cloud
topologyKey: failure-domain.beta.kubernetes.io/zone
場景四:
私有云服務中,某業務使用GPU進行大規模並行計算。爲保證性能,但願確保該業務對服務器的專屬性,避免將普通業務調度到部署GPU的服務器。
該場景要求Pod避免被調度到指定類型的節點,符合排斥關係,故可用策略PodToleratesNodeTaints解決。
首先先在配有GPU的node1中添加Taints污點(gpu = true),NoSchedule表示只要沒有對應Toleration的Pod就不容許調度到該節點。
kubectl taint nodes nodes1 gpu=true:NoSchedule
同時,經過更新服務的YAML文件,爲須要GPU資源業務的Pods添加Toleration(key: "gpu",value: "true"),表示容忍GPU服務器的Taints,這樣就能夠把指定業務調度到GPU服務器,同時把普通服務調度到其餘節點。
apiVersion: v1
kind: Pod
metadata:
generateName: redis-
labels:
app: redis
namespace: default
spec:
containers:
- image: 172.16.1.41:5000/redis
imagePullPolicy: Always
name: redis
schedulerName: default-scheduler
tolerations:
- effect: NoSchedule
key: gpu
operator: Equal
value: true
這裏須要提一下,tolerations裏的effect字段表示處理排斥的策略,支持三種調度規則:NoSchedule表示不容許調度,已調度的不影響;PreferNoSchedule表示儘可能不調度;NoExecute表示不容許調度,已運行的Pod將在以後刪除。
總結與展望
目前kube-scheduler已經提供了豐富的調度策略可供使用,通常狀況下,使用kube-scheduler的默認調度策略就能知足大部分需求,而且其插件化的形式也方便於用戶進行定製與二次開發。將來,咱們會在此基礎上對其進一步優化:包括增長cache以減小predict和prioritize階段的重複計算,已在TOS 1.9中實現;經過scheduler-extender擴展來針對local Volume、GPU等資源實現更合理調度;適配custom-metrics-server的API來實現實時調度,使得每一個節點的資源能更好的被利用。