k8s scheduler-framework 介紹

scheduling framework 是Kubernetes Scheduler的一種新的可插入架構,可簡化調度程序的自定義,
它向現有的調度程序中添加了一組新的pluginAPI。插件被編譯到調度程序中。


這些API允許大多數調度功能實現為插件,同時使調度core保持簡單且可維護。

框架工作流程

scheduling framework定義了一系列擴展點,調度插件註冊提交一個或多個擴展點,
一些可以改變調度結果,一些僅用於提供信息,
每次調度一個Pod的嘗試都分為兩個階段,即scheduling cycle(調度週期)和binding cycle(綁定週期)。

調度週期和綁定週期

調度週期為Pod選擇一個節點,並且綁定週期將該決策應用於集群。調度週期和綁定週期一起被稱為scheduling context。
調度週期是串行運行的,而綁定週期可能是並行的。
如果確定Pod不可調度或存在內部錯誤,則可以中止調度週期或綁定週期。Pod將返回隊列並重試。

擴展點

下圖顯示了Pod的調度上下文以及調度框架公開的擴展點。在此圖片中,Filter等效於預選,Scoring等效於優選。
一個插件可以在多個擴展點註冊以執行更復雜或有狀態的任務

k8s scheduler-framework 介紹

Queue sort

這些插件用於在調度隊列中對Pod進行排序。隊列排序插件本質上將提供less(Pod1, Pod2)功能。一次只能啟用一個隊列排序插件。

Pre-filter

這些插件用於預處理有關Pod的信息,或檢查集群或Pod必須滿足的某些條件。如果預過濾器插件返回錯誤,則調度週期將中止。

Filter

這些插件用於過濾無法運行Pod的節點。對於每個節點,調度程序將按其配置順序調用過濾器插件。
如果有任何過濾器插件將節點標記為不可行,則不會為該節點調用其餘插件。可以同時評估節點。

Post-filter

這是一個信息擴展點。將使用通過過濾階段的節點列表來調用插件。插件可以使用這些數據來更新內部狀態或生成日誌/指標。

注意: 希望執行“預評分”工作的插件應使用後過濾器擴展點。

Scoring

這些插件用於對已通過過濾階段的節點進行排名。調度程序將為每個節點調用每個計分插件。


將有一個定義明確的整數範圍,代表最小和最大分數。
在 Normalize scoring 階段之後,調度程序將根據配置的插件權重合並所有插件的節點分數

Normalize scoring

這些插件用於在調度程序計算節點的最終排名之前修改分數。
註冊此擴展點的插件將被調用,並帶有同一插件的評分結果。每個插件每個調度週期調用一次。
例如,假設一個插件BlinkingLightScorer根據節點閃爍的指示燈數量進行排序。

<code>func ScoreNode(_ *v1.pod, n *v1.Node) (int, error) {
return getBlinkingLightCount(n)
}
/<code>

但是,與相比,閃爍燈的最大數量可能會比 NodeScoreMax小。要解決此問題,BlinkingLightScorer也應該註冊該擴展點。

<code>func NormalizeScores(scores map[string]int) {
highest := 0
for _, score := range scores {
highest = max(highest, score)
}
for node, score := range scores {
scores[node] = score*NodeScoreMax/highest
}
}
/<code>

如果任何規範化評分插件返回錯誤,則調度週期將中止。

注意:希望執行pre-reserve工作的插件應使用規範化評分擴展點。

Reserve

這是一個信息擴展點。當為給定Pod保留節點上的資源時,維護運行時狀態的插件(也稱為stateful plugins)應使用此擴展點由調度程序通知。
這是在調度程序實際將Pod綁定到節點之前發生的,並且它存在是為了防止競爭條件,同時調度程序會等待綁定成功。
這是調度週期的最後一步。一旦Pod處於保留狀態,它將在綁定週期結束時觸發Unreserve插件(失敗)或 Post-bind插件(成功)。
注意:此概念以前稱為assume。

Permit

這些插件用於防止或延遲Pod的綁定。許可插件可以做以下三件事之一。

  • approve
    所有許可插件都批准Pod後,將其發送以進行綁定。
  • deny
    如果任何許可插件拒絕Pod,則將其返回到調度隊列。這將觸發Unreserve插件。
  • wait
    如果許可插件返回wait,則Pod將保持在許可階段,直到插件批准它為止。

    如果發生超時,wait將變為拒絕,並且Pod將返回到調度隊列,從而觸發 Unreserve插件。
    批准Pod綁定:
    儘管任何插件都可以從緩存中訪問waiting的Pod列表並批准它們(請參閱參考資料FrameworkHandle),
    但我們希望只有allow插件才能批准處於waiting狀態的預留Pod的綁定。批准Pod後,將其發送到預綁定階段。

Pre-bind

這些插件用於執行綁定Pod之前所需的任何工作。例如,預綁定插件可以在允許Pod在此處運行之前預配置網絡卷並將其安裝在目標節點上。
如果任何預綁定插件返回錯誤,則Pod被拒絕並返回到調度隊列。

Bind

這些插件用於將Pod綁定到節點。在所有預綁定插件完成之前,不會調用綁定插件。每個綁定插件均按配置順序調用。
綁定插件可以選擇是否處理給定的Pod。如果綁定插件選擇處理Pod,則會跳過其餘的綁定插件。

Post-bind

這是一個信息擴展點。成功綁定Pod後,將調用後綁定插件。這是綁定週期的結束,可用於清理關聯的資源。

Unreserve

這是一個信息擴展點。如果Pod被保留,然後在以後的階段中被拒絕,則將通知取消保留的插件。取消保留的插件應清除與保留的Pod相關聯的狀態。
使用此擴展點的插件通常也應使用 Reserve。

Plugin API

插件API分為兩個步驟。首先,插件必須註冊並配置,然後才能使用擴展點接口。擴展點接口具有以下形式。

<code>type Plugin interface {
Name() string
}
type QueueSortPlugin interface {
Plugin
Less(*v1.pod, *v1.pod) bool
}
type PreFilterPlugin interface {
Plugin
PreFilter(PluginContext, *v1.pod) error
}
// ...
/<code>

CycleState

大多數*插件函數將使用CycleState參數調用。 CycleState表示當前的調度上下文。
CycleState將提供API,用於訪問範圍為當前調度上下文的數據。
因為綁定週期可以同時執行,所以插件可以使用CycleState來確保它們正在處理正確的請求。
CycleState還提供類似於context.WithValue的API,可用於在不同擴展點的插件之間傳遞數據。


多個插件可以共享狀態或通過此機制進行通信。僅在單個調度上下文中保留狀態。值得注意的是,假定插件是受信任的。
調度程序不會阻止一個插件訪問或修改另一個插件的狀態。
唯一的例外是隊列排序插件。

警告:在調度上下文結束後,通過CycleState獲得的數據無效,並且插件保存該數據的引用的時間不應超過必要的時間。

FrameworkHandle

雖然CycleState提供與單個調度上下文有關的API,但是FrameworkHandle提供與插件的生存期有關的API。
這就是插件如何獲取客戶端(kubernetes.Interface)和SharedInformerFactory或從調度程序的群集狀態緩存讀取數據的方式。
該句柄還將提供API以列出和批准或拒絕等待的Pod。
警告:FrameworkHandle提供對kubernetes API服務器和調度程序的內部緩存的訪問。不能保證兩者都是同步的,編寫使用這兩個數據的插件時應格外小心。

要實現有用的功能,必須為插件提供對API服務器的訪問權限,特別是當這些功能使用了調度程序通常不考慮的對象類型時,
尤其如此。提供SharedInformerFactory可使插件安全共享緩存。

Plugin Registration

每個插件必須定義一個構造函數並將其添加到硬編碼的registry中。有關構造函數args的更多信息,請參見可選Args。

<code>type PluginFactory = func(runtime.Unknown, FrameworkHandle) (Plugin, error)
type Registry map[string]PluginFactory
func NewRegistry() Registry {
return Registry{
fooplugin.Name: fooplugin.New,
barplugin.Name: barplugin.New,
// New plugins are registered here.
}
}
/<code>

也可以將插件添加到Registry對象,然後將其注入調度程序中。請參閱自定義調度程序插件

插件生命週期

Initialization

插件初始化有兩個步驟。首先,註冊插件。其次,調度程序使用其配置來確定要實例化的插件。如果插件註冊了多個擴展點,則僅實例化一次。
實例化插件時,將向其傳遞config args和FrameworkHandle。

Concurrency

插件編寫者應考慮兩種併發類型。在評估多個節點時,一個插件可能會被同時調用幾次,而一個插件可能會從不同的調度上下文中被併發調用。


注意:在一個調度上下文中,將對每個擴展點進行串行評估。
在調度程序的主線程中,一次僅處理一個調度週期。在下一個調度週期開始之前,直至幷包括預留空間的任何擴展點都將完成。
在保留階段之後,綁定週期將異步執行。這意味著可以從兩個不同的調度上下文中同時調用一個插件,前提是至少有一個調用要在保留後到達擴展點。
有狀態的插件應謹慎處理這些情況。
最後,根據拒絕Pod的方式,可以從Permit線程或Bind線程調用取消un-reserve插件。
隊列排序擴展點是一種特殊情況。它不是調度上下文的一部分,可以為許多吊艙對同時調用。

k8s scheduler-framework 介紹

Plugin Configuration

可以在調度程序配置中啟用插件。另外,可以在配置中禁用默認插件(這裡好像沒實現)。在1.15中,調度框架沒有默認插件。
調度程序配置也可以包括插件的配置。這樣的配置將在調度程序初始化插件時傳遞給插件。該配置是任意值。接收插件應解碼並處理配置。
插件分為兩個部分:

  • 每個擴展點已啟用插件的列表(及其運行順序)。如果省略了這些列表之一,則將使用默認列表。
  • 每個插件的一組可選的自定義插件參數。省略插件的配置參數等效於使用該插件的默認配置。

插件配置由擴展點組織。每個列表中都必須包含一個註冊有多個要點的插件。

<code>type KubeSchedulerConfiguration struct {
// ... other fields
Plugins Plugins
PluginConfig []PluginConfig
}
type Plugins struct {
QueueSort []Plugin
PreFilter []Plugin
Filter []Plugin
PostFilter []Plugin

Score []Plugin
Reserve []Plugin
Permit []Plugin
PreBind []Plugin
Bind []Plugin
PostBind []Plugin
UnReserve []Plugin
}
type Plugin struct {
Name string
Weight int // Only valid for Score plugins
}
type PluginConfig struct {
Name string
Args runtime.Unknown
}
/<code>

下面的示例示出了調度器的配置,它使一些插件在reserve和preBind擴展點和禁用一個插件。它還提供了plugin的配置foo。

<code>apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
...
plugins:
reserve:
enabled:
- name: foo
- name: bar
disabled:
- name: baz
preBind:
enabled:
- name: foo
disabled:
- name: baz
pluginConfig:
- name: foo
args: >
Arbitrary set of args to plugin foo
/<code>

當配置中省略擴展點時,將使用該擴展點的默認插件。當存在擴展名並enabled提供擴展名時enabled,


除默認插件外,還將調用插件。首先調用默認插件,然後以配置中指定的相同順序調用其他已啟用的插件。
如果希望以不同的順序調用默認插件,則默認插件必須為,disabled且 enabled順序為所需。
假設有一個插件叫做默認foo的reserve,我們要添加插件bar,我們想要被調用之前foo,我們應該順序禁用foo 和啟用bar和foo。
以下示例顯示實現此目的的配置:

<code>apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
...
plugins:
reserve:
enabled:
- name: bar
- name: foo
disabled:
- name: foo
/<code>

啟用/禁用

指定後,將僅啟用特定擴展點的插件列表。如果配置中省略了擴展點,則默認插件集將用於該擴展點。

更改評估順序

關聯時,插件評估順序由插件在配置中出現的順序指定。註冊多個擴展點的插件在每個擴展點的順序可以不同。

可選的Args

插件可以從其配置中以任意結構接收參數。因為一個插件可能出現在多個擴展點中,所以配置位於PluginConfig的單獨列表中。
配置參數:

<code>{
"name": "ServiceAffinity",
"args": {
"LabelName": "app",
"LabelValue": "mysql"
}
}
/<code>

解析參數:

<code>func NewServiceAffinity(args *runtime.Unknown, h FrameworkHandle) (Plugin, error) {
if args == nil {
return nil, errors.Errorf("cannot find service affinity plugin config")
}
if args.ContentType != "application/json" {
return nil, errors.Errorf("cannot parse content type: %v", args.ContentType)
}
var config struct {
LabelName, LabelValue string
}
if err := json.Unmarshal(args.Raw, &config); err != nil {
return nil, errors.Wrap(err, "could not parse args")
}
//...
}
/<code>


分享到:


相關文章: