ProHoster > Blog > administration > Oprettelse af en ekstra kube-planlægger med et brugerdefineret sæt planlægningsregler
Oprettelse af en ekstra kube-planlægger med et brugerdefineret sæt planlægningsregler
Kube-scheduler er en integreret komponent af Kubernetes, som er ansvarlig for at planlægge pods på tværs af noder i overensstemmelse med specificerede politikker. Under driften af en Kubernetes-klynge behøver vi ofte ikke at tænke på, hvilke politikker der bruges til at planlægge pods, da sæt af politikker i standard-kube-planlæggeren er velegnet til de fleste hverdagsopgaver. Der er dog situationer, hvor det er vigtigt for os at finjustere processen med at tildele pods, og der er to måder at udføre denne opgave på:
Opret en kube-planlægger med et brugerdefineret sæt regler
Skriv din egen planlægger, og lær den at arbejde med API-serveranmodninger
I denne artikel vil jeg beskrive implementeringen af det første punkt for at løse problemet med ujævn planlægning af ildsteder på et af vores projekter.
En kort introduktion til, hvordan kube-scheduler fungerer
Det er især værd at bemærke det faktum, at kube-scheduler ikke er ansvarlig for direkte planlægning af pods - den er kun ansvarlig for at bestemme noden, hvorpå poden skal placeres. Med andre ord, resultatet af kube-schedulers arbejde er navnet på noden, som den returnerer til API-serveren for en planlægningsanmodning, og det er her dens arbejde slutter.
Først kompilerer kube-scheduler en liste over noder, hvor poden kan planlægges i overensstemmelse med prædikatpolitikkerne. Dernæst modtager hver node fra denne liste et vist antal point i overensstemmelse med prioritetspolitikkerne. Som et resultat vælges knudepunktet med det maksimale antal point. Hvis der er noder, der har samme maksimale score, vælges en tilfældig. En liste og beskrivelse af politikkerne for prædikater (filtrering) og prioriteter (scoring) kan findes i dokumentation.
Beskrivelse af problemlegemet
På trods af det store antal forskellige Kubernetes-klynger, der vedligeholdes hos Nixys, stødte vi først på problemet med at planlægge pods først for nylig, da et af vores projekter skulle køre et stort antal periodiske opgaver (~100 CronJob-enheder). For at forenkle beskrivelsen af problemet så meget som muligt, vil vi som eksempel tage en mikrotjeneste, inden for hvilken en cron-opgave lanceres en gang i minuttet, hvilket skaber en vis belastning på CPU'en. For at køre cron-opgaven blev der tildelt tre noder med absolut identiske karakteristika (24 vCPU'er på hver).
Samtidig er det umuligt at sige med nøjagtighed, hvor lang tid CronJob vil tage at udføre, da mængden af inputdata ændrer sig konstant. I gennemsnit, under normal drift af kube-scheduler, kører hver node 3-4 jobforekomster, som skaber ~20-30% af belastningen på CPU'en for hver node:
Selve problemet er, at nogle gange stoppede cron-taskpods med at blive planlagt på en af de tre noder. Det vil sige, at der på et tidspunkt ikke var planlagt en eneste pod for en af noderne, mens der på de to andre noder kørte 6-8 kopier af opgaven, hvilket skabte ~40-60% af CPU-belastningen:
Problemet gentog sig med absolut tilfældig frekvens og korrelerede af og til med det øjeblik, en ny version af koden blev rullet ud.
Ved at øge kube-scheduler-logningsniveauet til niveau 10 (-v=10), begyndte vi at registrere, hvor mange point hver node fik under evalueringsprocessen. Under normal planlægningsdrift kunne følgende oplysninger ses i loggene:
De der. at dømme efter informationen fra loggene, fik hver af knudepunkterne et lige antal sidste point, og et tilfældigt blev udvalgt til planlægning. På tidspunktet for problematisk planlægning så logfilerne således ud:
Hvoraf det kan ses, at en af noderne fik færre slutpoint end de andre, og derfor blev der kun lavet planlægning for de to noder, der opnåede den maksimale score. Således var vi bestemt overbevist om, at problemet netop ligger i skemalægningen af poderne.
Den yderligere algoritme til at løse problemet var indlysende for os - analyser logfilerne, forstå med hvilken prioritet noden ikke scorede point, og juster om nødvendigt politikkerne for standard kube-planlæggeren. Men her står vi over for to væsentlige vanskeligheder:
Ved det maksimale logningsniveau (10) afspejles point, der kun er opnået for nogle prioriteter. I ovenstående uddrag af logfiler kan du se, at for alle prioriteter afspejlet i logfilerne, scorer noder det samme antal point i normal- og problemplanlægning, men det endelige resultat i tilfælde af problemplanlægning er anderledes. Således kan vi konkludere, at for nogle prioriteter sker scoring "bag kulisserne", og vi har ingen måde at forstå, for hvilken prioritet noden ikke fik point. Vi beskrev dette problem i detaljer i spørgsmål Kubernetes repository på Github. I skrivende stund er der modtaget et svar fra udviklerne om, at der vil blive tilføjet logføring i Kubernetes v1.15,1.16, 1.17 og XNUMX opdateringerne.
Der er ingen nem måde at forstå, hvilket specifikt sæt politikker kube-scheduler arbejder med i øjeblikket. Ja, i dokumentation denne liste er opført, men den indeholder ikke oplysninger om, hvilke specifikke vægte der er tildelt hver af prioritetspolitikkerne. Du kan kun se vægtene eller redigere politikkerne for standard kube-planlæggeren i kildekoder.
Det er værd at bemærke, at vi engang var i stand til at registrere, at en node ikke modtog point i henhold til ImageLocalityPriority-politikken, som tildeler point til en node, hvis den allerede har det nødvendige billede til at køre applikationen. Det vil sige, at på det tidspunkt, hvor en ny version af applikationen blev rullet ud, lykkedes det cron-opgaven at køre på to noder, downloade et nyt billede fra docker-registret til dem, og dermed fik to noder en højere slutscore i forhold til den tredje. .
Som jeg skrev ovenfor, ser vi ikke i logfilerne information om evalueringen af ImageLocalityPriority-politikken, så for at kontrollere vores antagelse dumpede vi billedet med den nye version af applikationen på den tredje node, hvorefter planlægningen fungerede korrekt . Det var netop på grund af ImageLocalityPriority-politikken, at planlægningsproblemet blev observeret ret sjældent; oftere var det forbundet med noget andet. På grund af det faktum, at vi ikke fuldt ud kunne debugge hver af politikkerne på listen over prioriteter i standard kube-planlægningsprogrammet, havde vi et behov for fleksibel styring af pod-planlægningspolitikker.
Formulering af problemet
Vi ønskede, at løsningen på problemet skulle være så specifik som muligt, det vil sige, at hovedentiteterne i Kubernetes (her mener vi standard kube-planlæggeren) skulle forblive uændrede. Vi ønskede ikke at løse et problem ét sted og skabe det et andet. Således kom vi til to muligheder for at løse problemet, som blev annonceret i introduktionen til artiklen - oprettelse af en ekstra skemalægger eller skrivning af din egen. Hovedkravet for at planlægge cron-opgaver er at fordele belastningen jævnt på tre knudepunkter. Dette krav kan opfyldes af eksisterende kube-planlægningspolitikker, så for at løse vores problem er der ingen mening i at skrive din egen planlægger.
Instruktioner til oprettelse og implementering af en ekstra kube-planlægger er beskrevet i dokumentation. Men det forekom os, at implementeringsenheden ikke var nok til at sikre fejltolerance i driften af en så kritisk tjeneste som kube-scheduler, så vi besluttede at implementere en ny kube-scheduler som en statisk pod, som ville blive overvåget direkte af Kubelet. Derfor har vi følgende krav til den nye kube-planlægning:
Tjenesten skal implementeres som en statisk pod på alle klyngemastere
Fejltolerance skal angives i tilfælde af, at den aktive pod med kube-planlægning ikke er tilgængelig
Hovedprioriteten ved planlægning bør være antallet af tilgængelige ressourcer på noden (LeastRequestedPriority)
Implementeringsløsninger
Det er værd at bemærke med det samme, at vi vil udføre alt arbejde i Kubernetes v1.14.7, fordi Dette er den version, der blev brugt i projektet. Lad os starte med at skrive et manifest til vores nye kube-planlægger. Lad os tage standardmanifestet (/etc/kubernetes/manifests/kube-scheduler.yaml) som grundlag og bringe det til følgende form:
Ændrede navnet på poden og beholderen til kube-scheduler-cron
Angiv brugen af porte 10151 og 10159, som indstillingen blev defineret hostNetwork: true og vi kan ikke bruge de samme porte som standard kube-planlæggeren (10251 og 10259)
Ved at bruge parameteren --config specificerede vi den konfigurationsfil, som tjenesten skulle startes med
Konfigureret montering af konfigurationsfilen (scheduler-custom.conf) og planlægningspolitikfilen (scheduler-custom-policy-config.json) fra værten
Glem ikke, at vores kube-planlægger har brug for rettigheder svarende til standarden. Rediger dens klyngerolle:
Lad os nu tale om, hvad der skal være indeholdt i konfigurationsfilen og planlægningspolitikfilen:
Konfigurationsfil (scheduler-custom.conf)
For at opnå standard kube-scheduler-konfigurationen skal du bruge parameteren --write-config-to af dokumentation. Vi placerer den resulterende konfiguration i filen /etc/kubernetes/scheduler-custom.conf og reducerer den til følgende form:
Vi indstiller schedulerName til navnet på vores kube-scheduler-cron-tjeneste.
I parameteren lockObjectName du skal også angive navnet på vores tjeneste og sørge for, at parameteren leaderElect sat til sand (hvis du har én masterknude, kan du indstille den til falsk).
Angiv stien til filen med en beskrivelse af planlægningspolitikkerne i parameteren algorithmSource.
Det er værd at se nærmere på det andet punkt, hvor vi redigerer parametrene for nøglen leaderElection. For at sikre fejltolerance har vi aktiveret (leaderElect) processen med at vælge en leder (master) mellem pods af vores kube-planlægger ved hjælp af et enkelt slutpunkt for dem (resourceLock) med navnet kube-scheduler-cron (lockObjectName) i kube-systemets navneområde (lockObjectNamespace). Hvordan Kubernetes sikrer høj tilgængelighed af hovedkomponenterne (inklusive kube-scheduler) kan findes i artiklen.
Planlægningspolitikfil (scheduler-custom-policy-config.json)
Som jeg skrev tidligere, kan vi kun finde ud af, hvilke specifikke politikker standard kube-planlæggeren arbejder med ved at analysere dens kode. Det vil sige, at vi ikke kan få en fil med planlægningspolitikker for standard kube-planlæggeren på samme måde som en konfigurationsfil. Lad os beskrive de planlægningspolitikker, vi er interesserede i i filen /etc/kubernetes/scheduler-custom-policy-config.json som følger:
Således kompilerer kube-scheduler først en liste over noder, som en pod kan planlægges til i henhold til GeneralPredicates-politikken (som inkluderer et sæt PodFitsResources, PodFitsHostPorts, HostName og MatchNodeSelector-politikker). Og derefter evalueres hver node i overensstemmelse med sættet af politikker i prioritetsarrayet. For at opfylde betingelserne for vores opgave anså vi, at et sådant sæt politikker ville være den optimale løsning. Lad mig minde dig om, at et sæt politikker med deres detaljerede beskrivelser er tilgængeligt i dokumentation. For at udføre din opgave kan du blot ændre det anvendte sæt af politikker og tildele passende vægte til dem.
Lad os kalde manifestet af den nye kube-scheduler, som vi oprettede i begyndelsen af kapitlet, kube-scheduler-custom.yaml og placere det i den følgende sti /etc/kubernetes/manifests på tre hovedknudepunkter. Hvis alt er gjort korrekt, vil Kubelet starte en pod på hver node, og i logfilerne for vores nye kube-planlægger vil vi se oplysninger om, at vores politikfil blev anvendt med succes:
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:{}]'
Nu er der kun tilbage at angive i specifikationerne for vores CronJob, at alle anmodninger om planlægning af dens pods skal behandles af vores nye kube-planlægger:
I sidste ende fik vi en ekstra kube-planlægger med et unikt sæt af planlægningspolitikker, hvis arbejde overvåges direkte af kubelet. Derudover har vi oprettet valget af en ny leder mellem pods af vores kube-planlægger, hvis den gamle leder bliver utilgængelig af en eller anden grund.
Regelmæssige applikationer og tjenester planlægges fortsat gennem standard kube-planlægningsprogrammet, og alle cron-opgaver er blevet fuldstændigt overført til den nye. Belastningen skabt af cron-opgaver er nu jævnt fordelt på alle noder. I betragtning af, at de fleste af cron-opgaverne udføres på de samme noder som projektets hovedapplikationer, har dette reduceret risikoen for at flytte pods betydeligt på grund af manglende ressourcer. Efter at have introduceret den ekstra kube-planlægning, opstod der ikke længere problemer med ujævn planlægning af cron-opgaver.