[译] kubernetes:kube-scheduler 调度器代码结构概述
本文翻譯自 https://github.com/kubernetes/community/blob/master/contributors/devel/sig-scheduling/scheduling_code_hierarchy_overview.md
譯者:胡云 Troy
調(diào)度器代碼層次結構概述
介紹
調(diào)度器監(jiān)視新創(chuàng)建的還沒有分配節(jié)點的 Pod。當發(fā)現(xiàn)這樣的 Pod 后,調(diào)度器將 Pod 調(diào)度到最適合它的節(jié)點。一般來說,調(diào)度是計算機科學中一個相當廣泛的領域,它考慮了各種各樣的約束和限制。調(diào)度器的每個工作負載可能需要不同的方法來實現(xiàn)最佳調(diào)度結果。Kubernetes 項目提供的 kube-scheduler 調(diào)度器的目標是以簡單為代價提供高吞吐量。為了幫助構建調(diào)度器(默認或者定制化)和共享調(diào)度邏輯,kube-scheduler 實現(xiàn)了 調(diào)度框架。該框架沒有提供構建新調(diào)度器的所有部分。組裝一個功能齊全的單元仍然需要隊列、緩存、調(diào)度算法和其他構建元素。本文檔旨在描述所有單獨的部分是如何組合在一起,以及它們在整個體系結構中的作用,以便開發(fā)人員能夠快速了解調(diào)度器代碼。
調(diào)度 Pod
默認的調(diào)度器實例運行無限期的循環(huán),該循環(huán)(每次有 Pod 時)負責調(diào)用調(diào)度邏輯,確保 Pod 分配或重新排隊以供后續(xù)處理。每個循環(huán)由一個阻塞調(diào)度周期和一個非阻塞綁定周期組成。調(diào)度周期負責運行調(diào)度算法,選擇最合適的節(jié)點分配給 Pod。綁定周期確保 kube-apiserver 及時接收分配給 Pod 的節(jié)點。一個 Pod 可以立即綁定,或者在群調(diào)度中,等所有同級 Pod 分配節(jié)點之后再綁定。
圖片來源 調(diào)度框架
調(diào)度周期
每個周期遵循以下步驟:
- 獲取下一個調(diào)度的 Pod
- 根據(jù)提供的調(diào)度算法調(diào)度 Pod
- 如果調(diào)度 Pod 時出現(xiàn)
FitError錯誤,調(diào)度器將運行PostFilterPlugin搶占插件(如果該插件已注冊),該插件將指定一個可以運行 Pod 的節(jié)點。如果搶占成功,讓當前 Pod 知道分配的節(jié)點。調(diào)度器將處理錯誤,獲取下一個 Pod 并重新開始調(diào)度。 - 如果調(diào)度算法找到了合適的節(jié)點,則將 Pod 存儲到調(diào)度器緩存中(
AssumePod操作),然后按順序運行Reserve和Permit擴展點插件。任何插件運行失敗將結束當前調(diào)度周期,增加相關的指標,調(diào)度器的Error handler將處理調(diào)度錯誤。 - 成功運行所有擴展點后,繼續(xù)綁定循環(huán)。在執(zhí)行綁定循環(huán)的同時,調(diào)度周期開始處理下一個調(diào)度的 Pod(如果有的話)。
綁定周期
按相同順序運行以下四個步驟:
- 從
Permit擴展點調(diào)用插件的 WaitOnPermit (內(nèi)部 API)。擴展點的一些插件會發(fā)送操作請求去等待相應的條件(例如,等待額外的可用資源或者一組中的所有 Pod 被分配)。WaitOnPermit等待條件滿足直到超時。 - 調(diào)用 PreBind 擴展點的插件
- 調(diào)用 Bind 擴展點的插件
- 調(diào)用 PostBind 擴展點的插件
任何擴展點執(zhí)行失敗將調(diào)用所有 Reserve 插件的 Unreserve 操作(例如,為一群 Pod 分配空閑資源)。
配置和組裝調(diào)度器
調(diào)度器代碼庫分散在不同的地方:
- cmd/kube-scheduler/app:控制器代碼的位置以及 CLI 參數(shù)的定義(遵守所有 Kubernetes 控制器的標準設定)
- pkg/scheduler:默認調(diào)度器代碼庫的根目錄
- pkg/scheduler/core:默認調(diào)度算法的位置
- pkg/scheduler/framework:調(diào)度框架和插件
- pkg/scheduler/internal:緩存,隊列和其它元素的實現(xiàn)
- staging/src/k8s.io/kube-scheduler:ComponentConfig API 類型的位置
- test/e2e/scheduling:調(diào)度 e2e
- test/integration/scheduler:調(diào)度集成測試
- test/integration/scheduler_perf:調(diào)度性能基準
初始啟動配置
cmd/kube-scheduler/app 下的代碼負責收集調(diào)度器配置和調(diào)度器初始化邏輯,它是 kube-scheduler 作為 Kubernetes 控制面運行的一部分。代碼包括:
- 初始化 命令行選項(以及默認的
ComponentConfig) 和 驗證 - 初始化 指標,健康檢查 和 其它 handlers
- KubeSchedulerConfiguration 的讀取和默認配置
- 通過插件構建
registry(in-tree, out-of-tree) - 多種選項初始化調(diào)度器,例如 profiles,算法源,pod back off,等等。
- 調(diào)用 LogOrWriteConfig,用于記錄最終調(diào)度程序配置以進行調(diào)試
- 運行之前,
/configz已注冊,事件廣播程序已啟動,*選舉已啟動,server(包含所有配置的 handlers 和 informers)已啟動。
初始化之后,調(diào)度器開始運行。
更詳細地說,Setup 函數(shù)完成了調(diào)度器核心流程的初始化。首先,Setup 驗證傳遞的選項(NewSchedulerCommand() 中添加的 flags 直接設置在此選項結構的字段上)。如果傳遞的選項沒有引發(fā)任何錯誤,那么它將調(diào)用 opts.Config(),用于設置最終的內(nèi)部配置,包括安全服務、*選舉、客戶端,并開始解析與算法源相關的選項(比如,加載配置文件和初始化空 profiles,以及處理不推薦使用的選項像策略配置)。接下來,調(diào)用 c.Complete() 填充配置 Config 中的空值。此時,創(chuàng)建一個空 registry 注冊 out-of-tree 插件,在 registry 中為每個插件的 New 函數(shù)添加條目。Registry 只是插件名稱到插件工廠函數(shù)的映射。對于默認調(diào)度器,注冊 registry 這一步什么都不做(因為 cmd/kube-chuler/scheduler.go 中的 main 函數(shù)不向 NewSchedulerCommand() 傳遞任何信息)。這意味著默認的插件在 scheduler.New() 中初始化。
初始化是在調(diào)度框架之外執(zhí)行的,使用框架的用戶可以以不同的方式初始化環(huán)境來滿足自身的需求。例如,模擬器可以通過 informer 注入自身需要的對象。或者自定義的插件可以替換默認的插件。調(diào)度框架的已知使用者:
- cluster-autoscaler
- cluster-capacity
組裝調(diào)度器
默認調(diào)度器實現(xiàn)的目錄在 pkg/scheduler,調(diào)度器的各種元素在這里初始化并組合在一起:
- 默認調(diào)度選項,例如
node percentage, 初始化和最大backoff,profiles - 調(diào)度器緩存和隊列
- 實例化調(diào)度的
profiles以定制框架,每個profile可以更好的安置 Pod(每個 profile 定義自身使用的插件集合) -
Handler函數(shù)用于獲取下一個調(diào)度的 Pod(NextPod)和處理錯誤(Error)
在創(chuàng)建調(diào)度器實例的過程中,將執(zhí)行以下步驟:
- 初始化調(diào)度器 緩存
-
合并 帶插件的
in-tree和out-of-tree注冊表 -
Metrics已注冊 - 配置器 構建調(diào)度器實例(連接緩存,插件注冊表,調(diào)度算法和其它元素)
-
注冊
Event handlers以允許調(diào)度器對 PV、PVC、服務和其它與調(diào)度相關的資源的更新做出反應(最終,每個插件都將定義一組事件,并對其作出反應,更詳細的可參考 kubernetes/kubernetes#100347)。
下圖表明了初始化后各個元素是如何連接在一起的。Event handlers 確保 Pod 在 調(diào)度隊列中排隊,緩存隨 Pod 和節(jié)點的更新而更新(提供最新的快照 snapshot)。調(diào)度框架有對應的調(diào)度算法和綁定周期(每個框架實例有自己的 profile)。
調(diào)度框架
調(diào)度器的框架代碼目前位于 pkg/scheduler/framework 下。它包含 各種插件,負責過濾和評分節(jié)點(以及其他)。常常用作調(diào)度算法的構建模塊。
當 插件初始化 后,它會傳遞一個 框架 handler,該框架 handler 提供訪問和/或操作 pod、節(jié)點、clientset、事件記錄器和每個插件實現(xiàn)其功能所需的其他 handler 的接口。
調(diào)度緩存
緩存負責記錄集群的最新狀態(tài)。保存節(jié)點和 assumed Pod 以及 Pod 和 images 的狀態(tài)。緩存提供了協(xié)調(diào) Pod 和節(jié)點對象(調(diào)用 event handlers)的方法,使集群的狀態(tài)保持最新。允許在每個調(diào)度周期開始時使用最新狀態(tài)(在運行調(diào)度算法時固定集群狀態(tài))更新集群的快照。
緩存還允許運行假定的操作,該操作將 Pod 臨時存儲在緩存中,使得 Pod 看起來像已經(jīng)在快照的所有消費者的指定節(jié)點上運行那樣。假定操作忽視了 kube-apiserver 和 Pod 實際更新的時間,從而增加調(diào)度器的吞吐量。
以下操作使用假定的 Pod 進行操作:
-
AssumePod:用于通知調(diào)度算法找到可行的節(jié)點,以便在當前 Pod 進入綁定周期時可以調(diào)度下一個 Pod -
FinishBinding:用于發(fā)出綁定完成的信號,以便可以將 Pod 從假定 Pod 列表中刪除 -
ForgetPod:從假定的 Pod 列表中刪除 Pod,用于綁定周期中未能成功處理 Pod 的情況(例如,Reserve,Permit,PreBind或者Bind評估)
緩存跟蹤以下三個指標:
-
scheduler_cache_size_assumed_pods:在假定 Pod 列表中的 Pod 數(shù)量 -
scheduler_cache_size_pods:在緩存中的 Pod 數(shù)量 -
scheduler_cache_size_nodes:在緩存中的節(jié)點數(shù)量
快照
快照 捕獲集群的狀態(tài),其中包含集群中所有節(jié)點和每個節(jié)點上對象的信息。即節(jié)點對象、分配在每個節(jié)點上的 Pod、每個節(jié)點上所有 Pod 的請求資源、節(jié)點的可分配資源、拉取的鏡像以及做出調(diào)度決策所需的其他信息。每次調(diào)度 Pod 時,都會捕獲集群當前狀態(tài)的快照。這樣是為了避免在處理插件時更改 Pod 或節(jié)點時導致的數(shù)據(jù)不一致,因為一些插件可能會獲得不同的集群狀態(tài)。
配置器
配置器 通過將插件、緩存、隊列、handlers 和其他元素連接在一起來構建調(diào)度器實例。每個 profile 都使用自己的框架(所有框架共享 informers,event recorders 等)進行 初始化。
也可以讓配置器根據(jù) 策略文件 創(chuàng)建實例。不過,這種方法已被棄用,最終將從配置中刪除。只保留調(diào)度器配置作為提供給配置器配置的唯一方式。
默認調(diào)度算法
代碼庫定義了 ScheduleAlgorithm 接口。任何該接口的實現(xiàn)都可以用作調(diào)度算法。這里有兩種方法:
-
Schedule:負責使用從PreFilter到NormalizeScore擴展點的插件來調(diào)度 Pod,提供包含調(diào)度決策(最合適的節(jié)點)和附帶信息的 ScheduleResult,其中附帶信息包括評估了多少節(jié)點以及發(fā)現(xiàn)有多少節(jié)點可用于調(diào)度。 -
Extenders: 當前僅用于測試
默認算法實現(xiàn)的每個周期包括:
- 從調(diào)度緩存中獲取 當前快照
-
過濾掉所有無法調(diào)度 Pod 的節(jié)點
- 運行 PreFilter 插件(預處理階段,例如計算 Pod 親和性關系)
- 并行運算
Filter 插件:過濾掉不滿足 Pod 限制條件(例如資源,節(jié)點親和性等)的節(jié)點,包括運行Filter 擴展器 - 運行
PostFilter 插件如果沒有節(jié)點滿足要調(diào)度的 Pod
- 在 Pod 至少有兩個可行節(jié)點可以調(diào)度的情況下,運行 scoring 插件:
- 運行 PreScore 插件(預處理階段)
- 并行運行 Score 插件:每個節(jié)點都有一個分數(shù)向量(每個坐標對應一個插件)
- 運行 NormalizeScore 插件:給所有插件打分,間隔為 [0, 100]
- 計算每個節(jié)點的 權重分數(shù) (每個分數(shù)插件都可以分配一個權重,指示其分數(shù)在多大程度上優(yōu)于其他插件)
- 運行 打分擴展器,并且將分數(shù)計入每個節(jié)點的總分
-
選擇 并 返回 得分最高的節(jié)點。如果只有一個可供調(diào)度的節(jié)點則跳過
Prescore,Score和NormalizeScore擴展點,并且立即返回調(diào)度的節(jié)點。如果沒有可供調(diào)度的節(jié)點,將結果返回給調(diào)度器。
值得注意的是:
- 如果插件提供
score normalization,當調(diào)用 ScoreExtensions() 時,插件需要返回非 nil
總結
以上是生活随笔為你收集整理的[译] kubernetes:kube-scheduler 调度器代码结构概述的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 洛谷 P9683 A Certain F
- 下一篇: Feign源码解析6:如何集成disco