k8s排程器擴充套件機制
阿新 • • 發佈:2021-01-10
在kube-scheduler有四種擴充套件機制:
一、Multiple Scheduler
若要部署第二排程器,可以直接修改kubernetes的原始碼git clone https://github.com/kubernetes/kubernetes.git cd kubernetes make使用如下Dockerfile構建成映象:
FROM busybox ADD ./_output/local/bin/linux/amd64/kube-scheduler /usr/local/bin/kibe-scheduler建立ServiceAccount並將其繫結到ClusterRole,使其與kube-scheduler具有相同的許可權:
apiVersion: v1 kind: ServiceAccount metadata: name: my-scheduler namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: my-scheduler-as-kube-scheduler subjects: - kind: ServiceAccount name: my-scheduler namespace: kube-system roleRef: kind: ClusterRole name: system:kube-scheduler apiGroup: rbac.authorization.k8s.io --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: my-scheduler-as-volume-scheduler subjects: - kind: ServiceAccount name: my-scheduler namespace: kube-system roleRef: kind: ClusterRole name: system:volume-scheduler apiGroup: rbac.authorization.k8s.io部署該排程器:
apiVersion: apps/v1 kind: Deployment metadata: labels: component: scheduler tier: control-plane name: my-scheduler namespace: kube-system spec: selector: matchLabels: component: scheduler tier: control-plane replicas: 1 template: metadata: labels: component: scheduler tier: control-plane version: second spec: serviceAccountName: my-scheduler containers: - command: - /usr/local/bin/kube-scheduler - --address=0.0.0.0 - --leader-elect=false - --scheduler-name=my-scheduler image: xxxxxx livenessProbe: httpGet: path: /healthz port: 10251 initialDelaySeconds: 15 name: kube-second-scheduler readinessProbe: httpGet: path: /healthz port: 10251 resources: requests: cpu: '0.1' securityContext: privileged: false volumeMounts: [] hostNetwork: false hostPID: false volumes: []啟動時通過引數scheduler-name指定了第二排程器的名字 Pod通過spec.schedulerName指定使用的排程器(預設排程器是default-scheduler)
二、Multiple profiles
無需部署第二排程器,1.18+的kube-scheduler預設支援多profile:apiVersion: kubescheduler.config.k8s.io/v1beta1 kind: KubeSchedulerConfiguration profiles: - schedulerName: default-scheduler - schedulerName: no-scoring-scheduler plugins: preScore: disabled: - name: '*' score: disabled: - name: '*'
三、Schduler Extender
在啟動官方排程器之後,可以再啟動一個擴充套件排程器Schedule Extender。 Scheduler Extender實際上是一個額外的排程程序,使用者需要實現自己的Filter方法、Prioritize方法、Bind方法 策略檔案的配置內容:apiVersion: v1
kind: Policy
alwaysCheckALLPredicates: false # 不管每個prediacate的結果,所有的predicate都要走一遍
predicates:['GeneralPredicates','...'] # 不配的話使用預設的predicates;為空列表則跳過所有配置
hardPodAffinitySymmetricWeight:
# 配置打分演算法外掛;不配的話使用預設的打分外掛;為空列表則跳過所有配置
priorities:
- name: LeastRequestedPriority
weight: 1
extenders:
- urlPrxfix: "http://xxx:xxx/scheduler-gpu-extender"
filterVerb: filter
exableHttps: false
nodeCacheCapable: true # 服務是否已經有NodeCache,為true則排程器傳遞nodeName列表而不是整個nodeinfo完整結構
ignorable: false # 呼叫擴充套件排程器不成功時(如報錯或網路問題),是否可忽略擴充套件排程器
managedResources:
- name: "example/gpu-mem" # 官方排程器在遇到這個Resource時會用擴充套件排程器
ignoredByScheduler: false # resourceFit階段是否忽略這個資源的校驗
這裡可以看到配置的過濾器predicates、配置的打分器 priorities、配置的擴充套件排程器。
以Filter階段舉例,執行過程會經過2個階段:
(1)scheduler會先執行內建的Filter策略。如果執行失敗,會直接標識Pod排程失敗;
(2)如果內建的Filter策略執行成功,scheduler通過http呼叫Extender註冊的webhook,將排程所需要的Pod和Node的資訊傳送到Extender,根據返回Filter結果作為最終結果。
Schedule Extender存在以下問題:
(1)呼叫Extender的介面是HTTP請求,效能遠低於本地的函式呼叫。同時每次呼叫都需要將Pod和Node的資訊進行marshaling和unmarshalling的操作,會進一步降低效能;
(2)使用者可以擴充套件的點比較有限,位置比較固定,無法支援靈活的擴充套件,例如只能在執行完預設的Filter策略後才能呼叫。
因此,Extender在叢集規模較小,排程效率要求不高的情況下,是一個靈活可用的擴充套件方案。但是在正常生產環境的大型叢集中,Extender無法支援高吞吐量,效能較差。
四、Scheduling Framework
基於scheduler framework進行out-of-tree的scheduler plugins開發示例: kubernetes-sigs團隊實現了兩個外掛示例1、Qos的外掛
排程過程中如果Pod的優先順序相同,根據Pod的Qos來決定排程順序 (1)外掛構造 定義外掛的物件和建構函式:type Sort struct {} // 新初始化一個外掛,返回它 func New(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error){ return &Sort{}, nil }然後,實現QueueSort介面的Less函式,改為在優先順序相同的情況下,通過比較Qos來決定優先順序。
func (*Sort) Less(pInfo1, pInfo2 *framework.PodInfo) bool{ p1 := pod.GetPodPriority(pInfo1.Pod) p2 := pod.GetPodPriority(pInfo2.Pod) return (p1 > p2) || (p1 == p2 && compQOS(pInfo1.Pod, pInfo2.Pod)) } func compQOS(p1, p2 *v1.Pod) bool{ p1QOS, p2QOS := v1qos.GetPodQOS(p1), v1qos.GetPodQOS(p2) if p1QOS == v1.PodQOSGuaranteed { return true } else if p1QOS == v1.PodQOSBurstable { return p2QOS != v1.PodQOSGuaranteed } else{ return p2QOS == v1.PodQOSBestEffort } }(2)外掛註冊在main函式中註冊自定義外掛和相應的建構函式:
func main() { rand.Seed(time.Now().UnixNano()) // 向scheduler framework註冊使用者自定義外掛 command := app.NewSchedulerCommand( app.WithPlugin(qos.Name, qos.New), app.WithPlugin(qos.Name, qos.New), ) if err := command.Execute(); err != nil { os.Exit(1) } }(3)通過make命令進行程式碼編譯 (4)配置scheduler-config.yaml
apiVersion: kubescheduler.config.k8s.io/v1alpha2
kind: KubeSchedulerConfiguration
leaderElection:
leaderElect: false
clientConnection:
kubeconfig: "REPLACE_ME_WITH_KUBE_CONFIG_PATH"
profiles:
- schedulerName: default-scheduler
plugins:
queueSort:
enabled:
- name: QOSSort
disabled:
- name: "*"
啟動kube-scheduler時傳入叢集的kubeconfig檔案以及外掛的配置檔案即可
$ kube-scheduler --kubeconfig=scheduler.conf --config=scheduler-config.yaml
2、Coscheduling外掛
在併發系統中將多個相關聯的程序排程到不同處理器上同時執行,需要保證所有相關聯的程序能夠同時啟動。 部分程序的異常,可能導致整個關聯程序組的阻塞。這種導致阻塞的部分異常程序,稱之為碎片(fragement)。 Coscheduling的具體實現過程中,根據是否允許碎片存在,可以細分為完全不允許有碎片存在的Explicit Coscheduling(Gang Scheduling)、Local Coscheduling和Implicit Coscheduling。 對應到Kubernetes中,Coscheduling的含義就是:一個批任務(關聯程序組)包括了N個Pod(程序),Kubernetes排程器負責將這N個Pod組成的PodGroup排程到M個節點(處理器)上同時執行。 如果這個批任務需要部分Pod同時啟動即可執行,稱需啟動Pod的最小數量為min-available。 特別地,當min-available=N時,批任務要求滿足Gang Scheduling。 外掛通過給Pod打label的形式來定義PodGroup: pod-group.scheduling.sigs.k8s.io/name:xxx用於表示PodGroup的Name; pod-group.scheduling.sigs.k8s.io/min-available:"2" 表示PodGroup能夠執行所需要的最小副本數 備註:要求屬於同一個PodGroup的Pod必須保持相同的優先順序 Permit階段使用了延遲繫結功能,當屬於同一個PodGruop的Pod數量不夠時,進行等待;積累的Pod數目足夠時,才會將所有Pod全部繫結並建立。 如果某個Pod在Permit階段等待超時了,則會進入到UnReserve階段。 QueueSort階段,由於預設的Scheduler佇列並不能感知 PodGroup 的資訊,同一個PodGroup中的Pod在佇列中是混亂無序的。 一旦某個PodGroup在 Permit 階段處於等待狀態,其它的PodGroup也會因此處於等待狀態,從而導致死鎖。 此處自定義的Less方法繼承了預設的基於優先順序的比較方式,高優先順序的Pod會排在低優先順序的Pod之前。如果兩個Pod的優先順序相同,則採用新的排隊邏輯,以保證佇列中屬於同一個PodGroup的Pod排列在一起:- 如果兩個Pod 都是普通的Pod,則誰先建立誰在佇列裡邊排在前邊;
- 如果兩個Pod 一個是普通的Pod,另一個是屬於某個PodGroup的Pod,則比較的是前者的建立時間和後者所屬PodGroup的建立時間;
- 如果兩個Pod 都是屬於某個PodGroup的Pod,比較兩個 PodGroup 的建立時間,則誰先建立誰在佇列裡排在前邊;
- 如果兩個PodGroup 的建立時間相同,誰的PodGroup 的自增Id誰小誰在佇列裡排在前邊
$ wget http://kubeflow.oss-cn-beijing.aliyuncs.com/ack-coscheduling.tar.gz $ tar zxvf ack-coscheduling.tar.gz $ helm install ack-coscheduling -n kube-system ./ack-coscheduling NAME: ack-coscheduling LAST DEPLOYED: Mon Apr 13 16:03:57 2020 NAMESPACE: kube-system STATUS: deployed REVISION: 1 TEST SUITE: None在 Master 節點上,使用helm命令驗證是否安裝成功。
$ helm get manifest ack-coscheduling -n kube-system | kubectl get -n kube-system -f - NAME COMPLETIONS DURATION AGE scheduler-update-clusterrole 1/1 8s 35s scheduler-update 3/1 of 3 8s 35s使用Coscheduling時,只需要在建立任務的yaml描述中配置兩個描述任務狀態的label即可。 通過 helm 解除安裝,將kube-scheduler的版本及配置回滾到叢集預設的狀態:
$ helm uninstall ack-coscheduling -n kube-system阿里雲實現的外掛:
3、RequestedToCapacityRatio外掛
使用者自己定義的資源利用率與得分間的線性關係、根據哪些資源進行打分、每種資源的權重:apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
leaderElection:
leaderElect: false
clientConnection:
kubeconfig: "REPLACE_ME_WITH_KUBE_CONFIG_PATH"
plugins:
score:
enabled:
- name: RequestedToCapacityRatio
weight: 100
disabled:
- name: LeastRequestedPriority
pluginConfig:
- name: RequestedToCapacityRatio
args:
functionshape:
- utilization: 0
score: 0
- utilization: 100
score: 100
resourcetoweightmap: # 定義具體根據哪種資源型別進行binpack操作,多種資源時可以設定weight來進行比重設定
"cpu": 1
"nvidia.com/gpu": 1
在打分過程中,會通過計算(pod.Request + node.Allocated)/node.Total的結果得到對應資源的利用率,並且將利用率帶入上文中所述的打分函式中,得到相應的分數。
最後將所有的資源根據weight值,加權得到最終的分數。