Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

27. april på konferencen strejke 2019, som en del af afsnittet "DevOps" blev rapporten "Autoskalering og ressourcestyring i Kubernetes" givet. Den taler om, hvordan du kan bruge K8s til at sikre høj tilgængelighed af dine applikationer og sikre maksimal ydeevne.

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Af tradition er vi glade for at kunne præsentere video af rapporten (44 minutter, meget mere informativ end artiklen) og hovedresuméet i tekstform. Gå!

Lad os analysere rapportens emne ord for ord og starte fra slutningen.

Kubernetes

Lad os sige, at vi har Docker-containere på vores vært. For hvad? For at sikre repeterbarhed og isolation, hvilket igen giver mulighed for enkel og god udrulning, CI/CD. Vi har mange sådanne køretøjer med containere.

Hvad giver Kubernetes i dette tilfælde?

  1. Vi holder op med at tænke på disse maskiner og begynder at arbejde med "skyen" klynge af containere eller bælg (grupper af beholdere).
  2. Desuden tænker vi ikke engang på individuelle bælg, men klarer flereоstørre grupper. Sådan primitiver på højt niveau tillade os at sige, at der er en skabelon til at køre en bestemt arbejdsbyrde, og her er det nødvendige antal forekomster for at køre den. Hvis vi efterfølgende ændrer skabelonen, ændres alle instanser.
  3. Med deklarativ API I stedet for at udføre en sekvens af specifikke kommandoer, beskriver vi "verdens struktur" (i YAML), som er skabt af Kubernetes. Og igen: Når beskrivelsen ændres, ændres dens faktiske visning også.

Ressourcestyring

CPU

Lad os køre nginx, php-fpm og mysql på serveren. Disse tjenester vil faktisk have endnu flere processer kørende, som hver især kræver computerressourcer:

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)
(tallene på diaset er "papegøjer", det abstrakte behov for hver proces for computerkraft)

For at gøre det nemmere at arbejde med dette, er det logisk at kombinere processer i grupper (f.eks. alle nginx-processer i én gruppe "nginx"). En enkel og indlysende måde at gøre dette på er at lægge hver gruppe i en beholder:

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

For at fortsætte skal du huske, hvad en container er (i Linux). Deres udseende blev muliggjort takket være tre nøglefunktioner i kernen, implementeret for ganske lang tid siden: kapaciteter, navnerum и cgrupper. Og yderligere udvikling blev lettet af andre teknologier (herunder praktiske "skaller" som Docker):

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

I forbindelse med rapporten er vi kun interesserede i cgrupper, fordi kontrolgrupper er den del af funktionaliteten af ​​containere (Docker, etc.), der implementerer ressourcestyring. Processer kombineret i grupper, som vi ønskede, er kontrolgrupper.

Lad os vende tilbage til CPU-kravene for disse processer, og nu til grupper af processer:

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)
(Jeg gentager, at alle tal er et abstrakt udtryk for behovet for ressourcer)

Samtidig har selve CPU'en en vis endelig ressource (i eksemplet er dette 1000), som alle kan mangle (summen af ​​alle gruppers behov er 150+850+460=1460). Hvad vil der ske i dette tilfælde?

Kernen begynder at distribuere ressourcer og gør det "retfærdigt", hvilket giver den samme mængde ressourcer til hver gruppe. Men i det første tilfælde er der flere af dem end nødvendigt (333>150), så overskuddet (333-150=183) forbliver i reserve, som også er ligeligt fordelt mellem to andre containere:

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Som et resultat: den første container havde nok ressourcer, den anden - den havde ikke nok ressourcer, den tredje - den havde ikke nok ressourcer. Dette er resultatet af handlinger "ærlig" planlægger i LinuxCFS. Dens funktion kan justeres ved hjælp af opgaven vægte hver af beholderne. For eksempel sådan her:

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Lad os se på tilfældet med mangel på ressourcer i den anden beholder (php-fpm). Alle containerressourcer er fordelt ligeligt mellem processer. Som et resultat fungerer masterprocessen godt, men alle arbejdere sænker farten og modtager mindre end halvdelen af, hvad de har brug for:

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Sådan fungerer CFS-planlæggeren. Vi vil yderligere kalde de vægte, som vi tildeler containere anmodninger. Hvorfor det er sådan - se videre.

Lad os se på hele situationen fra den anden side. Som du ved, fører alle veje til Rom, og i tilfælde af en computer, til CPU'en. Én CPU, mange opgaver - du har brug for et trafiklys. Den enkleste måde at styre ressourcer på er "trafiklys": de gav en proces en fast adgangstid til CPU'en, derefter den næste osv.

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Denne tilgang kaldes hårde kvoter (svær begrænsning). Lad os huske det simpelthen som grænser. Men hvis du distribuerer grænser til alle containere, opstår der et problem: mysql kørte langs vejen og på et tidspunkt sluttede dets behov for CPU, men alle andre processer er tvunget til at vente, indtil CPU'en ledig.

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Lad os vende tilbage til Linux-kernen og dens interaktion med CPU'en - det overordnede billede er som følger:

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

cgroup har to indstillinger - i det væsentlige er disse to simple "drejninger", der giver dig mulighed for at bestemme:

  1. vægt for container (anmodninger) er aksjer;
  2. procentdel af den samlede CPU-tid for at arbejde med containeropgaver (grænser) er kontingent.

Hvordan måler man CPU?

Der er forskellige måder:

  1. Hvad er papegøjer, ingen ved - du skal forhandle hver gang.
  2. Interesse klarere, men relativt: 50% af en server med 4 kerner og med 20 kerner er helt forskellige ting.
  3. Du kan bruge de allerede nævnte vægte, som Linux kender, men de er også relative.
  4. Den mest passende mulighed er at måle computerressourcer i sekunder. De der. i sekunder af processortid i forhold til sekunder af realtid: 1 sekund af processortid blev givet pr. 1 rigtige sekund - dette er en hel CPU-kerne.

For at gøre det endnu nemmere at tale, begyndte de at måle direkte ind kerner, hvilket betyder af dem samme CPU-tid i forhold til den rigtige. Da Linux forstår vægte, men ikke så meget CPU-tid/kerner, var der behov for en mekanisme til at oversætte fra den ene til den anden.

Lad os overveje et simpelt eksempel med en server med 3 CPU-kerner, hvor tre pods vil få vægte (500, 1000 og 1500), som let konverteres til de tilsvarende dele af kernerne, der er allokeret til dem (0,5, 1 og 1,5).

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Hvis du tager en anden server, hvor der vil være dobbelt så mange kerner (6), og placerer de samme pods der, kan fordelingen af ​​kerner let beregnes ved blot at gange med 2 (henholdsvis 1, 2 og 3). Men et vigtigt øjeblik opstår, når en fjerde pod dukker op på denne server, hvis vægt for nemheds skyld vil være 3000. Den fjerner en del af CPU-ressourcerne (halvdelen af ​​kernerne), og for de resterende pods genberegnes de (halveret):

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Kubernetes og CPU-ressourcer

I Kubernetes måles CPU-ressourcer normalt i milliadrax, dvs. 0,001 kerner tages som basisvægt. (Det samme i Linux/cgroups terminologi kaldes en CPU-share, selvom mere præcist 1000 millicores = 1024 CPU-shares.) K8s sikrer, at den ikke placerer flere pods på serveren, end der er CPU-ressourcer for summen af ​​vægten af ​​alle pods.

Hvordan sker det? Når du tilføjer en server til en Kubernetes-klynge, rapporteres det, hvor mange CPU-kerner den har til rådighed. Og når du opretter en ny pod, ved Kubernetes-planlæggeren, hvor mange kerner denne pod skal bruge. Dermed vil poden blive tildelt en server, hvor der er nok kerner.

Hvad vil der ske hvis nej anmodning er angivet (dvs. poden har ikke et defineret antal kerner, den skal bruge)? Lad os finde ud af, hvordan Kubernetes generelt tæller ressourcer.

For en pod kan du angive både anmodninger (CFS-planlægger) og grænser (husker du trafiklyset?):

  • Hvis de er angivet ens, tildeles poden en QoS-klasse garanteret. Dette antal kerner, der altid er tilgængelige for den, er garanteret.
  • Hvis anmodningen er mindre end grænsen - QoS-klasse sprængbar. De der. Vi forventer, at en pod for eksempel altid bruger 1 kerne, men denne værdi er ikke en begrænsning for den: undertiden pod kan bruge mere (når serveren har ledige ressourcer til dette).
  • Der er også en QoS-klasse bedste indsats — det omfatter netop de pods, for hvilke anmodningen ikke er specificeret. Ressourcer gives til dem sidst.

Память

Med hukommelse er situationen ens, men lidt anderledes - trods alt er arten af ​​disse ressourcer anderledes. Generelt er analogien som følger:

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Lad os se, hvordan anmodninger implementeres i hukommelsen. Lad poderne leve på serveren, ændre hukommelsesforbruget, indtil en af ​​dem bliver så stor, at den løber tør for hukommelse. I dette tilfælde dukker OOM-morderen op og dræber den største proces:

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Dette passer ikke altid til os, så det er muligt at regulere, hvilke processer der er vigtige for os og ikke bør slås ihjel. For at gøre dette skal du bruge parameteren oom_score_adj.

Lad os vende tilbage til CPU'ens QoS-klasser og tegne en analogi med oom_score_adj-værdierne, der bestemmer hukommelsesforbrugsprioriteter for pods:

  • Den laveste oom_score_adj værdi for en pod - -998 - betyder, at en sådan pod skal aflives sidst, dette garanteret.
  • Den højeste - 1000 - er bedste indsats, sådanne bælg dræbes først.
  • For at beregne de resterende værdier (sprængbar) der er en formel, hvis essens bunder i, at jo flere ressourcer en pod har anmodet om, jo ​​mindre sandsynlighed er der for, at den bliver dræbt.

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Det andet "twist" - grænse_i_bytes - for grænser. Med det er alt enklere: vi tildeler simpelthen den maksimale mængde af udstedt hukommelse, og her (i modsætning til CPU'en) er der ingen spørgsmål om, hvordan man måler det (hukommelse).

I alt

Hver pod i Kubernetes er givet requests и limits - begge parametre for CPU og hukommelse:

  1. baseret på anmodninger fungerer Kubernetes-planlæggeren, som distribuerer pods blandt servere;
  2. baseret på alle parametre bestemmes pod'ens QoS-klasse;
  3. Relative vægte beregnes baseret på CPU-anmodninger;
  4. CFS-planlæggeren er konfigureret baseret på CPU-anmodninger;
  5. OOM killer er konfigureret baseret på hukommelsesanmodninger;
  6. et "trafiklys" er konfigureret baseret på CPU-grænser;
  7. Baseret på hukommelsesgrænser er der konfigureret en grænse for cgroup.

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Generelt svarer dette billede på alle spørgsmål om, hvordan hoveddelen af ​​ressourcestyring foregår i Kubernetes.

Autoskalering

K8s cluster-autoscaler

Lad os forestille os, at hele klyngen allerede er optaget, og en ny pod skal oprettes. Selvom poden ikke kan vises, hænger den i status Verserende. For at den skal vises, kan vi forbinde en ny server til klyngen eller... installere cluster-autoscaler, som vil gøre det for os: Bestil en virtuel maskine fra cloud-udbyderen (ved hjælp af en API-anmodning) og tilslut den til klyngen , hvorefter poden tilføjes.

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Dette er autoskalering af Kubernetes-klyngen, som fungerer godt (efter vores erfaring). Men som andre steder er der nogle nuancer her...

Så længe vi øgede klyngens størrelse, var alt fint, men hvad sker der, når klyngen begyndte at frigøre sig? Problemet er, at migrering af pods (for at frigøre værter) er meget teknisk vanskeligt og dyrt med hensyn til ressourcer. Kubernetes bruger en helt anden tilgang.

Overvej en klynge med 3 servere, der har Deployment. Den har 6 pods: nu er der 2 til hver server. Af en eller anden grund ønskede vi at slukke for en af ​​serverne. For at gøre dette bruger vi kommandoen kubectl drain, hvilken:

  • vil forbyde at sende nye pods til denne server;
  • vil slette eksisterende pods på serveren.

Da Kubernetes er ansvarlig for at vedligeholde antallet af pods (6), er det simpelthen vil genskabe dem på andre noder, men ikke på den, der er deaktiveret, da den allerede er markeret som utilgængelig til hosting af nye pods. Dette er en grundlæggende mekaniker for Kubernetes.

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Der er dog også en nuance her. I en lignende situation, for StatefulSet (i stedet for Deployment), vil handlingerne være anderledes. Nu har vi allerede en stateful applikation - for eksempel tre pods med MongoDB, hvoraf den ene har en eller anden form for problem (dataene er blevet beskadiget eller en anden fejl, der forhindrer poden i at starte korrekt). Og vi beslutter igen at deaktivere én server. Hvad vil der ske?

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

MongoDB kunne dø, fordi den har brug for et beslutningsdygtighed: for en klynge på tre installationer skal mindst to fungere. Dog dette ikke sker - tak til PodDisruptionBudget. Denne parameter bestemmer det mindst nødvendige antal arbejdspods. At vide, at en af ​​MongoDB pods ikke længere fungerer, og at se, at PodDisruptionBudget er indstillet til MongoDB minAvailable: 2, Kubernetes vil ikke tillade dig at slette en pod.

Nederste linje: For at bevægelsen (og faktisk genskabelsen) af pods skal fungere korrekt, når klyngen frigives, er det nødvendigt at konfigurere PodDisruptionBudget.

Horisontal skalering

Lad os overveje en anden situation. Der er en applikation, der kører som Deployment i Kubernetes. Brugertrafik kommer til dens pods (for eksempel er der tre af dem), og vi måler en bestemt indikator i dem (f.eks. CPU-belastning). Når belastningen stiger, optager vi det på en tidsplan og øger antallet af pods for at distribuere anmodninger.

I dag i Kubernetes behøver dette ikke at gøres manuelt: en automatisk stigning/reduktion i antallet af pods er konfigureret afhængigt af værdierne af de målte belastningsindikatorer.

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

De vigtigste spørgsmål her er: hvad der præcist skal måles и hvordan man tolker opnåede værdier (for at træffe en beslutning om at ændre antallet af bælg). Du kan måle meget:

Autoskalering og ressourcestyring i Kubernetes (gennemgang og videorapport)

Sådan gør du det teknisk - indsamle metrics osv. — Jeg talte detaljeret i rapporten om Overvågning og Kubernetes. Og det vigtigste råd til at vælge de optimale parametre er eksperiment!

Der er BRUG metode (Ubrugsmætning og fejl), hvis betydning er som følger. På hvilket grundlag giver det mening at skalere for eksempel php-fpm? Baseret på det faktum, at arbejderne er ved at løbe tør, er dette udnyttelse. Og hvis arbejderne er forbi, og nye forbindelser ikke accepteres, er det allerede mætning. Begge disse parametre skal måles, og afhængigt af værdierne skal der foretages skalering.

I stedet for en konklusion

Rapporten har en fortsættelse: om vertikal skalering og hvordan man vælger de rigtige ressourcer. Jeg vil tale om dette i fremtidige videoer vores YouTube - abonner så du ikke går glip af noget!

Videoer og dias

Video fra forestillingen (44 minutter):

Præsentation af rapporten:

PS

Andre rapporter om Kubernetes på vores blog:

Kilde: www.habr.com

Tilføj en kommentar