Tworzenie dodatkowego harmonogramu kube z niestandardowym zestawem reguł planowania

Tworzenie dodatkowego harmonogramu kube z niestandardowym zestawem reguł planowania

Kube-scheduler to integralny komponent Kubernetesa, który odpowiada za planowanie podów pomiędzy węzłami zgodnie z określonymi politykami. Często podczas działania klastra Kubernetes nie musimy się zastanawiać, jakie polityki służą do planowania podów, gdyż zestaw polityk domyślnego kube-scheduler nadaje się do większości codziennych zadań. Są jednak sytuacje, gdy ważne jest dla nas dopracowanie procesu przydzielania podów i są na to dwa sposoby, aby zrealizować to zadanie:

  1. Utwórz harmonogram kube z niestandardowym zestawem reguł
  2. Napisz własny harmonogram i naucz go obsługi żądań serwera API

W tym artykule opiszę realizację punktu pierwszego, aby rozwiązać problem nierównomiernego rozmieszczenia palenisk na jednym z naszych projektów.

Krótkie wprowadzenie do działania kube-scheduler

Warto szczególnie zwrócić uwagę na fakt, że kube-scheduler nie odpowiada za bezpośrednie planowanie podów - odpowiada jedynie za określenie węzła, na którym zostanie umieszczony pod. Inaczej mówiąc, efektem pracy kube-scheduler jest nazwa węzła, który zwraca do serwera API w celu wysłania żądania harmonogramu i na tym kończy się jego praca.

Najpierw kube-scheduler kompiluje listę węzłów, na których można zaplanować pod zgodnie z polityką predykatów. Następnie każdy węzeł z tej listy otrzymuje określoną liczbę punktów zgodnie z polityką priorytetów. W rezultacie wybierany jest węzeł z maksymalną liczbą punktów. Jeśli istnieją węzły, które mają ten sam maksymalny wynik, wybierany jest losowy. Listę i opis zasad predykatów (filtrowania) i priorytetów (punktowania) można znaleźć w dokumentacja.

Opis treści problemu

Pomimo dużej liczby różnych klastrów Kubernetes utrzymywanych w Nixys, po raz pierwszy zetknęliśmy się z problemem planowania podów dopiero niedawno, gdy jeden z naszych projektów wymagał uruchomienia dużej liczby zadań okresowych (~100 jednostek CronJob). Aby maksymalnie uprościć opis problemu, jako przykład weźmiemy mikroserwis, w ramach którego raz na minutę uruchamiane jest zadanie cron, powodując pewne obciążenie procesora. Do uruchomienia zadania cron przydzielono trzy węzły o absolutnie identycznych charakterystykach (po 24 vCPU na każdym).

Jednocześnie nie można dokładnie określić, ile czasu zajmie wykonanie CronJob, ponieważ ilość danych wejściowych stale się zmienia. Średnio podczas normalnej pracy kube-scheduler każdy węzeł uruchamia 3-4 instancje zadań, które powodują ~20-30% obciążenia procesora każdego węzła:

Tworzenie dodatkowego harmonogramu kube z niestandardowym zestawem reguł planowania

Problem sam w sobie polega na tym, że czasami pody zadań cron przestały być planowane w jednym z trzech węzłów. Oznacza to, że w pewnym momencie dla jednego z węzłów nie zaplanowano ani jednego poda, podczas gdy na pozostałych dwóch węzłach działało 6-8 kopii zadania, tworząc ~40-60% obciążenia procesora:

Tworzenie dodatkowego harmonogramu kube z niestandardowym zestawem reguł planowania

Problem powtarzał się z całkowicie losową częstotliwością i czasami był powiązany z momentem wdrożenia nowej wersji kodu.

Zwiększając poziom rejestrowania kube-scheduler do poziomu 10 (-v=10), zaczęliśmy rejestrować, ile punktów zdobył każdy węzeł w procesie oceny. Podczas normalnego planowania w dziennikach mogą być widoczne następujące informacje:

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

Te. sądząc po informacjach uzyskanych z logów, każdy z węzłów uzyskał jednakową liczbę punktów końcowych, a do planowania wybrano losowo jeden z węzłów. W momencie problematycznego planowania dzienniki wyglądały tak:

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

Z czego wynika, że ​​jeden z węzłów uzyskał mniej punktów końcowych niż pozostałe, dlatego planowanie przeprowadzono tylko dla dwóch węzłów, które uzyskały maksymalną liczbę punktów. Tym samym byliśmy zdecydowanie przekonani, że problem leży właśnie w harmonogramowaniu strąków.

Dalszy algorytm rozwiązania problemu był dla nas oczywisty - przeanalizuj logi, dowiedz się, jakim priorytetem węzeł nie zdobył punktów i, jeśli to konieczne, dostosuj zasady domyślnego harmonogramu kube. Natrafiamy tu jednak na dwie istotne trudności:

  1. Na maksymalnym poziomie logowania (10) uwzględniane są punkty zdobyte tylko za niektóre priorytety. Z powyższego fragmentu logów widać, że dla wszystkich priorytetów odzwierciedlonych w logach węzły zdobywają tę samą liczbę punktów w harmonogramowaniu normalnym i problemowym, ale ostateczny wynik w przypadku planowania problemów jest inny. Można zatem stwierdzić, że dla niektórych priorytetów punktacja odbywa się „za kulisami” i nie mamy możliwości zrozumienia, za jaki priorytet węzeł nie otrzymał punktów. Szczegółowo opisaliśmy ten problem w problem Repozytorium Kubernetes na Githubie. W chwili pisania tego tekstu otrzymano odpowiedź od programistów, że obsługa rejestrowania zostanie dodana w aktualizacjach Kubernetes v1.15,1.16, 1.17 i XNUMX.
  2. Nie ma łatwego sposobu, aby zrozumieć, z jakim konkretnym zestawem zasad aktualnie pracuje kube-scheduler. Tak w dokumentacja lista ta znajduje się na liście, ale nie zawiera informacji o tym, jakie konkretne wagi są przypisane do poszczególnych polityk priorytetowych. Możesz zobaczyć wagi lub edytować zasady domyślnego programu kube-scheduler tylko w kody źródłowe.

Warto zaznaczyć, że raz udało nam się odnotować, że węzeł nie otrzymał punktów zgodnie z polityką ImageLocalityPriority, która przyznaje punkty węzłowi, jeśli posiada już obraz niezbędny do uruchomienia aplikacji. Oznacza to, że w momencie wdrożenia nowej wersji aplikacji zadanie cron udało się uruchomić na dwóch węzłach, pobierając do nich nowy obraz z rejestru dokerów, dzięki czemu dwa węzły uzyskały wyższą ocenę końcową w stosunku do trzeciego .

Tak jak pisałem wyżej w logach nie widzimy informacji o ocenie polityki ImageLocalityPriority, dlatego w celu sprawdzenia naszego założenia zrzuciliśmy obraz z nową wersją aplikacji na trzeci węzeł, po czym harmonogram zadziałał poprawnie . To właśnie ze względu na politykę ImageLocalityPriority problem z harmonogramem był obserwowany dość rzadko, częściej był on powiązany z czymś innym. W związku z tym, że nie mogliśmy w pełni zdebugować każdej z polityk znajdujących się na liście priorytetów domyślnego kube-scheduler, pojawiła się potrzeba elastycznego zarządzania politykami planowania podów.

Stwierdzenie problemu

Chcieliśmy, aby rozwiązanie problemu było jak najbardziej konkretne, czyli główne byty Kubernetesa (tutaj mamy na myśli domyślny kube-scheduler) powinny pozostać niezmienione. Nie chcieliśmy rozwiązać problemu w jednym miejscu i stworzyć go w innym. W ten sposób doszliśmy do dwóch możliwości rozwiązania problemu, które zostały ogłoszone we wstępie do artykułu - utworzenie dodatkowego harmonogramu lub napisanie własnego. Głównym wymogiem planowania zadań cron jest równomierne rozłożenie obciążenia na trzy węzły. Wymaganie to można spełnić za pomocą istniejących polityk kube-scheduler, więc aby rozwiązać nasz problem, nie ma sensu pisać własnego harmonogramu.

Instrukcje dotyczące tworzenia i wdrażania dodatkowego harmonogramu kube są opisane w dokumentacja. Wydawało nam się jednak, że encja Deployment nie wystarczy, aby zapewnić odporność na awarie w działaniu tak krytycznej usługi jak kube-scheduler, dlatego postanowiliśmy wdrożyć nowy kube-scheduler jako Static Pod, który będzie bezpośrednio monitorowany przez Kubelet. Zatem mamy następujące wymagania dla nowego programu kube-scheduler:

  1. Usługę należy wdrożyć jako moduł statyczny na wszystkich wzorcach klastra
  2. Należy zapewnić tolerancję na błędy w przypadku, gdy aktywny pod z programem kube-scheduler jest niedostępny
  3. Głównym priorytetem przy planowaniu powinna być liczba dostępnych zasobów w węźle (LeastRequestedPriority)

Rozwiązania wdrożeniowe

Warto od razu zaznaczyć, że całość prac będziemy wykonywać w Kubernetesie v1.14.7, gdyż To jest wersja, która została wykorzystana w projekcie. Zacznijmy od napisania manifestu dla naszego nowego kube-scheduler. Weźmy jako podstawę domyślny manifest (/etc/kubernetes/manifests/kube-scheduler.yaml) i sprowadźmy go do następującej postaci:

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

Krótko o głównych zmianach:

  1. Zmieniono nazwę kapsuły i kontenera na kube-scheduler-cron
  2. Określono użycie portów 10151 i 10159 jako zdefiniowaną opcję hostNetwork: true i nie możemy używać tych samych portów, co domyślny program kube-scheduler (10251 i 10259)
  3. Za pomocą parametru --config określiliśmy plik konfiguracyjny, z którym należy uruchomić usługę
  4. Skonfigurowane montowanie pliku konfiguracyjnego (scheduler-custom.conf) i pliku zasad planowania (scheduler-custom-policy-config.json) z hosta

Nie zapominaj, że nasz kube-scheduler będzie potrzebował uprawnień podobnych do domyślnych. Edytuj rolę klastra:

kubectl edit clusterrole system:kube-scheduler

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

Porozmawiajmy teraz o tym, co powinno znajdować się w pliku konfiguracyjnym i pliku zasad planowania:

  • Plik konfiguracyjny (scheduler-custom.conf)
    Aby uzyskać domyślną konfigurację kube-scheduler, należy użyć parametru --write-config-to z dokumentacja. Powstałą konfigurację umieścimy w pliku /etc/kubernetes/scheduler-custom.conf i sprowadzimy do poniższej postaci:

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"

Krótko o głównych zmianach:

  1. UstawiamyharmonogramName na nazwę naszej usługi kube-scheduler-cron.
  2. W parametrze lockObjectName musisz także ustawić nazwę naszej usługi i upewnić się, że parametr leaderElect ustaw na true (jeśli masz jeden węzeł główny, możesz ustawić go na false).
  3. Określono ścieżkę do pliku z opisem zasad planowania w parametrze algorithmSource.

Warto przyjrzeć się bliżej drugiemu punktowi, w którym edytujemy parametry klucza leaderElection. Aby zapewnić odporność na błędy, włączyliśmy (leaderElect) proces wybierania lidera (mastera) pomiędzy podami naszego kube-scheduler przy użyciu dla nich jednego punktu końcowego (resourceLock) o nazwie kube-scheduler-cron (lockObjectName) w przestrzeni nazw kube-system (lockObjectNamespace). Jak Kubernetes zapewnia wysoką dostępność głównych komponentów (w tym kube-scheduler) można znaleźć w Artykuł.

  • Plik zasad planowania (scheduler-custom-policy-config.json)
    Jak pisałem wcześniej, z jakimi konkretnymi politykami współpracuje domyślny kube-scheduler, możemy dowiedzieć się jedynie analizując jego kod. Oznacza to, że nie możemy uzyskać pliku z zasadami planowania dla domyślnego kube-scheduler w taki sam sposób, jak plik konfiguracyjny. Opiszmy interesujące nas polityki planowania w pliku /etc/kubernetes/scheduler-custom-policy-config.json w następujący sposób:

{
  "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
}

Zatem kube-scheduler najpierw kompiluje listę węzłów, do których można zaplanować pod zgodnie z polityką GeneralPredicates (która obejmuje zestaw zasad PodFitsResources, PodFitsHostPorts, HostName i MatchNodeSelector). Następnie każdy węzeł jest oceniany zgodnie z zestawem zasad w tablicy priorytetów. Aby spełnić warunki naszego zadania, uznaliśmy, że taki zestaw polityk będzie optymalnym rozwiązaniem. Przypominam, że zbiór polis wraz z ich szczegółowym opisem dostępny jest w dokumentacja. Aby zrealizować swoje zadanie, możesz po prostu zmienić zestaw stosowanych polityk i przypisać im odpowiednie wagi.

Nazwijmy manifest nowego kube-scheduler, który stworzyliśmy na początku rozdziału, kube-scheduler-custom.yaml i umieść go w następującej ścieżce /etc/kubernetes/manifests na trzech głównych węzłach. Jeśli wszystko zostanie wykonane poprawnie, Kubelet uruchomi poda na każdym węźle, a w logach naszego nowego kube-scheduler zobaczymy informację, że nasz plik polityki został pomyślnie zastosowany:

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:{}]'

Teraz pozostaje tylko wskazać w specyfikacji naszego CronJob, że wszystkie żądania dotyczące planowania jego podów powinny być przetwarzane przez nasz nowy kube-scheduler:

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

wniosek

Docelowo otrzymaliśmy dodatkowy kube-scheduler z unikalnym zestawem polityk planowania, którego działanie jest monitorowane bezpośrednio przez kubelet. Dodatkowo ustawiliśmy wybór nowego lidera pomiędzy podami naszego kube-scheduler na wypadek, gdyby stary lider z jakiegoś powodu stał się niedostępny.

Regularne aplikacje i usługi są nadal planowane za pomocą domyślnego programu kube-scheduler, a wszystkie zadania cron zostały całkowicie przeniesione do nowego. Obciążenie utworzone przez zadania cron jest teraz równomiernie rozłożone na wszystkie węzły. Biorąc pod uwagę, że większość zadań cron jest wykonywana w tych samych węzłach, co główne aplikacje projektu, znacznie zmniejszyło to ryzyko przenoszenia podów z powodu braku zasobów. Po wprowadzeniu dodatkowego kube-scheduler nie pojawiały się już problemy z nierównym planowaniem zadań cron.

Przeczytaj także inne artykuły na naszym blogu:

Źródło: www.habr.com

Dodaj komentarz