Erstellen eines zusätzlichen Kube-Schedulers mit einem benutzerdefinierten Satz von Planungsregeln

Erstellen eines zusätzlichen Kube-Schedulers mit einem benutzerdefinierten Satz von Planungsregeln

Kube-Scheduler ist eine integrale Komponente von Kubernetes, die für die knotenübergreifende Planung von Pods gemäß festgelegten Richtlinien verantwortlich ist. Während des Betriebs eines Kubernetes-Clusters müssen wir oft nicht darüber nachdenken, welche Richtlinien zum Planen von Pods verwendet werden, da der Richtliniensatz des Standard-Kube-Schedulers für die meisten alltäglichen Aufgaben geeignet ist. Es gibt jedoch Situationen, in denen es für uns wichtig ist, den Prozess der Pod-Zuweisung zu optimieren, und es gibt zwei Möglichkeiten, diese Aufgabe zu erfüllen:

  1. Erstellen Sie einen Kube-Scheduler mit einem benutzerdefinierten Regelsatz
  2. Schreiben Sie Ihren eigenen Scheduler und bringen Sie ihm bei, mit API-Serveranfragen zu arbeiten

In diesem Artikel beschreibe ich die Umsetzung des ersten Punktes zur Lösung des Problems der ungleichmäßigen Planung von Feuerstellen bei einem unserer Projekte.

Eine kurze Einführung in die Funktionsweise von Kube-Scheduler

Besonders hervorzuheben ist die Tatsache, dass kube-scheduler nicht für die direkte Planung von Pods verantwortlich ist, sondern nur für die Bestimmung des Knotens, auf dem der Pod platziert werden soll. Mit anderen Worten: Das Ergebnis der Arbeit von kube-scheduler ist der Name des Knotens, den er für eine Planungsanforderung an den API-Server zurückgibt, und dort endet seine Arbeit.

Zunächst erstellt kube-scheduler eine Liste von Knoten, auf denen der Pod gemäß den Prädikatenrichtlinien geplant werden kann. Als nächstes erhält jeder Knoten aus dieser Liste eine bestimmte Anzahl von Punkten gemäß den Prioritätsrichtlinien. Als Ergebnis wird der Knoten mit der maximalen Anzahl an Punkten ausgewählt. Wenn es Knoten gibt, die die gleiche maximale Punktzahl haben, wird ein zufälliger Knoten ausgewählt. Eine Liste und Beschreibung der Richtlinien für Prädikate (Filterung) und Prioritäten (Bewertung) finden Sie in Dokumentation.

Beschreibung des Problemkörpers

Trotz der großen Anzahl verschiedener Kubernetes-Cluster, die bei Nixys verwaltet werden, stießen wir erst kürzlich auf das Problem der Pod-Planung, als eines unserer Projekte eine große Anzahl periodischer Aufgaben (ca. 100 CronJob-Entitäten) ausführen musste. Um die Problembeschreibung möglichst einfach zu gestalten, nehmen wir als Beispiel einen Microservice, bei dem einmal pro Minute eine Cron-Task gestartet wird, die die CPU etwas belastet. Zur Ausführung der Cron-Task wurden drei Knoten mit absolut identischen Eigenschaften zugewiesen (jeweils 24 vCPUs).

Gleichzeitig lässt sich nicht genau sagen, wie lange die Ausführung des CronJob dauern wird, da sich die Menge der Eingabedaten ständig ändert. Während des normalen Betriebs von kube-scheduler führt jeder Knoten durchschnittlich 3–4 Jobinstanzen aus, die etwa 20–30 % der CPU-Last jedes Knotens verursachen:

Erstellen eines zusätzlichen Kube-Schedulers mit einem benutzerdefinierten Satz von Planungsregeln

Das Problem selbst besteht darin, dass Cron-Task-Pods manchmal nicht mehr auf einem der drei Knoten geplant wurden. Das heißt, zu einem bestimmten Zeitpunkt war für einen der Knoten kein einziger Pod geplant, während auf den anderen beiden Knoten 6–8 Kopien der Aufgabe liefen, was ~40–60 % der CPU-Last verursachte:

Erstellen eines zusätzlichen Kube-Schedulers mit einem benutzerdefinierten Satz von Planungsregeln

Das Problem trat mit völlig zufälliger Häufigkeit erneut auf und hing gelegentlich mit dem Zeitpunkt zusammen, an dem eine neue Version des Codes eingeführt wurde.

Durch die Erhöhung der Kube-Scheduler-Protokollierungsstufe auf Stufe 10 (-v=10) begannen wir aufzuzeichnen, wie viele Punkte jeder Knoten während des Bewertungsprozesses gewonnen hat. Während des normalen Planungsbetriebs konnten in den Protokollen folgende Informationen angezeigt werden:

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

Diese. Nach den aus den Protokollen erhaltenen Informationen zu urteilen, erzielte jeder Knoten die gleiche Anzahl an Endpunkten und ein zufälliger Knoten wurde für die Planung ausgewählt. Zum Zeitpunkt der problematischen Planung sahen die Protokolle so aus:

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

Daraus ist ersichtlich, dass einer der Knoten weniger Endpunkte erzielte als die anderen und die Planung daher nur für die beiden Knoten durchgeführt wurde, die die maximale Punktzahl erreichten. Daher waren wir definitiv davon überzeugt, dass das Problem genau in der Planung der Pods liegt.

Der weitere Algorithmus zur Lösung des Problems war für uns offensichtlich: Analysieren Sie die Protokolle, verstehen Sie, mit welcher Priorität der Knoten keine Punkte erzielt hat, und passen Sie gegebenenfalls die Richtlinien des Standard-Kube-Schedulers an. Allerdings stehen wir hier vor zwei wesentlichen Schwierigkeiten:

  1. Bei der maximalen Protokollierungsstufe (10) werden nur für einige Prioritäten gewonnene Punkte berücksichtigt. Im obigen Auszug aus den Protokollen können Sie sehen, dass Knoten für alle in den Protokollen wiedergegebenen Prioritäten bei der normalen Planung und bei der Problemplanung die gleiche Punktzahl erzielen, das Endergebnis bei der Problemplanung jedoch unterschiedlich ist. Daraus können wir schließen, dass die Bewertung bei einigen Prioritäten „hinter den Kulissen“ erfolgt und wir keine Möglichkeit haben zu verstehen, für welche Priorität der Knoten keine Punkte erhalten hat. Wir haben dieses Problem ausführlich beschrieben Problem Kubernetes-Repository auf Github. Zum Zeitpunkt des Verfassens dieses Artikels haben die Entwickler geantwortet, dass in den Kubernetes-Updates v1.15,1.16, 1.17 und XNUMX Protokollierungsunterstützung hinzugefügt wird.
  2. Es gibt keine einfache Möglichkeit zu verstehen, mit welchen spezifischen Richtlinien kube-scheduler derzeit arbeitet. Ja in Dokumentation Diese Liste wird aufgeführt, enthält jedoch keine Informationen darüber, welche spezifischen Gewichtungen den einzelnen Prioritätsrichtlinien zugewiesen werden. Sie können die Gewichtungen nur in sehen oder die Richtlinien des Standard-Kube-Schedulers bearbeiten Quellcodes.

Es ist erwähnenswert, dass wir einmal feststellen konnten, dass ein Knoten keine Punkte gemäß der ImageLocalityPriority-Richtlinie erhalten hat, die einem Knoten Punkte vergibt, wenn er bereits über das zum Ausführen der Anwendung erforderliche Image verfügt. Das heißt, als eine neue Version der Anwendung eingeführt wurde, gelang es der Cron-Task, auf zwei Knoten zu laufen und ein neues Image aus der Docker-Registrierung auf sie herunterzuladen, sodass zwei Knoten im Vergleich zum dritten Knoten eine höhere Endpunktzahl erhielten .

Wie ich oben geschrieben habe, sehen wir in den Protokollen keine Informationen über die Auswertung der ImageLocalityPriority-Richtlinie. Um unsere Annahme zu überprüfen, haben wir das Image mit der neuen Version der Anwendung auf dem dritten Knoten abgelegt, woraufhin die Planung korrekt funktionierte . Gerade aufgrund der ImageLocalityPriority-Richtlinie wurde das Planungsproblem recht selten beobachtet; häufiger war es mit etwas anderem verbunden. Aufgrund der Tatsache, dass wir nicht alle Richtlinien in der Prioritätenliste des Standard-Kube-Schedulers vollständig debuggen konnten, bestand ein Bedarf an einer flexiblen Verwaltung der Pod-Planungsrichtlinien.

Formulierung des Problems

Wir wollten, dass die Lösung des Problems so spezifisch wie möglich ist, das heißt, die Hauptentitäten von Kubernetes (hier meinen wir den Standard-Kube-Scheduler) sollten unverändert bleiben. Wir wollten ein Problem nicht an einem Ort lösen und es an einem anderen schaffen. So kamen wir zu zwei Möglichkeiten zur Lösung des Problems, die in der Einleitung des Artikels angekündigt wurden – das Erstellen eines zusätzlichen Schedulers oder das Schreiben eines eigenen. Die Hauptanforderung für die Planung von Cron-Aufgaben besteht darin, die Last gleichmäßig auf drei Knoten zu verteilen. Diese Anforderung kann durch bestehende Kube-Scheduler-Richtlinien erfüllt werden. Um unser Problem zu lösen, macht es also keinen Sinn, einen eigenen Scheduler zu schreiben.

Anweisungen zum Erstellen und Bereitstellen eines zusätzlichen Kube-Schedulers finden Sie in Dokumentation. Allerdings schien es uns, dass die Deployment-Entität nicht ausreichte, um Fehlertoleranz beim Betrieb eines so wichtigen Dienstes wie Kube-Scheduler zu gewährleisten. Daher entschieden wir uns, einen neuen Kube-Scheduler als statischen Pod bereitzustellen, der direkt überwacht werden sollte von Kubelet. Somit haben wir folgende Anforderungen an den neuen Kube-Scheduler:

  1. Der Dienst muss als statischer Pod auf allen Cluster-Mastern bereitgestellt werden
  2. Für den Fall, dass der aktive Pod mit Kube-Scheduler nicht verfügbar ist, muss Fehlertoleranz bereitgestellt werden
  3. Die Hauptpriorität bei der Planung sollte die Anzahl der verfügbaren Ressourcen auf dem Knoten sein (LeastRequestedPriority).

Implementierungslösungen

Es ist sofort erwähnenswert, dass wir alle Arbeiten in Kubernetes v1.14.7 ausführen werden, denn Dies ist die Version, die im Projekt verwendet wurde. Beginnen wir damit, ein Manifest für unseren neuen Kube-Scheduler zu schreiben. Nehmen wir das Standardmanifest (/etc/kubernetes/manifests/kube-scheduler.yaml) als Grundlage und bringen es in die folgende Form:

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

Kurz zu den wichtigsten Änderungen:

  1. Der Name des Pods und Containers wurde in kube-scheduler-cron geändert
  2. Als Option wurde die Verwendung der Ports 10151 und 10159 angegeben 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. Konfigurierte Bereitstellung der Konfigurationsdatei (scheduler-custom.conf) und der Planungsrichtliniendatei (scheduler-custom-policy-config.json) vom Host

Vergessen Sie nicht, dass unser Kube-Scheduler ähnliche Rechte wie der Standard benötigt. Bearbeiten Sie die Clusterrolle:

kubectl edit clusterrole system:kube-scheduler

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

Lassen Sie uns nun darüber sprechen, was in der Konfigurationsdatei und der Planungsrichtliniendatei enthalten sein sollte:

  • Konfigurationsdatei (scheduler-custom.conf)
    Um die standardmäßige Kube-Scheduler-Konfiguration zu erhalten, müssen Sie den Parameter verwenden --write-config-to von Dokumentation. Die resultierende Konfiguration werden wir in der Datei /etc/kubernetes/scheduler-custom.conf ablegen und auf die folgende Form reduzieren:

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"

Kurz zu den wichtigsten Änderungen:

  1. Wir setzen „schedulerName“ auf den Namen unseres kube-scheduler-cron-Dienstes.
  2. Im Parameter lockObjectName Sie müssen auch den Namen unseres Dienstes festlegen und sicherstellen, dass der Parameter leaderElect auf true setzen (wenn Sie einen Masterknoten haben, können Sie ihn auf false setzen).
  3. Geben Sie den Pfad zur Datei mit einer Beschreibung der Planungsrichtlinien im Parameter an algorithmSource.

Es lohnt sich, einen genaueren Blick auf den zweiten Punkt zu werfen, in dem wir die Parameter für die Taste bearbeiten leaderElection. Um Fehlertoleranz zu gewährleisten, haben wir (leaderElect) der Prozess der Auswahl eines Anführers (Masters) zwischen den Pods unseres Kube-Schedulers unter Verwendung eines einzigen Endpunkts für sie (resourceLock) mit dem Namen kube-scheduler-cron (lockObjectName) im Kube-System-Namespace (lockObjectNamespace). Wie Kubernetes eine hohe Verfügbarkeit der Hauptkomponenten (einschließlich Kube-Scheduler) gewährleistet, finden Sie in Artikel.

  • Planungsrichtliniendatei (scheduler-custom-policy-config.json)
    Wie ich bereits geschrieben habe, können wir nur durch die Analyse seines Codes herausfinden, mit welchen spezifischen Richtlinien der Standard-Kube-Scheduler arbeitet. Das heißt, wir können eine Datei mit Planungsrichtlinien für den Standard-Kube-Scheduler nicht auf die gleiche Weise wie eine Konfigurationsdatei erhalten. Beschreiben wir die für uns interessanten Planungsrichtlinien in der Datei /etc/kubernetes/scheduler-custom-policy-config.json wie folgt:

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

Daher erstellt kube-scheduler zunächst eine Liste von Knoten, für die ein Pod gemäß der GeneralPredicates-Richtlinie geplant werden kann (die eine Reihe von PodFitsResources-, PodFitsHostPorts-, HostName- und MatchNodeSelector-Richtlinien umfasst). Und dann wird jeder Knoten gemäß den Richtlinien im Prioritäten-Array bewertet. Um die Bedingungen unserer Aufgabe zu erfüllen, waren wir der Ansicht, dass eine solche Reihe von Richtlinien die optimale Lösung wäre. Ich möchte Sie daran erinnern, dass eine Reihe von Richtlinien mit ihren detaillierten Beschreibungen in verfügbar sind Dokumentation. Um Ihre Aufgabe zu erfüllen, können Sie einfach die verwendeten Richtlinien ändern und ihnen entsprechende Gewichtungen zuweisen.

Nennen wir das Manifest des neuen Kube-Schedulers, den wir zu Beginn des Kapitels erstellt haben, kube-scheduler-custom.yaml und platzieren es im folgenden Pfad /etc/kubernetes/manifests auf drei Masterknoten. Wenn alles richtig gemacht wurde, startet Kubelet einen Pod auf jedem Knoten und in den Protokollen unseres neuen Kube-Schedulers sehen wir die Information, dass unsere Richtliniendatei erfolgreich angewendet wurde:

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

Jetzt müssen wir nur noch in der Spezifikation unseres CronJob angeben, dass alle Anfragen zur Planung seiner Pods von unserem neuen Kube-Scheduler verarbeitet werden sollen:

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

Abschluss

Letztendlich haben wir einen zusätzlichen Kube-Scheduler mit einem einzigartigen Satz an Planungsrichtlinien erhalten, dessen Arbeit direkt vom Kubelet überwacht wird. Darüber hinaus haben wir die Wahl eines neuen Anführers zwischen den Pods unseres Kube-Schedulers eingerichtet, für den Fall, dass der alte Anführer aus irgendeinem Grund nicht verfügbar ist.

Reguläre Anwendungen und Dienste werden weiterhin über den Standard-Kube-Scheduler geplant und alle Cron-Aufgaben wurden vollständig auf den neuen übertragen. Die durch Cron-Tasks verursachte Last wird nun gleichmäßig auf alle Knoten verteilt. Wenn man bedenkt, dass die meisten Cron-Aufgaben auf denselben Knoten ausgeführt werden wie die Hauptanwendungen des Projekts, hat dies das Risiko, Pods aufgrund fehlender Ressourcen zu verschieben, erheblich reduziert. Nach der Einführung des zusätzlichen Kube-Schedulers traten keine Probleme mehr mit ungleichmäßiger Planung von Cron-Aufgaben auf.

Lesen Sie auch andere Artikel in unserem Blog:

Source: habr.com

Kommentar hinzufügen