楠木軒

Kubernetes 是如何調度的?

由 太史憶秋 發佈於 科技

自互聯網出現以來 ,雲計算的概念已經提出了有 50 年。從1957 年,John McCarthy 將計算機中的分時共享概念設計成了一種工具。從此以後,這個概念的名字經歷過數次變化:從“服務中心(service bureau)”到應用服務提供商,到互聯網即服務,到雲計算,再到軟件定義的數據中心。

一直以來基礎設施是雲計算的基礎核心,基礎設施服務(Infrastructure as a Service,IaaS)將IT基礎設施資源(計算、網絡與存儲)以一種彈性的服務方式對外提供。近十餘年來隨着雲計算不斷髮展與落地,雲基礎設施技術架構也在不斷往前演進,從傳統虛擬化、基礎設施資源管理走向軟件定義架構。隨着近5年來以容器和微服務為代表的雲原生技術興起,雲基礎設施架構需要演進以全面擁抱雲原生技術。

以容器、容器編排、微服務、服務網格為代表的雲原生技術正在日益體現出期在雲計算領域的非凡價值,眾所周知,容器編排中 Kubernetes 已經成為容器編排領域的事實標準。

kubernetes 可以提供所需的編排和管理功能,以便針對工作負載大規模部署容器。藉助 Kubernetes 編排功能,可以快速的構建跨多個容器的應用服務、跨集羣調度、擴展這些容器,並長期持續管理這些容器的健康狀況。在 Kubernetes 中,調度是指將 Pod 放置到合適的 Node上,然後對應 Node 上的 Kubelet才能夠運行這些 pod。

那麼kubernetes 是如何進行調度的呢?我們來一起看下。

Kubernetes 是如何調度的?

kube-scheduler 是 Kubernetes 集羣的默認調度器,並且是集羣 控制面的一部分。同時 kube-scheduler 在設計上是允許你自己寫一個調度組件並替換原有的 kube-scheduler。

對每一個新創建的 Pod 或者是未被調度的 Pod,kube-scheduler 會選擇一個最優的 Node 去運行這個 Pod。那麼kube-scheduler 是如何選擇最優的 Node 呢?

kube-scheduler監聽apiserver的/api/pod/,當發現集羣中有未得到調度的pod(即PodSpec.NodeName為空)時,會查詢集羣各node的信息,經過Predicates(過濾)、Priorities(優選器),得到最適合該pod運行的node後,再向apiserver發送請求,將該容器綁定到選中的node上。Kubernetes Scheduler 提供的調度流程分三步:

過濾, 遍歷nodelist,選擇出符合要求的候選節點,Kubernetes內置了多種預選規則供用户選擇。

打分, 在選擇出符合要求的候選節點中,採用優選規則計算出每個節點的積分,最後選擇得分最高的。

綁定, 選出其中得分最高的 Node 來運行 Pod。之後,調度器將這個調度決定通知給 kube-apiserver,這個過程叫做綁定。

整個過程,如圖所示:

預選的算法可以參考源碼predicates.go(

const (

常用的預選策略:

PodFitsHostPorts:如果 Pod 中定義了 hostPort 屬性,那麼需要先檢查這個指定端口是否 已經被 Node 上其他服務佔用了。

PodFitsHost:若 pod 對象擁有 hostname 屬性,則檢查 Node 名稱字符串與此屬性是否匹配。

PodFitsResources:檢查 Node 上是否有足夠的資源(如,cpu 和內存)來滿足 pod 的資源請求。

PodMatchNodeSelector:檢查 Node 的 標籤 是否能匹配 Pod 屬性上 Node 的 標籤 值。

NoVolumeZoneConflict:檢測 pod 請求的 Volumes 在 Node 上是否可用,因為某些存儲卷存在區域調度約束。

NoDiskConflict:檢查 Pod 對象請求的存儲卷在 Node 上是否可用,若不存在衝突則通過檢查。

MaxCSIVolumeCount:檢查 Node 上已經掛載的 CSI 存儲卷數量是否超過了指定的最大值。

CheckNodeMemoryPressure:如果 Node 上報了內存資源壓力過大,而且沒有配置異常,那麼 Pod 將不會被調度到這個 Node 上。

CheckNodePIDPressure:如果 Node 上報了 PID 資源壓力過大,而且沒有配置異常,那麼 Pod 將不會被調度到這個 Node 上。

CheckNodeDiskPressure:如果 Node 上報了磁盤資源壓力過大(文件系統滿了或者將近滿了), 而且配置異常,那麼 Pod 將不會被調度到這個 Node 上。

CheckNodeCondition:Node 可以上報其自身的狀態,如磁盤、網絡不可用,表明 kubelet 未準備好運行 pod。如果 Node 被設置成這種狀態,那麼 pod 將不會被調度到這個 Node 上。

PodToleratesNodeTaints:檢查 pod 屬性上的 tolerations 能否容忍 Node 的 taints。

CheckVolumeBinding:檢查 Node 上已經綁定的和未綁定的 PVCs 能否滿足 Pod 對象的存儲卷需求。

打分策略如下:

SelectorSpreadPriority:儘量將歸屬於同一個 Service、StatefulSet 或 ReplicaSet 的 Pod 資源分散到不同的 Node 上。

InterPodAffinityPriority:遍歷 Pod 對象的親和性條目,並將那些能夠匹配到給定 Node 的條目的權重相加,結果值越大的 Node 得分越高。

LeastRequestedPriority:空閒資源比例越高的 Node 得分越高。換句話説,Node 上的 Pod 越多,並且資源被佔用的越多,那麼這個 Node 的得分就會越少。

MostRequestedPriority:空閒資源比例越低的 Node 得分越高。這個調度策略將會把你所有的工作負載(Pod)調度到儘量少的 Node 上。

RequestedToCapacityRatioPriority:為 Node 上每個資源佔用比例設定得分值,給資源打分函數在打分時使用。

BalancedResourceAllocation:優選那些使得資源利用率更為均衡的節點。

NodePreferAvoidPodsPriority:這個策略將根據 Node 的註解信息中是否含有

NodeAffinityPriority:基於 Pod 屬性中

TaintTolerationPriority:基於 Pod 中對每個 Node 上污點容忍程度進行優先級評估,這個策略能夠調整待選 Node 的排名。

ImageLocalityPriority:Node 上已經擁有 Pod 需要的 容器鏡像 的 Node 會有較高的優先級。

ServiceSpreadingPriority:這個調度策略的主要目的是確保將歸屬於同一個 Service 的 Pod 調度到不同的 Node 上。如果 Node 上 沒有歸屬於同一個 Service 的 Pod,這個策略更傾向於將 Pod 調度到這類 Node 上。最終的目的:即使在一個 Node 宕機之後 Service 也具有很強容災能力。

CalculateAntiAffinityPriorityMap:這個策略主要是用來實現pod反親和。

EqualPriorityMap:將所有的 Node 設置成相同的權重為 1。

自定義調度器

除了 kubernetes 自帶的調度器,考慮到實際環境中的各種複雜情況,kubernetes 的調度器採用插件化的形式實現,可以方便用户進行定製或者二次開發,我們可以自定義一個調度器並以插件形式和 kubernetes 進行集成。你也可以編寫自己的調度器。通過 spec:schedulername 參數指定調度器的名字,可以為 pod 選擇某個調度器進行調度。

kube-scheduler在啓動的時候可以通過--policy-config-file參數來指定調度策略文件,我們可以根據我們自己的需要來組裝PredicatesPriority函數。選擇不同的過濾函數和優先級函數、控制優先級函數的權重、調整過濾函數的順序都會影響調度過程。

比如下面的 pod 選擇 test-my-scheduler 進行調度,而不是默認的 default-scheduler:

apiVersion: v1

調度器的編寫請參考 kubernetes 默認調度器的實現,最核心的內容就是讀取 apiserver 中 pod 的值,根據特定的算法找到合適的 node,然後把調度結果會寫到 apiserver。

官方給出的範例:

#!/bin/bash

☞中國 AI 應用元年來了!

☞新基建東風下,開發者這樣抓住工業互聯網風口!

☞15 歲黑進系統,發挑釁郵件意外獲 Offer,不惑之年捐出全部財產,Twitter CEO 太牛了!

☞避坑!使用 Kubernetes 最易犯的 10 個錯誤

☞必讀!53個Python經典面試題詳解

☞贈書 | 1月以來 Tether 增發47億 USDT,美元都去哪兒了?