k8s調度器kube-scheduler


kube-scheduler簡介

調度是容器編排的重要環節,需要經過嚴格的監控和控制,現實生產通常對調度有各類限制,譬如某些服務必須在業務獨享的機器上運行,或者從災備的角度考慮盡量把服務調度到不同機器,這些需求在Kubernetes集群依靠調度組件kube-scheduler滿足。

kube-scheduler是Kubernetes中的關鍵模塊,扮演管家的角色遵從一套機制為Pod提供調度服務,例如基於資源的公平調度、調度Pod到指定節點、或者通信頻繁的Pod調度到同一節點等。容器調度本身是一件比較復雜的事,因為要確保以下幾個目標:

  1. 公平性:在調度Pod時需要公平的進行決策,每個節點都有被分配資源的機會,調度器需要對不同節點的使用作出平衡決策。

  2. 資源高效利用:最大化群集所有資源的利用率,使有限的CPU、內存等資源服務盡可能更多的Pod。

  3. 效率問題:能快速的完成對大批量Pod的調度工作,在集群規模擴增的情況下,依然保證調度過程的性能。

  4. 靈活性:在實際運作中,用戶往往希望Pod的調度策略是可控的,從而處理大量復雜的實際問題。因此平台要允許多個調度器並行工作,同時支持自定義調度器。

為達到上述目標,kube-scheduler通過結合Node資源、負載情況、數據位置等各種因素進行調度判斷,確保在滿足場景需求的同時將Pod分配到最優節點。顯然,kube-scheduler影響着Kubernetes集群的可用性與性能,Pod數量越多集群的調度能力越重要,尤其達到了數千級節點數時,優秀的調度能力將顯著提升容器平台性能。

調度流程

kube-scheduler的根本工作任務是根據各種調度算法將Pod綁定(bind)到最合適的工作節點,整個調度流程分為兩個階段:預選策略(Predicates)和優選策略(Priorities)。

  1. 預選(Predicates):輸入是所有節點,輸出是滿足預選條件的節點。kube-scheduler根據預選策略過濾掉不滿足策略的Nodes。例如,如果某節點的資源不足或者不滿足預選策略的條件如“Node的label必須與Pod的Selector一致”時則無法通過預選。

  2. 優選(Priorities):輸入是預選階段篩選出的節點,優選會根據優先策略為通過預選的Nodes進行打分排名,選擇得分最高的Node。例如,資源越富裕、負載越小的Node可能具有越高的排名。

通俗點說,調度的過程就是在回答兩個問題:1. 候選有哪些?2. 其中最適合的是哪個?

值得一提的是,如果在預選階段沒有節點滿足條件,Pod會一直處在Pending狀態直到出現滿足的節點,在此期間調度器會不斷的進行重試。

到這里我們可以對Pod的整個啟動流程進行總結

  1. 資源管控中心Controller Manager創建新的Pod,將該Pod加入待調度的Pod列表。

  2. kube-scheduler通過API Server提供的接口監聽Pods,獲取待調度pod,經過預選和優選兩個階段對各個Node節點打分排序,為待調度Pod列表中每個對象選擇一個最優的Node。

  3. kube-scheduler將Pod與Node的綁定寫入etcd(元數據管理服務)。

  4. 節點代理服務kubelet通過API Server監聽到kube-scheduler產生的綁定信息,獲得Pod列表,下載Image並啟動容器,然后由kubelet負責拉起Pod。

到此為止就完成了Pod的調度與啟動。

 

預選和優選算法介紹

 

kube-scheduler支持多種預選和優選算法,開發者可以根據當前場景下的重要要素進行選擇。

預選策略(Predicates)

  1. 基於存儲卷數量的判斷

    • MaxEBSVolumeCount:確保已掛載的EBS存儲卷數量不超過設置的最大值(默認39),調度器會檢查直接或及間接使用這種類型存儲的PVC,累加總數,如果卷數目超過設最大值限制,則不能調度新Pod到這個節點上。

    • MaxGCEPDVolumeCount:同上,確保已掛載的GCE存儲卷數量不超過預設的最大值(默認16)。

    • MaxAzureDiskVolumeCount:同上,確保已掛載的Azure存儲卷不超過設置的最大值(默認16)。

  2. 基於資源壓力狀態的判斷

    • CheckNodeMemoryPressure:判斷節點是否已經進入到內存壓力狀態,如果是則只允許調度內存為0標記的Pod。

    • CheckNodeDiskPressure:判斷節點是否已經進入到磁盤壓力狀態,如果是,則不能調度新的Pod。

  3. 基於卷沖突的判斷

    • NoDiskConflict:卷沖突判斷,即如果該節點已經掛載了某個卷,其它同樣使用相同卷的Pod將不能再調度到該節點。

    • NoVolumeZoneConflict:對於給定的某塊區域,判斷如果在此區域的節點上部署Pod是否存在卷沖突。

    • NoVolumeNodeConflict:對於某個指定節點,檢查如果在此節點上部署Pod是否存在卷沖突。

  4. 基於約束關系的判斷

    • MatchNodeSelector:檢查節點標簽(label)是否匹配Pod指定的nodeSelector,是則通過預選。

    • MatchInterPodAffinity:根據Pod之間的親和性做判斷。

    • PodToleratesNodeTaints:排斥性關系,即判斷Pod不允許被調度到哪些節點。這里涉及到兩個概念Taints(污點)和Toleration(容忍)。Node可以定義一或多個Taint,Pod可以定義一或多個Toleration,對於具有某個Taint的節點,只有遇到能容忍它的(即帶有對應Toleration的)Pod,才允許Pod被調度到此節點,從而避免Pod被分配到不合適的節點。

  5. 基於適合性的判斷

    • PodFitsResources:檢查節點是否有足夠資源(如CPU、內存、GPU等)滿足Pod的運行需求。

    • PodFitsHostPorts:檢查Pod容器所需的HostPort是否已被節點上其它容器或服務占用。如果已被占用,則禁止Pod調度到該節點。

    • PodFitsHost:檢查Pod指定的NodeName是否匹配當前節點。

 

優選策略(Priorities)

優選過程會根據優選策略對每個候選節點進行打分,最終把Pod調度到分值最高的節點。kube-scheduler用一組優先級函數處理每個通過預選的節點,每個函數返回0-10的分數,各個函數有不同權重,最終得分是所有優先級函數的加權和,即節點得分的計算公式為:

優選的優先級函數包括:

  • LeastRequestedPriority(默認權重1):盡量將Pod調度到計算資源占用比較小的Node上,這里涉及兩種計算資源:內存和CPU。計算公式如下:其中,capacity表示該節點的現有容量,requested表示Pod所請求的容量。

  • BalancedResourceAllocation(默認權重1):CPU和內存使用率越接近的節點權重越高。該策略均衡了節點CPU和內存的配比,盡量選擇在部署Pod后各項資源更均衡的機器。該函數不能單獨使用,必須和LeastRequestedPriority同時使用,因為如果請求的資源(CPU或者內存)大於節點的capacity,那么該節點永遠不會被調度到。計算公式如下:

  • SelectorSpreadPriority(默認權重1):把屬於同一個Service或者ReplicationController的Pod,盡量分散在不同的節點上,如果指定了區域,則盡量把Pod分散在該區域的不同節點。通常來說節點上已運行的Pod越少,節點分數越高。計算公式如下,是基於節點的計算和基於區域的計算的加權和,其中,maxPriority代表系數,默認為10,maxCount為節點最多允許運行的Pod數量,nodeCount為該節點已經存在的Pod數量,maxCountByZone為該區域最多允許的Pod數量,zoneCount為區域內已經運行的Pod數量。

  • NodeAffinityPriority(默認權重1):盡量調度到標簽匹配Pod屬性要求的節點,判斷行為與預選中的MatchNodeSelector相似,未來可能會完全將其取代。

    該函數提供兩種選擇器:requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution。前者是強要求,指定將Pod調度到節點上必須滿足的所有規則;后者是弱要求,即盡可能調度到滿足特定限制的節點,但允許不滿足。計算公式如下,在節點滿足requiredDuringSchedulingIgnoredDuringExecution后,該節點會累加preferredDuringSchedulingIgnoredDuringExecution中所有滿足條件的規則的權值weight_i,CountWeight為preferredDuringSchedulingIgnoredDuringExecution中所有規則的權值總和:

    InterPodAffinityPriority(默認權重1):疊加該節點已調度的每個Pod的權重,權重可配置文件里設定,通常親和性越強的Pod權重越高,如果Pod滿足要求則將加到權重和,具有最高權重和的節點是最優的。提供兩種選擇器:requiredDuringSchedulingIgnoredDuringExecution(保證所選的主機必須滿足所有Pod對主機的規則要求)、preferredDuringSchedulingIgnoredDuringExecution(調度器會盡量但不保證滿足NodeSelector的所有要求)。

  • 計算公式如下,其中,weight_i為節點上符合親和性的每個Pod的權重,sumCount為節點上符合親和性的Pod權重和,maxCount為節點上所有Pod的權重和,minCount為節點上最小的Pod權重,maxPriority是系數,默認為10。

  • NodePreferAvoidPodsPriority(默認權重10000):避免將ReplicationController或ReplicaSet調度到節點。如果Pod由RC或者RS的Controller調度,則得分為0,不對最終加權得分產生影響;如果不是,則總分為100000,意味着覆蓋其他策略,直接決定最優節點。

  • TaintTolerationPriority(默認權重1):Pod與Node的排斥性判斷。通過Pod的tolerationList與節點Taint進行匹配,配對失敗的項越少得分越高,類似於Predicates策略中的PodToleratesNodeTaints。計算公式如下:其中,totalTaints表示Taint總個數,intolerableTaints表示配對不成功的個數。

  • ImageLocalityPriority(默認權重1):盡量調度到Pod所需鏡像的節點。檢查Node是否存在Pod所需鏡像:如果不存在,返回0分;如果存在,則鏡像越大得分越高。計算公式如下:其中,sumSize表示該節點上存在的Pod所需鏡像大小總和,maxImgSize表示Pod所需鏡像總大小,minImgSize表示Pod所需最小鏡像的尺寸。

  • EqualPriority(默認權重1):給予所有節點相等權重,一般僅用於測試。

  • MostRequestedPriority(默認權重1):適用於動態伸縮集群環境,會優先調度Pod到使用率最高的節點,方便在伸縮集群時,先騰出空閑機器,從而進行停機處理。

案例演示

下面將通過四個案例來介紹如何在實戰中對以上的調度策略進行選擇。

    • 場景一:調度到基於SSD的節點

      某架構使用分布式緩存Redis,對磁盤讀寫敏感,開發者希望調度到配置SSD的節點,可用預選策略MatchNodeSelector解決,對目標Node設置label,Redis的spec添加nodeSelector。

假設Redis的YAML文件如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-master
  labels:
    name: redis
  namespace: default
spec:
  replicas: 4
  template:
    metadata:
      labels:
      name: redis
  spec:
    containers:
    - name: master
      image: 172.16.1.41:5000/redis:3.0.5
      resources:
        requests:
          cpu: 100m
          memory: 100Mi

節點transwarp配置了SSD,因此添加“disk=ssd”標簽。

kubectl label node transwarp disk=ssd

通過更新Redis服務的YAML文件字段添加nodeSelector標簽“disk=ssd”來匹配節點transwarp。

kubectl patch deploy redis-master -p  '{"spec":{"template":{"spec":{"nodeSelector:"{"disk":"ssd"}}}}}'
    • 場景二:只允許調度到CentOS節點

      某系統中有各種異構系統機器,比如SLES、RHEL、CentOS7.2、CentOS7.3等,現在有一個線上任務只能運行在CentOS系統,但是不區分CentOS系統版本。當然我們可以使用MatchNodeSelector來實現匹配,然而如果以這種方式實現需要備注多個標簽,因此建議用NodeAffinityPriority解決。

我們在該服務的YAML文件中將NodeAffinityPriority選擇器的指定為requiredDuringSchedulingIgnoredDuringExecution,表示強要求,指定Pod必須被調度到key為“operation system”,值為“centos 7.2”或“centos 7.3”的節點。

requiredDuringSchedulingIgnoredDuringExecution:
  nodeSelectorTerms:
  - matchExpressions:
    - key: operation system
      operator: In
      values:
      -  centos 7.2
      -  centos 7.3

如果需要優先調度到CentOS 7.2的節點,還可以增加一個由preferredDuringSchedulingIgnoredDuringExecution決定的調度策略,優先調度到具有“another-node-label-key-system”標簽,且值為“another-node-label-value-centos7.2”的節點。

preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
  preference:
   matchExpressions:
   - key: another-node-label-key-system
     operator: In
     values:
     - another-node-label-value-centos7.2

該線上服務的YAML文件具體內容如下:

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: operation system
            operator: In
            values:
            -  centos 7.2
            -  centos 7.3
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key-system
            operator: In
            values:
            - another-node-label-value-centos7.2
  containers:
  - name: with-node-affinity
    image: gcr.io/google_containers/pause:2.0

注意:如果同時指定了nodeSelector和nodeAffinity,目標節點必須同時滿足這兩個規則;nodeSelectorTerms可以指定多個,滿足任意一個即可滿足;matchExpressions也可以指定多個,要求必須都滿足。

    • 場景三:調度到同一分區

      現有公有雲組件API服務,需要與認證服務器頻繁通信,因此要盡可能的協同部署在雲提供商的同一個分區中。

      該場景的需求是使API服務和認證服務運行在同一分區,轉化到更通用的視角就是把Pod優先調度到更具Pod親和性的節點,故可用優選策略InterPodAffinityPriority解決。

首先我們對API服務添加標簽“security=cloud”,它的其中一段配置如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-flag
  labels:
    security: "cloud"
spec:
  containers:
  - name: nginx
    image: nginx

為了使認證服務分配到相同分區,需要在其YAML文件中添加requiredDuringSchedulingIgnoredDuringExecution字段去親和具有“security=cloud”標簽的Pod,同時添加topologyKey: failure-domain.beta.kubernetes.io/zone字段來限制拓撲域范圍,表示把部署限制在同一個分區。

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - cloud
        topologyKey: failure-domain.beta.kubernetes.io/zone
    • 場景四:

      私有雲服務中,某業務使用GPU進行大規模並行計算。為保證性能,希望確保該業務對服務器的專屬性,避免將普通業務調度到部署GPU的服務器。

      該場景要求Pod避免被調度到指定類型的節點,符合排斥關系,故可用策略PodToleratesNodeTaints解決。

首先先在配有GPU的node1中添加Taints污點(gpu = true),NoSchedule表示只要沒有對應Toleration的Pod就不允許調度到該節點。

kubectl taint nodes nodes1 gpu=true:NoSchedule

同時,通過更新服務的YAML文件,為需要GPU資源業務的Pods添加Toleration(key: "gpu",value: "true"),表示容忍GPU服務器的Taints,這樣就可以把指定業務調度到GPU服務器,同時把普通服務調度到其他節點。

apiVersion: v1
kind: Pod
metadata:
  generateName: redis-
  labels:
    app: redis
  namespace: default
spec:
  containers:
  - image: 172.16.1.41:5000/redis
    imagePullPolicy: Always
    name: redis
  schedulerName: default-scheduler
  tolerations:
  - effect:  NoSchedule
    key: gpu
    operator:  Equal
    value: true

這里需要提一下,tolerations里的effect字段表示處理排斥的策略,支持三種調度規則:NoSchedule表示不允許調度,已調度的不影響;PreferNoSchedule表示盡量不調度;NoExecute表示不允許調度,已運行的Pod將在之后刪除。

總結與展望

 目前kube-scheduler已經提供了豐富的調度策略可供使用,一般情況下,使用kube-scheduler的默認調度策略就能滿足大部分需求,並且其插件化的形式也方便於用戶進行定制與二次開發。未來,我們會在此基礎上對其進一步優化:包括增加cache以減少predict和prioritize階段的重復計算,已在TOS 1.9中實現;通過scheduler-extender擴展來針對local Volume、GPU等資源實現更合理調度;適配custom-metrics-server的API來實現實時調度,使得每個節點的資源能更好的被利用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM