從零開始入門 K8s | 調度器的調度流程和算法介紹

點擊這裏,查看調度算法及如何配置調度器等重要內容node

調度流程

調度流程概覽

Kubernetes 做爲當下最主流的容器自動化運維平臺,做爲 K8s 的容器編排的核心組件 kube-scheduler 將是我今天介紹的主角,以下介紹的版本都是以 release-1.16 爲基礎,下圖是 kube-scheduler 的主要幾大組件:
1.png算法

Policy

Scheduler 的調度策略啓動配置目前支持三種方式,配置文件 / 命令行參數 / ConfigMap。調度策略能夠配置指定調度主流程中要用哪些過濾器 (Predicates)、打分器 (Priorities) 、外部擴展的調度器 (Extenders),以及最新支持的 SchedulerFramwork 的自定義擴展點 (Plugins)。api

Informer

Scheduler 在啓動的時候經過 K8s 的 informer 機制以 List+Watch 從 kube-apiserver 獲取調度須要的數據例如:Pods、Nodes、Persistant Volume(PV), Persistant Volume Claim(PVC) 等等,並將這些數據作必定的預處理做爲調度器的的 Cache。緩存

調度流水線

經過Informer 將須要調度的 Pod 插入 Queue 中,Pipeline 會循環從 Queue Pop 等待調度的 Pod 放入 Pipeline 執行。架構

調度流水線 (Schedule Pipeline) 主要有三個階段:Scheduler Thread,Wait Thread,Bind Thread。運維

  • Scheduler Thread 階段: 從如上的架構圖能夠看到 Schduler Thread 會經歷 Pre Filter -> Filter -> Post Filter-> Score -> Reserve,能夠簡單理解爲 Filter -> Score -> Reserve。

Filter 階段用於選擇符合 Pod Spec 描述的 Nodes;Score 階段用於從 Filter 事後的 Nodes 進行打分和排序;Reserve 階段將 Pod 跟排序後的最優 Node 的 NodeCache 中,表示這個 Pod 已經分配到這個 Node 上, 讓下一個等待調度的 Pod 對這個 Node 進行 Filter 和 Score 的時候能看到剛纔分配的 Pod。異步

  • Wait Thread 階段:這個階段能夠用來等待 Pod 關聯的資源的 Ready 等待,例如等待 PVC 的 PV 建立成功,或者 Gang 調度中等待關聯的 Pod 調度成功等等;
  • Bind Thread 階段: 用於將 Pod 和 Node 的關聯持久化 Kube APIServer。

整個調度流水線只有在 Scheduler Thread 階段是串行的一個 Pod 一個 Pod 的進行調度,在 Wait 和 Bind 階段 Pod 都是異步並行執行。優化

調度詳細流程

解說完 kube-scheduler 的幾大部件的做用和關聯關係以後,接下來深刻理解下 Scheduler Pipeline 的具體工做原理,以下是 kube-scheduler 的詳細流程圖,先解說調度隊列:
2.pngspa

SchedulingQueue 有三個子隊列 activeQ、backoffQ、unschedulableQ。插件

Scheduler 啓動的時候全部等待被調度的 Pod 都會進入 activieQ,activeQ 會按照 Pod 的 priority 進行排序,Scheduler Pipepline 會從 activeQ 獲取一個 Pod 進行 Pipeline 執行調度流程,當調度失敗以後會直接根據狀況選擇進入 unschedulableQ 或者 backoffQ,若是在當前 Pod 調度期間 Node Cache、Pod Cache 等 Scheduler Cache 有變化就進入 backoffQ,不然進入 unschedulableQ。

unschedulableQ 會按期較長時間(例如 60 秒)刷入 activeQ 或者 backoffQ,或者在 Scheduler Cache 發生變化的時候觸發關聯的 Pod 刷入 activeQ 或者 backoffQ;backoffQ 會以 backoff 機制相比 unschedulableQ 比較快地讓待調度的 Pod 進入 activeQ 進行從新調度。

接着詳細介紹 Scheduler Thread 階段,在 Scheduler Pipeline 拿到一個等待調度的 Pod,會從 NodeCache 裏面拿到相關的 Node 執行 Filter 邏輯匹配,這從 NodeCache 遍歷 Node 的過程有一個空間算法上的優化,簡單能夠歸納爲在避免過濾全部節點的同時考慮了調度的容災取樣調度

具體的優化算法邏輯(有興趣的同窗能夠看 node_tree.go 的 Next 方法):在 NodeCache 中,Node 是按照 zone 進行分堆。在 filter 階段的時候,爲會 NodeCache 維護一個 zondeIndex,每 Pop 一個 Node 進行過濾,zoneIndex 日後挪一個位置,而後從該 zone 的 node 列表中取一個 node 出來。

能夠看到上圖縱軸有一個 nodeIndex,每次也會自增。若是當前 zone 的節點無數據,那就會從下一個 zone 中拿數據。大概的流程就是 zoneIndex 從左向右,nodeIndex 從上到下,保證拿到的 Node 節點是按照 zone 打散,從而實現避免過濾全部節點的同時考慮了節點的 az 均衡部署。(最新 release-v.1.17 的版本已經取消這種算法,爲何取消應該是沒有考慮 Pod 的 prefer 和 node 的 prefer,沒有實現 Pod 的 Spec 要求)

取樣調度裏面的取樣規模這裏簡單介紹一下,默認的取樣比率公式 = Max (5, 50 - 集羣的 node 數 / 125),取樣規模 = Max (100, 集羣 Node 數*取樣比率)。

這裏舉個例子:節點規模爲 3000 個節點,那麼取樣比例 = Max (5, 50 - 3000/125) = 26%,那麼取樣規模 = Max (100, 3000* 0.26) = 780,在調度流水線裏面,Filter 只要匹配到 780 個候選節點,就能夠中止 Filter 流程,走到 Score 階段。

Score 階段依據 Policy 配置的算分插件,進行排序,分數最高的節點做爲 SelectHost。接着將這個 Pod 分配到這個 Node 上,這個過程叫作 Reserver 階段能夠稱爲帳本預佔。預佔的過程修改 Pod 在 PodCache 的狀態爲 Assumed 的狀態(處於內存態)。

調度過程涉及到 Pod 狀態機的生命週期,這裏簡單介紹下 Pod 的幾個主要狀態:Initial(虛擬狀態)->Assumed(Reserver)->Added->Deleted(虛擬狀態); 當經過 Informer watch 到 Pod 數據已經肯定分配到這個節點的時候,纔會把 Pod 的狀態變成 Added。選中完節點在 Bind 的時候,有可能會 Bind 失敗,在 Bind 失敗的時候會作回退,就是把預佔用的帳本作 Assumed 的數據退回 Initial,也就是把 Assumed 狀態擦除,從 Node 裏面把 Pod 帳本清除。

若是 Bind 失敗,會把 Pod 從新丟回到 unschedulableQ 隊列裏面。在調度隊列中,什麼狀況下 Pod 會到 backoffQ 中呢?這是一個很細節的點。若是在這麼一個調度週期裏面,Cache 發生了變化,會把 Pod 放到 backoffQ 裏面。在 backoffQ 裏面等待的時間會比在 unschedulableQ 裏面時間更短,backoffQ 裏有一個降級策略,是 2 的指數次冪降級。假設重試第一次爲 1s,那第二次就是 2s,第三次就是 4s,第四次就是 8s,最大到 10s。

調度算法實現

關鍵字:存儲 緩存 運維 Kubernetes 算法 Cloud Native 容災 調度Perl 容器

相關文章
相關標籤/搜索