Стварэнне дадатковага kube-scheduler'a з кастамным наборам правіл планавання

Стварэнне дадатковага kube-scheduler'a з кастамным наборам правіл планавання

Kube-scheduler з'яўляецца неад'емным кампанентам Kubernetes, які адказвае за планаванне падоў па нодах у адпаведнасці з зададзенымі палітыкамі. Часцяком, падчас эксплуатацый Kubernetes-кластара нам не прыходзіцца задумвацца аб тым, па якіх менавіта палітыкам адбываецца планаванне падоў, бо набор палітык дэфолтнага kube-scheduler'a падыходзіць для большасці паўсядзённых задач. Аднак сустракаюцца сітуацыі, калі нам важна тонка кіраваць працэсам размеркавання падоў, і для выканання гэтай задачы ёсць два шляхі:

  1. Стварыць kube-scheduler з кастамным наборам правіл
  2. Напісаць свой уласны scheduler і навучыць яго працаваць з запытамі API-сервера

У рамках дадзенага артыкула я апішу рэалізацыю менавіта першага пункта для рашэння праблемы нераўнамернага планавання падоў на адным з нашых праектаў.

Кароткая ўступная аб працы kube-scheduler'a

Варта асоба адзначыць той факт, што kube-scheduler не адказвае за непасрэднае планаванне подаў - ён адказвае толькі за вызначэнне ноды, на якую трэба размясціць пад. Інакш кажучы, вынік працы kube-scheduler'a – гэта імя ноды, якое ён вяртае API-серверу на запыт аб планаванні і на гэтым яго праца сканчаецца.

Спачатку kube-scheduler складае спіс нод, на якія можа быць запланаваны пад у адпаведнасці з палітыкамі predicates. Далей кожная нода з гэтага спісу атрымлівае пэўную колькасць ачкоў у адпаведнасці з палітыкамі priorites. У выніку выбіраецца нода, якая набрала максімальную колькасць ачкоў. Калі ёсць ноды, якія набралі аднолькавы максімальны бал, выбіраецца выпадковая. Са спісам і апісаннем палітык predicates (filtering) і priorites (scoring) можна азнаёміцца ​​ў дакументацыі.

Апісанне цела праблемы

Нягледзячы на ​​вялікую колькасць розных Kubernetes кластараў на абслугоўванні ў Nixys, упершыню з праблемай планавання подаў мы сутыкнуліся толькі нядаўна, калі для аднаго з нашых праектаў з'явілася неабходнасць запуску вялікай колькасці перыядычных задач (~100 сутнасцяў CronJob). Каб максімальна спрасціць апісанне праблемы, у якасці прыкладу возьмем адзін мікрасэрвіс, у рамках якога раз у хвіліну запускаецца cron-задача, якая стварае некаторую нагрузку на CPU. Для працы cron-задачы былі выдзелены тры абсалютна аднолькавыя па характарыстыках ноды (24 vCPU на кожнай).

Пры гэтым нельга з дакладнасцю сказаць колькі часу будзе выконвацца CronJob, бо аб'ём уваходных дадзеных увесь час змяняецца. У сярэднім, пры нармальнай працы kube-scheduler'a, на кожнай нодзе працуе 3-4 экзэмпляры задання, якія ствараюць ~20-30% нагрузкі на CPU кожнай ноды:

Стварэнне дадатковага kube-scheduler'a з кастамным наборам правіл планавання

Сама праблема складаецца ў тым, што часам поды cron-задачы пераставалі планавацца на адну з трох нод. Гэта значыць, у нейкі момант часу на адну з нод не планавалася ніводнага пода, тады як на двух іншых нодах працавала па 6-8 асобнікаў задання ствараючы ~40-60% нагрузкі на CPU:

Стварэнне дадатковага kube-scheduler'a з кастамным наборам правіл планавання

Праблема паўтаралася з абсалютна выпадковай перыядычнасцю і зрэдку карэлявала з момантам выкаткі новай версіі кода.

Падвысіўшы ўзровень лагавання kube-scheduler'a да 10 узроўня (-v=10) мы пачалі фіксаваць, колькі набірае ачкоў падчас адзнакі кожная з нод. Пры нармальнай працы планавання ў логах можна было ўбачыць наступную інфармацыю:

resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node03: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1387 millicores 4161694720 memory bytes, score 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node02: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1347 millicores 4444810240 memory bytes, score 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node03: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1387 millicores 4161694720 memory bytes, score 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node01: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1687 millicores 4790840320 memory bytes, score 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node02: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1347 millicores 4444810240 memory bytes, score 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node01: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1687 millicores 4790840320 memory bytes, score 9
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: NodeAffinityPriority, Score: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: NodeAffinityPriority, Score: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: NodeAffinityPriority, Score: (0)                                                                                       
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node01: InterPodAffinityPriority, Score: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: TaintTolerationPriority, Score: (10)                                                                                   
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node02: InterPodAffinityPriority, Score: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: TaintTolerationPriority, Score: (10)                                                                                   
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node01: SelectorSpreadPriority, Score: (10)                                                                                                        
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node03: InterPodAffinityPriority, Score: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: TaintTolerationPriority, Score: (10)                                                                                   
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node02: SelectorSpreadPriority, Score: (10)                                                                                                        
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node03: SelectorSpreadPriority, Score: (10)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:781] Host Node01 => Score 100043                                                                                                                                                                        
generic_scheduler.go:781] Host Node02 => Score 100043                                                                                                                                                                        
generic_scheduler.go:781] Host Node03 => Score 100043

Г.зн. мяркуючы па інфармацыі, атрыманай з логаў, кожная з нод набірала роўную колькасць выніковых ачкоў і для планавання выбіралася выпадковая. У момант праблемнага планавання логі выглядалі наступным чынам:

resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node02: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1587 millicores 4581125120 memory bytes, score 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node03: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1087 millicores 3532549120 memory bytes, score 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node02: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1587 millicores 4581125120 memory bytes, score 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node01: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 987 millicores 3322833920 memory bytes, score 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node01: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 987 millicores 3322833920 memory bytes, score 9 
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node03: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1087 millicores 3532549120 memory bytes, score 9
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node03: InterPodAffinityPriority, Score: (0)                                                                                                        
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node02: InterPodAffinityPriority, Score: (0)                                                                                                        
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node01: InterPodAffinityPriority, Score: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: TaintTolerationPriority, Score: (10)                                                                                   
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node03: SelectorSpreadPriority, Score: (10)                                                                                                        
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node02: SelectorSpreadPriority, Score: (10)                                                                                                        
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: TaintTolerationPriority, Score: (10)                                                                                   
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node01: SelectorSpreadPriority, Score: (10)                                                                                                        
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: NodeAffinityPriority, Score: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: TaintTolerationPriority, Score: (10)                                                                                   
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: NodeAffinityPriority, Score: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: NodeAffinityPriority, Score: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:781] Host Node03 => Score 100041                                                                                                                                                                        
generic_scheduler.go:781] Host Node02 => Score 100041                                                                                                                                                                        
generic_scheduler.go:781] Host Node01 => Score 100038

З якіх відаць, што адна з нод набірала менш выніковых ачкоў, чым астатнія, і таму планаванне выконвалася толькі на дзве ноды, якія набралі максімальны бал. Такім чынам мы сапраўды пераканаліся, што праблема заключаецца менавіта ў планаванні падоў.

Далейшы алгарытм рашэння праблемы быў для нас відавочны - прааналізаваць логі, зразумець па якім менавіта прыярытэце нода не дабрала ачкоў і, пры неабходнасці, скарэктаваць палітыкі дэфолтнага kube-scheduler'а. Аднак тут мы сутыкнуліся з дзвюма істотнымі цяжкасцямі:

  1. На максімальным узроўні лагавання (10) адлюстроўваецца набор ачкоў толькі па некаторых прыярытэтах. У прыведзеным вышэй урыўку логаў можна заўважыць, што па ўсіх прыярытэтах, адлюстраваных у логах, ноды набіраюць аднолькавую колькасць ачкоў пры нармальным і праблемным планаванні, аднак фінальны вынік у выпадку праблемнага планавання адрозніваецца. Такім чынам, можна зрабіць выснову, што па нейкіх прыярытэтах падлік ачкоў адбываецца "за кадрам", і ў нас няма ніякай магчымасці зразумець па якім менавіта прыярытэце нода не дабрала акуляры. Дадзеную праблему мы падрабязна апісалі ў пытанне рэпазітара Kubernetes на Github. На момант напісання артыкула быў атрыманы адказ ад распрацоўшчыкаў, што падтрымка лагіравання будзе дададзена ў абнаўленнях Kubernetes v1.15,1.16 і 1.17.
  2. Няма простага спосабу зразумець з якім канкрэтна наборам палітык у дадзены момант працуе kube-scheduler. Так, у дакументацыі гэты спіс пералічаны, але ў ім няма інфармацыі якія канкрэтна вагі выстаўлены кожнай з палітык priorites. Убачыць вагі або адрэдагаваць палітыкі дэфолтнага kube-scheduler'a можна толькі ў зыходніках.

Варта адзначыць, адзін раз нам удалося зафіксаваць, што нода не дабірала акуляры па палітыцы ImageLocalityPriority, якая налічвае акуляры надзе, калі на ёй ужо ёсць выява, неабходная для запуску прыкладання. Т. е. у момант выкаткі новай версіі прыкладання cron-задача паспявала запускацца на двух нодах, выпампоўваючы на ​​іх новая выява з docker registry, і такім чынам дзве ноды атрымлівалі вялікі выніковы бал адносна трэцяй.

Як я ўжо пісаў вышэй, у логах мы не бачым інфармацыі аб ацэнцы палітыкі ImageLocalityPriority, таму, каб праверыць сваю здагадку, мы спулілі выяву з новай версіяй прыкладання на трэцюю ноду, пасля чаго планаванне запрацавала карэктна. Менавіта з-за палітыкі ImageLocalityPriority праблема планавання назіралася дастаткова рэдка, часцей яна была звязана з нечым іншым. З-за таго, што мы не маглі паўнавартасна дэбажыць кожную з палітык у спісе priorites дэфолтнага kube-scheduler'a, у нас з'явілася неабходнасць у гнуткім кіраванні палітыкамі планавання падоў.

Пастаноўка задачы

Мы хацелі, каб рашэнне праблемы было максімальна кропкавым, гэта значыць асноўныя сутнасці Kubernetes (тут маецца на ўвазе дэфолтны kube-scheduler) павінны заставацца нязменнымі. Нам не хацелася вырашаць праблему ў адным месцы і ствараць яе ў іншым. Такім чынам, мы прыйшлі да двух варыянтаў рашэння праблемы, якія былі агучаныя ва ўводзінах да артыкула – стварэнне дадатковага scheduler'a ці напісанне свайго. Асноўнае патрабаванне да планавання cron-задач - раўнамернае размеркаванне нагрузкі па трох нодам. Гэтае патрабаванне можна задаволіць ужо існуючымі палітыкамі kube-scheduler'a, таму для рашэння нашай задачы няма сэнсу пісаць свой уласны scheduler.

Інструкцыя стварэння і Deployment дадатковага kube-scheduler'a апісаны ў дакументацыі. Аднак, нам падалося, што сутнасці Deployment недастаткова для забеспячэння адмоваўстойлівасці ў працы такога крытычнага сэрвісу як kube-scheduler, таму мы вырашылі разгарнуць новы kube-scheduler як Static Pod, за якім будзе сачыць непасрэдна Kubelet. Такім чынам, у нас склаліся наступныя патрабаванні да новага kube-scheduler'у:

  1. Сэрвіс павінен быць разгорнуты як Static Pod на ўсіх майстрах кластара
  2. Павінна быць прадугледжана адмоваўстойлівасць на выпадак недаступнасці актыўнага пода з kube-scheduler'ам
  3. Асноўным прыярытэтам пры планаванні павінна быць колькасць даступных рэсурсаў на нодзе (LeastRequestedPriority)

Рэалізацыя рашэнняў

Варта адразу адзначыць, што ўсе працы мы будзем праводзіць у Kubernetes v1.14.7, т.я. менавіта гэтая версія выкарыстоўвалася ў праекце. Пачнём з напісання маніфесту для нашага новага kube-scheduler'a. За аснову возьмем маніфест дэфолтнага (/etc/kubernetes/manifests/kube-scheduler.yaml) і прывядзем яго да наступнага ўвазе:

kind: Pod
metadata:
  labels:
    component: scheduler
    tier: control-plane
  name: kube-scheduler-cron
  namespace: kube-system
spec:
      containers:
      - command:
        - /usr/local/bin/kube-scheduler
        - --address=0.0.0.0
        - --port=10151
        - --secure-port=10159
        - --config=/etc/kubernetes/scheduler-custom.conf
        - --authentication-kubeconfig=/etc/kubernetes/scheduler.conf
        - --authorization-kubeconfig=/etc/kubernetes/scheduler.conf
        - --v=2
        image: gcr.io/google-containers/kube-scheduler:v1.14.7
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 8
          httpGet:
            host: 127.0.0.1
            path: /healthz
            port: 10151
            scheme: HTTP
          initialDelaySeconds: 15
          timeoutSeconds: 15
        name: kube-scheduler-cron-container
        resources:
          requests:
            cpu: '0.1'
        volumeMounts:
        - mountPath: /etc/kubernetes/scheduler.conf
          name: kube-config
          readOnly: true
        - mountPath: /etc/localtime
          name: localtime
          readOnly: true
        - mountPath: /etc/kubernetes/scheduler-custom.conf
          name: scheduler-config
          readOnly: true
        - mountPath: /etc/kubernetes/scheduler-custom-policy-config.json
          name: policy-config
          readOnly: true
      hostNetwork: true
      priorityClassName: system-cluster-critical
      volumes:
      - hostPath:
          path: /etc/kubernetes/scheduler.conf
          type: FileOrCreate
        name: kube-config
      - hostPath:
          path: /etc/localtime
        name: localtime
      - hostPath:
          path: /etc/kubernetes/scheduler-custom.conf
          type: FileOrCreate
        name: scheduler-config
      - hostPath:
          path: /etc/kubernetes/scheduler-custom-policy-config.json
          type: FileOrCreate
        name: policy-config

Коратка па асноўных зменах:

  1. Змянілі імя пода і кантэйнера на kube-scheduler-cron
  2. Паказалі выкарыстанне партоў 10151 і 10159, бо вызначана опцыя. hostNetwork: true і мы не можам выкарыстоўваць тыя ж парты, што і дэфолтны kube-scheduler (10251 і 10259)
  3. З дапамогай параметра -config паказалі файл канфігурацыі з якой павінен запускацца сэрвіс
  4. Наладзілі мантаванне файла канфігурацыі (scheduler-custom.conf) і файла палітык планавання (scheduler-custom-policy-config.json) з хаста

Не забываем, што нашаму kube-scheduler'у запатрабуюцца правы, аналагічныя дэфолтнаму. Рэдагуем яго кластарную ролю:

kubectl edit clusterrole system:kube-scheduler

...
   resourceNames:
    - kube-scheduler
    - kube-scheduler-cron
...

Цяпер пагаворым аб тым, што павінна змяшчацца ў файле канфігурацыі і файле з палітыкамі планавання:

  • Файл канфігурацыі (scheduler-custom.conf)
    Для атрымання канфігурацыі дэфолтнага kube-scheduler'a неабходна скарыстацца параметрам --write-config-to з дакументацыі. Атрыманую канфігурацыю размесцім у файле /etc/kubernetes/scheduler-custom.conf і прывядзем да наступнага ўвазе:

apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
schedulerName: kube-scheduler-cron
bindTimeoutSeconds: 600
clientConnection:
  acceptContentTypes: ""
  burst: 100
  contentType: application/vnd.kubernetes.protobuf
  kubeconfig: /etc/kubernetes/scheduler.conf
  qps: 50
disablePreemption: false
enableContentionProfiling: false
enableProfiling: false
failureDomains: kubernetes.io/hostname,failure-domain.beta.kubernetes.io/zone,failure-domain.beta.kubernetes.io/region
hardPodAffinitySymmetricWeight: 1
healthzBindAddress: 0.0.0.0:10151
leaderElection:
  leaderElect: true
  leaseDuration: 15s
  lockObjectName: kube-scheduler-cron
  lockObjectNamespace: kube-system
  renewDeadline: 10s
  resourceLock: endpoints
  retryPeriod: 2s
metricsBindAddress: 0.0.0.0:10151
percentageOfNodesToScore: 0
algorithmSource:
   policy:
     file:
       path: "/etc/kubernetes/scheduler-custom-policy-config.json"

Коратка па асноўных зменах:

  1. Задалі ў schedulerName імя нашага сэрвісу kube-scheduler-cron.
  2. У параметры lockObjectName таксама трэба задаць імя нашага сэрвісу і пераканацца, што параметр leaderElect выстаўлены ў значэнне true (у выпадку, калі ў вас адна майстар-нода, можна выставіць значэнне false).
  3. Указалі шлях да файла з апісаннем палітык планавання ў параметры algorithmSource.

Варта больш падрабязна спыніцца на другім пункце, дзе мы рэдагуемы параметры для ключа leaderElection. Для забеспячэння адмоваўстойлівасці мы актывавалі (leaderElect) працэс выбару кіроўнага (майстра) паміж подамі нашага kube-scheduler'a з дапамогай выкарыстання адзінага для іх endpoint (resourceLock) з імем kube-scheduler-cron (lockObjectName) у прасторы імёнаў kube-system (lockObjectNamespace). Аб тым як у Kubernetes забяспечваецца высокая даступнасць асноўных кампанентаў (у тым ліку kube-scheduler) можна азнаёміцца ​​ў артыкуле.

  • Файл палітык планавання (scheduler-custom-policy-config.json)
    Як я ўжо пісаў раней – даведацца з якімі канкрэтна палітыкамі працуе дэфолтны kube-scheduler мы можам толькі аналізуючы ягоны код. Гэта значыць, мы не можам атрымаць файл з палітыкамі планавання дэфолтнага kube-scheduler'а па аналогіі з файлам канфігурацыі. Апішам якія цікавяць нас палітыкі планавання ў файле /etc/kubernetes/scheduler-custom-policy-config.json наступным чынам:

{
  "kind": "Policy",
  "apiVersion": "v1",
  "predicates": [
    {
      "name": "GeneralPredicates"
    }
  ],
  "priorities": [
    {
      "name": "ServiceSpreadingPriority",
      "weight": 1
    },
    {
      "name": "EqualPriority",
      "weight": 1
    },
    {
      "name": "LeastRequestedPriority",
      "weight": 1
    },
    {
      "name": "NodePreferAvoidPodsPriority",
      "weight": 10000
    },
    {
      "name": "NodeAffinityPriority",
      "weight": 1
    }
  ],
  "hardPodAffinitySymmetricWeight" : 10,
  "alwaysCheckAllPredicates" : false
}

Такім чынам, kube-scheduler спачатку складае спіс нод, на якія можа быць запланаваны пад у адпаведнасці з палітыкай GeneralPredicates (якая ўключае ў сябе набор палітык PodFitsResources, PodFitsHostPorts, HostName і MatchNodeSelector). І далей праводзіцца ацэнка кожнай ноды ў адпаведнасці з наборам палітык у масіве priorities. Для выкананьня ўмоваў нашай задачы мы палічылі, што такі набор палітык будзе аптымальным рашэньнем. Нагадаю, што набор палітык з іх падрабязным апісаннем даступны ў дакументацыі. Для выканання сваёй задачы вы можаце проста змяніць набор палітык і прызначыць ім адпаведныя вагі.

Маніфест новага kube-scheduler'а, які мы стваралі ў пачатку раздзела, назавём kube-scheduler-custom.yaml і размесцім па наступным шляху /etc/kubernetes/manifests на трох майстар-нодах. Калі ўсё выканана правільна, Kubelet на кожнай нодзе запусціць пад, а ў логах нашага новага kube-scheduler'а мы ўбачым інфармацыю аб тым, што наш файл з палітыкамі паспяхова ўжыўся:

Creating scheduler from configuration: {{ } [{GeneralPredicates <nil>}] [{ServiceSpreadingPriority 1 <nil>} {EqualPriority 1 <nil>} {LeastRequestedPriority 1 <nil>} {NodePreferAvoidPodsPriority 10000 <nil>} {NodeAffinityPriority 1 <nil>}] [] 10 false}
Registering predicate: GeneralPredicates
Predicate type GeneralPredicates already registered, reusing.
Registering priority: ServiceSpreadingPriority
Priority type ServiceSpreadingPriority already registered, reusing.
Registering priority: EqualPriority
Priority type EqualPriority already registered, reusing.
Registering priority: LeastRequestedPriority
Priority type LeastRequestedPriority already registered, reusing.
Registering priority: NodePreferAvoidPodsPriority
Priority type NodePreferAvoidPodsPriority already registered, reusing.
Registering priority: NodeAffinityPriority
Priority type NodeAffinityPriority already registered, reusing.
Creating scheduler with fit predicates 'map[GeneralPredicates:{}]' and priority functions 'map[EqualPriority:{} LeastRequestedPriority:{} NodeAffinityPriority:{} NodePreferAvoidPodsPriority:{} ServiceSpreadingPriority:{}]'

Зараз застаецца толькі паказаць у spec'е нашай CronJob'ы, што ўсе запыты на планаванне яе pod'аў павінен апрацоўваць наш новы kube-scheduler:

...
 jobTemplate:
    spec:
      template:
        spec:
          schedulerName: kube-scheduler-cron
...

Заключэнне

У канчатковым выніку мы атрымалі дадатковы kube-scheduler з унікальным наборам палітык планавання, за працай якога сочыць непасрэдна kubelet. Акрамя таго мы настроілі выбары новага лідэра паміж подамі нашага kube-scheduler'а ў выпадку, калі стары лідэр па нейкіх прычынах становіцца недаступны.

Звычайныя прыкладанні і сэрвісы працягваюць планавацца праз дэфолтны kube-scheduler, а ўсе cron-задачы цалкам перакладзены на новы. Нагрузка, якая ствараецца cron-задачамі, зараз раўнамерна размяркоўваецца па ўсіх нодах. Улічваючы, што большая частка cron-задач выконваецца на тых жа нодах, што і асноўныя прыкладанні праекта, гэта дазволіла значна знізіць рызыку пераезду падоў з-за недахопу рэсурсаў. Пасля ўкаранення дадатковага kube-scheduler'а, праблем з нераўнамерным планаваннем cron-задач больш не ўзнікала.

Таксама чытайце іншыя артыкулы ў нашым блогу:

Крыніца: habr.com

Дадаць каментар