1. 程式人生 > 實用技巧 >k8s排程器擴充套件機制

k8s排程器擴充套件機制

在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誰小誰在佇列裡排在前邊
Prefilter 階段增加一個過濾條件,當一個Pod排程時,會計算該Pod所屬PodGroup的Pod的Sum(包括Running狀態的),如果Sum小於min-available時,則肯定無法滿足min-available要求,則直接在該階段拒絕掉,不再進入排程的主流程。 UnReserve 階段會直接拒絕掉所有跟Pod屬於同一個PodGroup的Pod,避免剩餘的Pod進行長時間的無效等待。 Coscheduling外掛和原生排程器程式碼已經統一構建成一個容器映象。可以通過helm chart包ack-coscheduling來自動安裝。它會啟動一個任務,自動用Coscheduling scheduler替換原生的kube-scheduler,並且會修改相關Config檔案,使scheduling framework正確地載入Coscheduling 外掛。 下載 helm chart包,執行命令安裝:
$  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值,加權得到最終的分數。