Creazione di un kube-scheduler aggiuntivo con un insieme personalizzato di regole di pianificazione

Creazione di un kube-scheduler aggiuntivo con un insieme personalizzato di regole di pianificazione

Il kube-scheduler è un componente essenziale di Kubernetes, responsabile della pianificazione dei pod sui nodi in base alle politiche definite. Spesso, durante l'uso di un cluster Kubernetes, non ci preoccupiamo di quali politiche vengano utilizzate per la pianificazione dei pod, poiché il set di politiche predefinite del kube-scheduler è adatto per la maggior parte delle attività quotidiane. Tuttavia, ci sono situazioni in cui è importante gestire in modo preciso il processo di distribuzione dei pod, e per affrontare questa questione ci sono due approcci:

  1. Creare un kube-scheduler con un set di regole personalizzate
  2. Scrivere il proprio scheduler e insegnargli a lavorare con le richieste dell'API server

In questo articolo descriverò l'implementazione del primo punto per risolvere il problema della pianificazione non uniforme dei pod in uno dei nostri progetti.

Breve introduzione al funzionamento del kube-scheduler

È importante sottolineare che il kube-scheduler non è responsabile della pianificazione diretta dei pod — si occupa solo di determinare il nodo su cui posizionare il pod. In altre parole, il risultato del lavoro del kube-scheduler è il nome del nodo, che viene restituito all'API server in risposta a una richiesta di pianificazione, e da quel momento la sua funzione termina.

Inizialmente, il kube-scheduler compila un elenco di nodi sui quali il pod può essere pianificato in base alle politiche di predicati. Successivamente, ogni nodo di questo elenco riceve un certo numero di punti in base alle politiche di priorità. Alla fine, viene scelto il nodo che ha accumulato il punteggio massimo. Se ci sono nodi con un punteggio massimo uguale, viene scelto casualmente uno di essi. È possibile consultare l'elenco e la descrizione delle politiche di predicati (filtering) e priorità (scoring) in documentazione.

Descrizione del problema

Nonostante il grande numero di diversi cluster Kubernetes gestiti da Nixys, ci siamo imbattuti nel problema della pianificazione dei pod solo di recente, quando per uno dei nostri progetti è emersa la necessità di avviare un gran numero di attività periodiche (~100 entità CronJob). Per semplificare al massimo la descrizione del problema, prendiamo come esempio un microservizio, all'interno del quale viene eseguita ogni minuto un'attività cron che genera un certo carico sulla CPU. Per l'esecuzione dell'attività cron sono stati assegnati tre nodi assolutamente identici per caratteristiche (24 vCPU ciascuno).

Non è possibile determinare con precisione quanto tempo ci vorrà per eseguire un CronJob, poiché il volume dei dati in ingresso varia costantemente. In media, durante il normale funzionamento di kube-scheduler, su ogni nodo operano 3-4 istanze del job, che creano circa il 20-30% di carico sulla CPU di ogni nodo:

Creazione di un kube-scheduler aggiuntivo con un insieme personalizzato di regole di pianificazione

Il problema principale è che a volte i pod delle cron-job smettevano di essere pianificati su uno dei tre nodi. Cioè, in un certo momento, su uno dei nodi non veniva pianificato alcun pod, mentre sugli altri due nodi c'erano 6-8 istanze del job attive, generando circa il 40-60% di carico sulla CPU:

Creazione di un kube-scheduler aggiuntivo con un insieme personalizzato di regole di pianificazione

Il problema si verificava con una periodicità assolutamente casuale e occasionalmente si correllava con il momento in cui veniva rilasciata una nuova versione del codice.

Aumentando il livello di logging di kube-scheduler a 10 (-v=10), abbiamo iniziato a registrare quanti punti otteneva nel processo di valutazione ciascun nodo. Durante il normale funzionamento della pianificazione, nei log potevamo vedere le seguenti informazioni:

resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node03: BalancedResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 1387 millicores 4161694720 byte di memoria, punteggio 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node02: BalancedResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 1347 millicores 4444810240 byte di memoria, punteggio 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node03: LeastResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 1387 millicores 4161694720 byte di memoria, punteggio 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node01: BalancedResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 1687 millicores 4790840320 byte di memoria, punteggio 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node02: LeastResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 1347 millicores 4444810240 byte di memoria, punteggio 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node01: LeastResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 1687 millicores 4790840320 byte di memoria, punteggio 9
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: NodeAffinityPriority, Punteggio: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: NodeAffinityPriority, Punteggio: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: NodeAffinityPriority, Punteggio: (0)                                                                                       
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node01: InterPodAffinityPriority, Punteggio: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: TaintTolerationPriority, Punteggio: (10)                                                                                   
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node02: InterPodAffinityPriority, Punteggio: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: TaintTolerationPriority, Punteggio: (10)                                                                                   
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node01: SelectorSpreadPriority, Punteggio: (10)                                                                                                        
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node03: InterPodAffinityPriority, Punteggio: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: TaintTolerationPriority, Punteggio: (10)                                                                                   
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node02: SelectorSpreadPriority, Punteggio: (10)                                                                                                        
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node03: SelectorSpreadPriority, Punteggio: (10)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: SelectorSpreadPriority, Punteggio: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: SelectorSpreadPriority, Punteggio: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: SelectorSpreadPriority, Punteggio: (10)                                                                                    
generic_scheduler.go:781] Host Node01 -> Punteggio 100043                                                                                                                                                                        
generic_scheduler.go:781] Host Node02 -> Punteggio 100043                                                                                                                                                                        
generic_scheduler.go:781] Host Node03 -> Punteggio 100043

Cioè, a giudicare dalle informazioni ottenute dai log, ciascuna delle nodi ha accumulato un numero uguale di punti finali e veniva selezionata casualmente per la pianificazione. Durante il problema di pianificazione, i log apparivano nel seguente modo:

resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node02: BalancedResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 1587 millicores 4581125120 byte di memoria, punteggio 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node03: BalancedResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 1087 millicores 3532549120 byte di memoria, punteggio 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node02: LeastResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 1587 millicores 4581125120 byte di memoria, punteggio 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node01: BalancedResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 987 millicores 3322833920 byte di memoria, punteggio 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node01: LeastResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 987 millicores 3322833920 byte di memoria, punteggio 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node03: LeastResourceAllocation, capacità 23900 millicores 67167186944 byte di memoria, richiesta totale 1087 millicores 3532549120 byte di memoria, punteggio 9
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node03: InterPodAffinityPriority, Punteggio: (0)
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node02: InterPodAffinityPriority, Punteggio: (0)
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node01: InterPodAffinityPriority, Punteggio: (0)
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: TaintTolerationPriority, Punteggio: (10)
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node03: SelectorSpreadPriority, Punteggio: (10)
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node02: SelectorSpreadPriority, Punteggio: (10)
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: TaintTolerationPriority, Punteggio: (10)
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node01: SelectorSpreadPriority, Punteggio: (10)
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: NodeAffinityPriority, Punteggio: (0)
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: SelectorSpreadPriority, Punteggio: (10)
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: SelectorSpreadPriority, Punteggio: (10)
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: TaintTolerationPriority, Punteggio: (10)
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: NodeAffinityPriority, Punteggio: (0)
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: NodeAffinityPriority, Punteggio: (0)
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: SelectorSpreadPriority, Punteggio: (10)
generic_scheduler.go:781] Host Node03 -> Punteggio 100041
generic_scheduler.go:781] Host Node02 -> Punteggio 100041
generic_scheduler.go:781] Host Node01 -> Punteggio 100038

Da cui si evince che uno dei nodi ha accumulato meno punti finali rispetto agli altri, e quindi la pianificazione è stata effettuata solo su due nodi, che hanno ottenuto il punteggio massimo. In questo modo ci siamo assicurati che il problema riguardasse proprio la pianificazione dei pod.

L'algoritmo per risolvere il problema ci è sembrato immediato: analizzare i log, capire secondo quale priorità il nodo non ha accumulato punti e, se necessario, correggere le politiche del kube-scheduler di default. Tuttavia, ci siamo scontrati con due difficoltà significative:

  1. Al massimo livello di logging (10) viene riportato un insieme di punti solo su alcune priorità. Nell'estratto di log riportato sopra si può notare che, per tutte le priorità riportate nei log, i nodi accumulano lo stesso numero di punti sia nella pianificazione normale che in quella problematica; tuttavia, il risultato finale in caso di pianificazione problematica è diverso. Pertanto, si può concludere che, per alcune priorità, il conteggio dei punti avviene 'in sordina', e non abbiamo modo di capire in base a quale priorità il nodo non ha accumulato punti. Questo problema è stato descritto in dettaglio in problema repository Kubernetes su Github. Al momento della stesura dell'articolo abbiamo ricevuto una risposta dagli sviluppatori, che confermava che il supporto per il logging sarebbe stato aggiunto negli aggiornamenti di Kubernetes v1.15, v1.16 e v1.17.
  2. Non c'è modo semplice di capire con quale specifico insieme di politiche stia attualmente lavorando kube-scheduler. Sì, in documentazione questo elenco è specificato, ma non contiene informazioni sui pesi specifici assegnati a ciascuna delle politiche prioritarie. È possibile visualizzare i pesi o modificare le politiche del kube-scheduler di default solo in sorgenti.

Va notato che una volta siamo riusciti a registrare che il nodo non ha accumulato punti secondo la politica ImageLocalityPriority, che assegna punti al nodo se esso ha già un'immagine necessaria per l'avvio dell'applicazione. Cioè, al momento del rollout di una nuova versione dell'applicazione, il job cron riusciva a partire su due nodi, caricando su di essi una nuova immagine dal docker registry, e in questo modo i due nodi ottenevano un punteggio finale maggiore rispetto al terzo.

Come già scritto sopra, nei log non vediamo informazioni sulla valutazione della politica ImageLocalityPriority, quindi, per verificare la nostra supposizione, abbiamo eseguito il pull di un'immagine con una nuova versione dell'applicazione su un terzo nodo, dopo di che la pianificazione ha cominciato a funzionare correttamente. La problematica della pianificazione si è verificata piuttosto raramente a causa della politica ImageLocalityPriority, più frequentemente era legata ad altro. Dato che non siamo stati in grado di eseguire un debug completo di ciascuna delle politiche nell'elenco delle priorità del kube-scheduler predefinito, abbiamo avuto la necessità di una gestione flessibile delle politiche di pianificazione dei pod.

Definizione del compito

Volevamo che la soluzione al problema fosse il più mirata possibile, cioè le principali entità di Kubernetes (qui si intende il kube-scheduler predefinito) dovrebbero rimanere invariate. Non volevamo risolvere il problema in un punto e crearne uno in un altro. Così siamo arrivati a due opzioni per risolvere il problema, come accennato nell'introduzione dell'articolo: creare un scheduler aggiuntivo o scrivere il nostro. Il principale requisito per la pianificazione dei cron job è la distribuzione uniforme del carico tra i tre nodi. Questo requisito può essere soddisfatto dalle politiche esistenti del kube-scheduler, quindi non ha senso scrivere il nostro scheduler per risolvere la nostra esigenza.

Le istruzioni per la creazione e il Deployment di un kube-scheduler aggiuntivo sono descritte in documentazione. Tuttavia, ci è sembrato che le entità di Deployment non fossero sufficienti per garantire la disponibilità operativa di un servizio così critico come il kube-scheduler, quindi abbiamo deciso di implementare un nuovo kube-scheduler come Static Pod, monitorato direttamente da Kubelet. Pertanto, abbiamo stabilito i seguenti requisiti per il nuovo kube-scheduler:

  1. Il servizio deve essere implementato come Static Pod su tutti i master del cluster
  2. Deve essere prevista ridondanza in caso di inattività del pod attivo con il kube-scheduler
  3. La principale priorità nella pianificazione deve essere la quantità di risorse disponibili sul nodo (LeastRequestedPriority)

Implementazione della soluzione

Vale la pena sottolineare che tutte le operazioni saranno eseguite su Kubernetes v1.14.7, in quanto questa versione è stata utilizzata nel progetto. Iniziamo a scrivere il manifesto per il nostro nuovo kube-scheduler. Prenderemo come base il manifesto predefinito (/etc/kubernetes/manifests/kube-scheduler.yaml) e lo adatteremo nel seguente modo:

tipo: Pod
metadata:
  labels:
    componente: scheduler
    livello: control-plane
  nome: 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

Brevemente sulle principali modifiche:

  1. Abbiamo cambiato il nome del pod e del container in kube-scheduler-cron
  2. Abbiamo specificato l'uso delle porte 10151 e 10159 poiché è stata definita l'opzione hostNetwork: true e non possiamo utilizzare le stesse porte del kube-scheduler predefinito (10251 e 10259)
  3. Con il parametro —config abbiamo specificato il file di configurazione da cui deve essere avviato il servizio
  4. Abbiamo configurato il montaggio del file di configurazione (scheduler-custom.conf) e del file delle politiche di pianificazione (scheduler-custom-policy-config.json) dall'host

Non dimentichiamo che al nostro kube-scheduler serviranno privilegi simili a quelli predefiniti. Modifichiamo il suo ruolo di cluster:

kubectl edit clusterrole system:kube-scheduler

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

Ora parliamo di cosa dovrebbe essere contenuto nel file di configurazione e nel file delle politiche di pianificazione:

  • File di configurazione (scheduler-custom.conf)
    Per ottenere la configurazione del kube-scheduler predefinito è necessario utilizzare il parametro --write-config-to da documentazione. La configurazione ottenuta sarà salvata nel file /etc/kubernetes/scheduler-custom.conf e sarà resa nel seguente formato:

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"

Brevemente sulle principali modifiche:

  1. Abbiamo impostato nel parametro schedulerName il nome del nostro servizio kube-scheduler-cron.
  2. Nel parametro lockObjectName dobbiamo anche impostare il nome del nostro servizio e assicurarci che il parametro leaderElect sia impostato su true (se hai un solo nodo master, puoi impostarlo su false).
  3. Abbiamo specificato il percorso del file contenente la descrizione delle politiche di pianificazione nel parametro algorithmSource.

Vale la pena soffermarsi più nel dettaglio sul secondo punto, dove modifichiamo i parametri per la chiave leaderElection. Per garantire la tolleranza ai guasti, abbiamo attivato (leaderElect) il processo di elezione del leader (master) tra i pod del nostro kube-scheduler utilizzando un endpoint comune per loro (resourceLock) chiamato kube-scheduler-cron (lockObjectName) nello spazio dei nomi kube-system (lockObjectNamespace). Puoi trovare informazioni su come Kubernetes garantisce alta disponibilità per i componenti principali (incluso kube-scheduler) in abbiamo chiarito il corretto completamento dei programmi che utilizzano il mediastreamer..

  • Il file delle politiche di pianificazione (scheduler-custom-policy-config.json)
    Come ho già scritto in precedenza, per scoprire con quali politiche specifiche opera il kube-scheduler di default possiamo solo analizzare il suo codice. In altre parole, non possiamo ottenere un file con le politiche di pianificazione del kube-scheduler di default analogamente al file di configurazione. Descriveremo le politiche di pianificazione che ci interessano nel file /etc/kubernetes/scheduler-custom-policy-config.json come segue:

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

In questo modo, kube-scheduler prima compila un elenco di nodi su cui può essere pianificato un pod secondo la politica GeneralPredicates (che include un insieme di politiche PodFitsResources, PodFitsHostPorts, HostName e MatchNodeSelector). Successivamente, viene effettuata una valutazione di ciascun nodo in base all'insieme di politiche nell'array delle priorità. Per soddisfare le condizioni della nostra attività, abbiamo ritenuto che tale insieme di politiche fosse la soluzione ottimale. Ricordo che l'insieme di politiche con la loro descrizione dettagliata è disponibile in documentazione. Per eseguire il proprio compito, è possibile semplicemente modificare l'insieme di politiche utilizzate e assegnare loro i pesi corrispondenti.

Il manifesto del nuovo kube-scheduler che abbiamo creato all'inizio del capitolo sarà chiamato kube-scheduler-custom.yaml e sarà posizionato nel seguente percorso /etc/kubernetes/manifests su tre nodi master. Se tutto è stato eseguito correttamente, Kubelet su ciascun nodo avvierà il pod, e nei log del nostro nuovo kube-scheduler vedremo informazioni che attestano che il nostro file di politiche è stato applicato con successo:

Creating scheduler from configuration: {{ } [{GeneralPredicates } ] [{ServiceSpreadingPriority 1 } {EqualPriority 1 } {LeastRequestedPriority 1 } {NodePreferAvoidPodsPriority 10000 } {NodeAffinityPriority 1 } ] [] 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:{}]'

Ora non resta che specificare nello spec della nostra CronJob che tutte le richieste per pianificare i suoi pod devono essere gestite dal nostro nuovo kube-scheduler:

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

Conclusione

Alla fine abbiamo ottenuto un kube-scheduler aggiuntivo con un insieme unico di politiche di pianificazione, il cui funzionamento è monitorato direttamente dal kubelet. Inoltre, abbiamo configurato le elezioni di un nuovo leader tra i pod del nostro kube-scheduler nel caso in cui il vecchio leader diventi indisponibile per qualche motivo.

Le applicazioni e i servizi ordinari continuano a essere pianificati tramite il kube-scheduler predefinito, mentre tutti i cron-job sono stati completamente trasferiti al nuovo. Il carico generato dai cron-job è ora distribuito uniformemente su tutte le nodi. Dato che la maggior parte dei cron-job viene eseguita sulle stesse nodi delle principali applicazioni del progetto, ciò ha notevolmente ridotto il rischio di migrazione dei pod a causa della mancanza di risorse. Dopo l'implementazione di un kube-scheduler aggiuntivo, non si sono più verificati problemi di pianificazione irregolare dei cron-job.

Leggi anche altri articoli nel nostro blog:

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster