Erstellung eines zusÀtzlichen kube-schedulers mit benutzerdefiniertem Regelwerk

Erstellung eines zusÀtzlichen kube-schedulers mit benutzerdefiniertem Regelwerk

Der Kube-Scheduler ist ein wesentlicher Bestandteil von Kubernetes, der fĂŒr die Planung von Pods auf Knoten gemĂ€ĂŸ festgelegten Richtlinien verantwortlich ist. Oft mĂŒssen wir uns im Betrieb eines Kubernetes-Clusters keine Gedanken darĂŒber machen, nach welchen Richtlinien Pods geplant werden, da der Standardset an Richtlinien des kube-schedulers fĂŒr die meisten alltĂ€glichen Aufgaben geeignet ist. Es gibt jedoch Situationen, in denen wir den Prozess der Verteilung von Pods feiner steuern möchten, und dafĂŒr gibt es zwei AnsĂ€tze:

  1. Einen kube-scheduler mit einem benutzerdefinierten Regelwerk erstellen
  2. Einen eigenen Scheduler schreiben und ihn beibringen, mit Anfragen an den API-Server zu arbeiten

In diesem Artikel werde ich die Umsetzung des ersten Ansatzes zur Lösung des Problems der ungleichen Pod-Planung in einem unserer Projekte beschreiben.

Kurze EinfĂŒhrung in die Funktionsweise des kube-schedulers

Es ist besonders wichtig zu erwĂ€hnen, dass der kube-scheduler nicht fĂŒr die unmittelbare Planung von Pods verantwortlich ist – er bestimmt lediglich den Knoten, auf dem der Pod platziert werden soll. Anders ausgedrĂŒckt, das Ergebnis der Arbeit des kube-schedulers ist der Name des Knotens, den er dem API-Server auf die Anfrage zur Planung zurĂŒckgibt, und damit endet seine Arbeit.

ZunĂ€chst erstellt der kube-scheduler eine Liste von Knoten, auf die ein Pod gemĂ€ĂŸ den Vorgaben der Politiken geplant werden kann. Danach erhĂ€lt jeder Knoten aus dieser Liste eine bestimmte Anzahl von Punkten gemĂ€ĂŸ den PrioritĂ€ten. Als Ergebnis wird der Knoten ausgewĂ€hlt, der die höchste Punktzahl erreicht hat. Wenn mehrere Knoten die gleiche maximale Punktzahl erreicht haben, wird zufĂ€llig einer ausgewĂ€hlt. Eine Liste und eine Beschreibung der Richtlinien fĂŒr Predicates (Filterung) und PrioritĂ€ten (Bewertung) finden Sie in Dokumentation.

Beschreibung des Problembereichs

Trotz der großen Anzahl verschiedener Kubernetes-Cluster, die bei Nixys verwaltet werden, sind wir erst kĂŒrzlich auf ein Problem mit der Pod-Planung gestoßen, als fĂŒr eines unserer Projekte die Notwendigkeit entstand, eine große Anzahl periodischer Aufgaben (~100 CronJob-EntitĂ€ten) auszufĂŒhren. Um die Beschreibung des Problems zu vereinfachen, nehmen wir als Beispiel einen Microservice, innerhalb dessen einmal pro Minute eine Cron-Aufgabe gestartet wird, die eine gewisse CPU-Last erzeugt. FĂŒr die AusfĂŒhrung der Cron-Aufgabe wurden drei völlig identische Knoten (jeweils 24 vCPUs) bereitgestellt.

Es lĂ€sst sich jedoch nicht genau sagen, wie lange der CronJob ausgefĂŒhrt wird, da das Volumen der Eingangsdaten stĂ€ndig variiert. Im Durchschnitt arbeiten bei normalem Betrieb des kube-schedulers 3-4 Instanzen der Aufgabe auf jedem Knoten, die etwa 20-30% der CPU-Last jedes Knotens erzeugen:

Erstellung eines zusÀtzlichen kube-schedulers mit benutzerdefiniertem Regelwerk

Das eigentliche Problem besteht darin, dass die Pods der Cron-Aufgabe manchmal auf einem der drei Knoten nicht mehr geplant wurden. Das bedeutet, dass zu einem bestimmten Zeitpunkt auf einem der Knoten kein einziger Pod geplant wurde, wÀhrend auf den beiden anderen Knoten jeweils 6-8 Instanzen der Aufgabe arbeiteten und etwa 40-60% der CPU-Last erzeugten:

Erstellung eines zusÀtzlichen kube-schedulers mit benutzerdefiniertem Regelwerk

Das Problem trat mit absolut zufÀlliger PeriodizitÀt auf und korrelierte gelegentlich mit dem Zeitpunkt der Bereitstellung einer neuen Codeversion.

Indem wir die Protokollierungsstufe des kube-schedulers auf 10 (-v=10) erhöhten, begannen wir zu erfassen, wie viele Punkte jede der Knoten im Bewertungsprozess gesammelt hat. Bei normaler Planung konnte im Protokoll folgende Information gesehen werden:

resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node03: Ausgewogene Ressourcenallokation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 1387 Millicores 4161694720 Speicherbytes, Punktzahl 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node02: Ausgewogene Ressourcenallokation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 1347 Millicores 4444810240 Speicherbytes, Punktzahl 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node03: Minimale Ressourcenallokation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 1387 Millicores 4161694720 Speicherbytes, Punktzahl 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node01: Ausgewogene Ressourcenallokation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 1687 Millicores 4790840320 Speicherbytes, Punktzahl 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node02: Minimale Ressourcenallokation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 1347 Millicores 4444810240 Speicherbytes, Punktzahl 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node01: Minimale Ressourcenallokation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 1687 Millicores 4790840320 Speicherbytes, Punktzahl 9
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: NodeAffinityPriority, Punktzahl: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: NodeAffinityPriority, Punktzahl: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: NodeAffinityPriority, Punktzahl: (0)                                                                                       
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node01: InterPodAffinityPriority, Punktzahl: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: TaintTolerationPriority, Punktzahl: (10)                                                                                   
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node02: InterPodAffinityPriority, Punktzahl: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: TaintTolerationPriority, Punktzahl: (10)                                                                                   
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node01: SelectorSpreadPriority, Punktzahl: (10)                                                                                                        
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node03: InterPodAffinityPriority, Punktzahl: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: TaintTolerationPriority, Punktzahl: (10)                                                                                   
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node02: SelectorSpreadPriority, Punktzahl: (10)                                                                                                        
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node03: SelectorSpreadPriority, Punktzahl: (10)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: SelectorSpreadPriority, Punktzahl: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: SelectorSpreadPriority, Punktzahl: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: SelectorSpreadPriority, Punktzahl: (10)                                                                                    
generic_scheduler.go:781] Host Node01 -> Punktzahl 100043                                                                                                                                                                        
generic_scheduler.go:781] Host Node02 -> Punktzahl 100043                                                                                                                                                                        
generic_scheduler.go:781] Host Node03 -> Punktzahl 100043

Das heißt, basierend auf den Informationen aus den Protokollen, erhielt jede der Knoten eine gleichmĂ€ĂŸige Anzahl an Endpunkten, und es wurde eine zufĂ€llige fĂŒr die Planung ausgewĂ€hlt. Zum Zeitpunkt der problematischen Planung sahen die Protokolle wie folgt aus:

resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node02: BalancedResourceAllocation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 1587 Millicores 4581125120 Speicherbytes, Punktzahl 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node03: BalancedResourceAllocation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 1087 Millicores 3532549120 Speicherbytes, Punktzahl 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node02: LeastResourceAllocation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 1587 Millicores 4581125120 Speicherbytes, Punktzahl 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node01: BalancedResourceAllocation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 987 Millicores 3322833920 Speicherbytes, Punktzahl 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node01: LeastResourceAllocation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 987 Millicores 3322833920 Speicherbytes, Punktzahl 9 
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node03: LeastResourceAllocation, KapazitÀt 23900 Millicores 67167186944 Speicherbytes, Gesamtanforderung 1087 Millicores 3532549120 Speicherbytes, Punktzahl 9
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node03: InterPodAffinityPriority, Punktzahl: (0)                                                                                                        
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node02: InterPodAffinityPriority, Punktzahl: (0)                                                                                                        
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node01: InterPodAffinityPriority, Punktzahl: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: TaintTolerationPriority, Punktzahl: (10)                                                                                   
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node03: SelectorSpreadPriority, Punktzahl: (10)                                                                                                        
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node02: SelectorSpreadPriority, Punktzahl: (10)                                                                                                        
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: TaintTolerationPriority, Punktzahl: (10)                                                                                   
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node01: SelectorSpreadPriority, Punktzahl: (10)                                                                                                        
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: NodeAffinityPriority, Punktzahl: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: SelectorSpreadPriority, Punktzahl: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: SelectorSpreadPriority, Punktzahl: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: TaintTolerationPriority, Punktzahl: (10)                                                                                   
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: NodeAffinityPriority, Punktzahl: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: NodeAffinityPriority, Punktzahl: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: SelectorSpreadPriority, Punktzahl: (10)                                                                                    
generic_scheduler.go:781] Host Node03 -> Punktzahl 100041                                                                                                                                                                        
generic_scheduler.go:781] Host Node02 -> Punktzahl 100041                                                                                                                                                                        
generic_scheduler.go:781] Host Node01 -> Punktzahl 100038

Es ist ersichtlich, dass einer der Knoten weniger Gesamtpunktzahl als die anderen erzielt hat, weshalb die Planung nur auf die beiden Knoten durchgefĂŒhrt wurde, die die maximale Punktzahl erreicht haben. Dadurch haben wir mit Sicherheit festgestellt, dass das Problem tatsĂ€chlich in der Planung der Pods liegt.

Der nĂ€chste Schritt zur Lösung des Problems war fĂŒr uns offensichtlich — die Protokolle zu analysieren, herauszufinden, nach welchem spezifischen PrioritĂ€tskriterium der Knoten nicht genug Punkte erzielt hat, und gegebenenfalls die Standard-Politiken des kube-schedulers anzupassen. Allerdings standen wir hier vor zwei wesentlichen Schwierigkeiten:

  1. Auf der höchsten Protokollebene (10) wird die Punktzahl nur fĂŒr einige PrioritĂ€ten angezeigt. Im obigen Log-Ausschnitt kann man erkennen, dass die Knoten bei normaler und problematischer Planung gleich viele Punkte fĂŒr alle im Protokoll aufgefĂŒhrten PrioritĂ€ten erzielen, das Endergebnis bei problematischer Planung jedoch abweicht. Daraus kann man schließen, dass fĂŒr einige PrioritĂ€ten die Punkte „im Hintergrund“ gezĂ€hlt werden, und wir haben keine Möglichkeit zu verstehen, nach welchem spezifischen PrioritĂ€tskriterium der Knoten nicht genug Punkte erzielt hat. Dieses Problem haben wir ausfĂŒhrlich in issue dem Kubernetes-Repository auf Github beschrieben. Zum Zeitpunkt des Schreibens des Artikels erhielten wir eine Antwort von den Entwicklern, dass die UnterstĂŒtzung des Protokollierens in den Aktualisierungen von Kubernetes v1.15, 1.16 und 1.17 hinzugefĂŒgt wird.
  2. Es gibt keinen einfachen Weg zu verstehen, mit welchem spezifischen Satz von Politiken der kube-scheduler derzeit arbeitet. Ja, in Dokumentation dieser Liste wird es aufgefĂŒhrt, aber es fehlen Informationen darĂŒber, welche spezifischen Gewichte jeweils fĂŒr die PrioritĂ€ten festgelegt sind. Die Gewichte zu sehen oder die Standard-Politiken des kube-schedulers zu bearbeiten, ist nur in Quellcodes.

Es ist erwĂ€hnenswert, dass es uns einmal gelungen ist festzustellen, dass der Knoten keine Punkte fĂŒr die Politik 'ImageLocalityPriority' erzielt hat, die Punkte an den Knoten vergibt, wenn sich darauf bereits ein Image befindet, das fĂŒr den Start der Anwendung benötigt wird. Das heißt, im Moment der Bereitstellung einer neuen Version der Anwendung konnte der cron-Job auf zwei Knoten gestartet werden, wĂ€hrend er das neue Image aus dem Docker-Registry zog, und so erhielten die beiden Knoten eine höhere Gesamtpunktzahl im Vergleich zum dritten.

Wie ich bereits oben erwĂ€hnt habe, sehen wir in den Protokollen keine Informationen zur Bewertung der Politik ImageLocalityPriority. Um meine Vermutung zu ĂŒberprĂŒfen, haben wir ein Image mit der neuen Version der Anwendung auf den dritten Knoten gespult, woraufhin das Scheduling korrekt funktionierte. Gerade aufgrund der Politik ImageLocalityPriority trat das Scheduling-Problem eher selten auf; hĂ€ufiger war es mit etwas anderem verbunden. Da wir nicht in der Lage waren, jede der Politiken in der Liste der PrioritĂ€ten des Standard-kube-schedulers vollstĂ€ndig zu debuggen, benötigten wir ein flexibles Management der Pod-Scheduling-Politiken.

Aufgabenstellung

Wir wollten, dass die Lösung des Problems so gezielt wie möglich ist, das heißt, die HauptentitĂ€ten von Kubernetes (hier ist der Standard-kube-scheduler gemeint) sollten unverĂ€ndert bleiben. Wir wollten das Problem nicht an einem Ort lösen und es woanders schaffen. So kamen wir auf zwei Lösungsmöglichkeiten, die im EinfĂŒhrungsteil des Artikels genannt wurden — die Erstellung eines zusĂ€tzlichen schedulers oder das Schreiben eines eigenen. Das Hauptkriterium fĂŒr die Planung von Cron-Jobs war die gleichmĂ€ĂŸige Verteilung der Last auf drei Knoten. Dieses Kriterium kann bereits mit den bestehenden Politiken des kube-schedulers erfĂŒllt werden, daher macht es keinen Sinn, einen eigenen Scheduler fĂŒr unsere Aufgabe zu schreiben.

Die Anleitung zur Erstellung und Bereitstellung eines zusĂ€tzlichen kube-schedulers ist in Dokumentation. Allerdings erschien es uns, dass die EntitĂ€ten Deployment nicht ausreichend sind, um die Ausfallsicherheit eines so kritischen Dienstes wie kube-scheduler zu gewĂ€hrleisten, weshalb wir beschlossen, einen neuen kube-scheduler als Static Pod bereitzustellen, den Kubelet direkt ĂŒberwachen wird. Folglich ergeben sich die folgenden Anforderungen an den neuen kube-scheduler:

  1. Der Dienst muss als Static Pod auf allen Master-Knoten des Clusters bereitgestellt werden.
  2. Es muss eine Ausfallsicherheit fĂŒr den Fall der NichtverfĂŒgbarkeit des aktiven Pods mit kube-scheduler vorgesehen sein.
  3. Der Hauptfokus beim Scheduling sollte auf der Anzahl verfĂŒgbarer Ressourcen auf dem Knoten (LeastRequestedPriority) liegen.

Implementierung der Lösung

Es sollte erwĂ€hnt werden, dass wir alle Arbeiten in Kubernetes v1.14.7 durchfĂŒhren werden, da genau diese Version im Projekt verwendet wurde. Wir beginnen mit dem Schreiben des Manifests fĂŒr unseren neuen kube-scheduler. Wir nehmen das Manifest des Standard-schedulers (\/etc\/kubernetes\/manifests\/kube-scheduler.yaml) als Grundlage und bringen es in folgende Form:

art: Pod
metadaten:
  labels:
    komponent: scheduler
    ebene: control-plane
  name: kube-scheduler-cron
  namespace: kube-system
spec:
      container:
      - befehl:
        - /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
        bild: 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
        ressourcen:
          anfragen:
            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

Kurze Übersicht der wesentlichen Änderungen:

  1. Wir haben den Namen des Pods und des Containers auf kube-scheduler-cron geÀndert.
  2. Wir haben die Verwendung der Ports 10151 und 10159 angegeben, da eine Option definiert ist. hostNetwork: true und wir können nicht dieselben Ports wie der Standard-kube-scheduler (10251 und 10259) verwenden.
  3. Mit dem Parameter —config haben wir die Konfigurationsdatei angegeben, mit der der Dienst gestartet werden soll.
  4. Wir haben das EinhÀngen der Konfigurationsdatei (scheduler-custom.conf) und der Planungsrichtliniendatei (scheduler-custom-policy-config.json) vom Host konfiguriert.

Vergessen wir nicht, dass unser kube-scheduler dieselben Rechte benötigt wie der Standard. Wir bearbeiten seine Cluster-Rolle:

kubectl edit clusterrole system:kube-scheduler

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

Jetzt sprechen wir darĂŒber, was in der Konfigurationsdatei und der Datei mit den Planungsrichtlinien enthalten sein sollte:

  • Konfigurationsdatei (scheduler-custom.conf)
    Um die Konfiguration des Standard-kube-schedulers zu erhalten, muss der Parameter genutzt werden. --write-config-to aus Dokumentation. Die erhaltene Konfiguration wird in der Datei /etc/kubernetes/scheduler-custom.conf abgelegt und erhÀlt folgende Form:

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"

Kurze Übersicht der wesentlichen Änderungen:

  1. Wir haben im schedulerName den Namen unseres Dienstes kube-scheduler-cron festgelegt.
  2. Im Parameter lockObjectName muss ebenfalls der Name unseres Dienstes angegeben werden, und wir mĂŒssen sicherstellen, dass der Parameter leaderElect auf true gesetzt ist (wenn Sie einen Master-Knoten haben, kann der Wert auch auf false gesetzt werden).
  3. Der Pfad zur Datei mit der Beschreibung der Planungsrichtlinien wurde im Parameter algorithmSource.

sollte nĂ€her erlĂ€utert werden, wo wir die Parameter fĂŒr den SchlĂŒssel leaderElectionbearbeiten. Um die Ausfallsicherheit zu gewĂ€hrleisten, haben wir den (leaderElect) Prozess der Wahl des Leiters (Masters) zwischen den Pods unseres kube-schedulers aktiviert, indem wir einen gemeinsamen Endpoint verwendet haben, der fĂŒr sie vorgesehen ist (resourceLock) mit dem Namen kube-scheduler-cron (lockObjectName) im Namensraum kube-system (lockObjectNamespace). Informationen zur GewĂ€hrleistung der hohen VerfĂŒgbarkeit der Hauptkomponenten (einschließlich kube-scheduler) in Kubernetes finden Sie in Artikel.

  • Die Datei der Planungsrichtlinien (scheduler-custom-policy-config.json)
    Wie ich bereits zuvor erwĂ€hnt habe – wir können nur durch die Analyse seines Codes herausfinden, mit welchen spezifischen Richtlinien der Standard-kube-scheduler arbeitet. Das bedeutet, dass wir keine Datei mit den Planungsrichtlinien des Standard-kube-schedulers analog zur Konfigurationsdatei erhalten können. Wir werden die uns interessierenden Planungsrichtlinien in der Datei /etc/kubernetes/scheduler-custom-policy-config.json wie folgt beschreiben:

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

Somit erstellt der kube-scheduler zunĂ€chst eine Liste von Nodes, auf denen ein Pod gemĂ€ĂŸ der GeneralPredicates-Richtlinie geplant werden kann (die eine Reihe von Richtlinien wie PodFitsResources, PodFitsHostPorts, HostName und MatchNodeSelector umfasst). Anschließend erfolgt die Bewertung jeder Node gemĂ€ĂŸ der PrioritĂ€ten im Array priorities. Um die Anforderungen unserer Aufgabe zu erfĂŒllen, haben wir festgestellt, dass dieser Richtlinienmix die optimale Lösung darstellt. Ich erinnere daran, dass die Richtlinien mit ihren detaillierten Beschreibungen verfĂŒgbar sind in Dokumentation. Um Ihre Aufgabe zu erfĂŒllen, können Sie einfach den Satz der verwendeten Richtlinien Ă€ndern und ihnen entsprechende Gewichtungen zuweisen.

Das Manifest des neuen kube-schedulers, das wir am Anfang des Kapitels erstellt haben, nennen wir kube-scheduler-custom.yaml und speichern es im folgenden Pfad /etc/kubernetes/manifests auf den drei Master-Nodes. Wenn alles richtig gemacht wird, startet Kubelet auf jeder Node den Pod, und in den Logs unseres neuen kube-schedulers sehen wir Informationen, dass unsere Richtliniendatei erfolgreich angewendet wurde:

Scheduler aus der Konfiguration erstellen: {{ } [{GeneralPredicates } ] [{ServiceSpreadingPriority 1 } {EqualPriority 1 } {LeastRequestedPriority 1 } {NodePreferAvoidPodsPriority 10000 } {NodeAffinityPriority 1 } ] [] 10 false}
Predicate registrieren: GeneralPredicates
Predicate-Typ GeneralPredicates bereits registriert, wird wiederverwendet.
PrioritÀt registrieren: ServiceSpreadingPriority
PrioritÀtstyp ServiceSpreadingPriority bereits registriert, wird wiederverwendet.
PrioritÀt registrieren: EqualPriority
PrioritÀtstyp EqualPriority bereits registriert, wird wiederverwendet.
PrioritÀt registrieren: LeastRequestedPriority
PrioritÀtstyp LeastRequestedPriority bereits registriert, wird wiederverwendet.
PrioritÀt registrieren: NodePreferAvoidPodsPriority
PrioritÀtstyp NodePreferAvoidPodsPriority bereits registriert, wird wiederverwendet.
PrioritÀt registrieren: NodeAffinityPriority
PrioritÀtstyp NodeAffinityPriority bereits registriert, wird wiederverwendet.
Scheduler erstellen mit Fit-PrÀdikaten 'map[GeneralPredicates:{}]' und PrioritÀtsfunktionen 'map[EqualPriority:{} LeastRequestedPriority:{} NodeAffinityPriority:{} NodePreferAvoidPodsPriority:{} ServiceSpreadingPriority:{}]'

Jetzt mĂŒssen wir nur noch im Spec unserer CronJob angeben, dass alle Anforderungen zur Planung ihrer Pods von unserem neuen kube-scheduler bearbeitet werden sollen:

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

Fazit

Am Ende haben wir einen zusĂ€tzlichen kube-scheduler mit einem einzigartigen Satz von Planungsrichtlinien erhalten, dessen Betrieb direkt von kubelet ĂŒberwacht wird. DarĂŒber hinaus haben wir die Wahl eines neuen Leaders zwischen den Pods unseres kube-schedulers konfiguriert fĂŒr den Fall, dass der alte Leader aus irgendeinem Grund nicht mehr erreichbar ist.

Übliche Anwendungen und Dienste werden weiterhin ĂŒber den Standard-kube-scheduler geplant, wĂ€hrend alle Cron-Jobs vollstĂ€ndig auf den neuen umgestellt wurden. Die durch Cron-Jobs erzeugte Last wird nun gleichmĂ€ĂŸig auf alle Knoten verteilt. Da der Großteil der Cron-Jobs auf denselben Knoten ausgefĂŒhrt wird wie die Hauptanwendungen des Projekts, konnte das Risiko von Pod-Verschiebungen aufgrund von Ressourcenmangel erheblich gesenkt werden. Nach der Implementierung eines zusĂ€tzlichen kube-scheduler traten keine Probleme mit ungleichmĂ€ĂŸiger Planung von Cron-Jobs mehr auf.

Lesen Sie auch andere Artikel in unserem Blog:

Quelle: habr.com

60GB SSD 8Gb DDR4