Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

27. april på konferansen Streik 2019, som en del av "DevOps"-delen, ble rapporten "Autoskalering og ressursstyring i Kubernetes" gitt. Den snakker om hvordan du kan bruke K8s for å sikre høy tilgjengelighet av applikasjonene dine og sikre topp ytelse.

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Av tradisjon er vi glade for å presentere video av rapporten (44 minutter, mye mer informativ enn artikkelen) og hovedoppsummeringen i tekstform. Gå!

La oss analysere rapportens tema ord for ord og starte fra slutten.

Kubernetes

La oss si at vi har Docker-containere på verten vår. For hva? For å sikre repeterbarhet og isolasjon, som igjen gir mulighet for enkel og god utplassering, CI/CD. Vi har mange slike kjøretøy med containere.

Hva gir Kubernetes i dette tilfellet?

  1. Vi slutter å tenke på disse maskinene og begynner å jobbe med "skyen" klynge av containere eller pods (grupper av beholdere).
  2. Dessuten tenker vi ikke engang på individuelle pods, men klarer merоstørre grupper. Slik primitiver på høyt nivå la oss si at det er en mal for å kjøre en viss arbeidsmengde, og her er det nødvendige antallet forekomster for å kjøre den. Hvis vi senere endrer malen, vil alle forekomster endres.
  3. Med deklarativ API I stedet for å utføre en sekvens av spesifikke kommandoer, beskriver vi "verdens struktur" (i YAML), som er laget av Kubernetes. Og igjen: Når beskrivelsen endres, vil den faktiske visningen også endres.

Ressursforvaltning

prosessor

La oss kjøre nginx, php-fpm og mysql på serveren. Disse tjenestene vil faktisk ha enda flere prosesser kjørende, som hver krever dataressurser:

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)
(tallene på lysbildet er "papegøyer", det abstrakte behovet for hver prosess for datakraft)

For å gjøre det lettere å jobbe med dette er det logisk å kombinere prosesser i grupper (for eksempel alle nginx-prosesser til én gruppe “nginx”). En enkel og åpenbar måte å gjøre dette på er å legge hver gruppe i en beholder:

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

For å fortsette må du huske hva en beholder er (i Linux). Utseendet deres ble muliggjort takket være tre nøkkelfunksjoner i kjernen, implementert for ganske lenge siden: evner, navnerom и cgrupper. Og videreutvikling ble tilrettelagt av andre teknologier (inkludert praktiske "skall" som Docker):

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

I sammenheng med rapporten er vi kun interessert i cgrupper, fordi kontrollgrupper er den delen av funksjonaliteten til containere (Docker, etc.) som implementerer ressursstyring. Prosesser kombinert i grupper, slik vi ønsket, er kontrollgrupper.

La oss gå tilbake til CPU-kravene for disse prosessene, og nå for grupper av prosesser:

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)
(Jeg gjentar at alle tall er et abstrakt uttrykk for behovet for ressurser)

Samtidig har selve CPU-en en viss begrenset ressurs (i eksemplet er dette 1000), som alle kan mangle (summen av behovene til alle gruppene er 150+850+460=1460). Hva vil skje i dette tilfellet?

Kjernen begynner å distribuere ressurser og gjør det "rettferdig", og gir samme mengde ressurser til hver gruppe. Men i det første tilfellet er det flere av dem enn nødvendig (333>150), så overskuddet (333-150=183) forblir i reserve, som også er likt fordelt mellom to andre beholdere:

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Som et resultat: den første beholderen hadde nok ressurser, den andre – den hadde ikke nok ressurser, den tredje – den hadde ikke nok ressurser. Dette er resultatet av handlinger "ærlig" planlegger i Linux - CFS. Driften kan justeres ved hjelp av oppgaven vekt hver av beholderne. For eksempel slik:

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

La oss se på tilfellet med mangel på ressurser i den andre beholderen (php-fpm). Alle containerressurser er fordelt likt mellom prosesser. Som et resultat fungerer masterprosessen bra, men alle arbeidere senker farten og mottar mindre enn halvparten av det de trenger:

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Dette er hvordan CFS-planleggeren fungerer. Vi vil videre kalle vektene som vi tildeler containere forespørsler. Hvorfor det er slik - se videre.

La oss se på hele situasjonen fra den andre siden. Som du vet, fører alle veier til Roma, og når det gjelder en datamaskin, til CPU. Én CPU, mange oppgaver - du trenger et trafikklys. Den enkleste måten å administrere ressurser på er "trafikklys": de ga en prosess en fast tilgangstid til CPU-en, deretter den neste osv.

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Denne tilnærmingen kalles harde kvoter (vanskelig begrensende). La oss huske det rett og slett som grenser. Men hvis du distribuerer grenser til alle containere, oppstår et problem: mysql kjørte langs veien og på et tidspunkt sluttet behovet for CPU, men alle andre prosesser er tvunget til å vente til CPU-en tomgang.

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

La oss gå tilbake til Linux-kjernen og dens interaksjon med CPU - det overordnede bildet er som følger:

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

cgroup har to innstillinger - i hovedsak er dette to enkle "vridninger" som lar deg bestemme:

  1. vekt for container (forespørsler) er aksjer;
  2. prosentandel av den totale CPU-tiden for arbeid med containeroppgaver (grenser) er kvote.

Hvordan måle CPU?

Det er forskjellige måter:

  1. Hva er papegøyer, ingen vet - du må forhandle hver gang.
  2. interesse klarere, men relativt: 50 % av en server med 4 kjerner og med 20 kjerner er helt forskjellige ting.
  3. Du kan bruke de som allerede er nevnt vekt, som Linux kjenner til, men de er også relative.
  4. Det mest passende alternativet er å måle dataressurser i sekunder. De. i sekunder prosessortid i forhold til sekunder sanntid: 1 sekund prosessortid ble gitt per 1 ekte sekund - dette er én hel CPU-kjerne.

For å gjøre det enda lettere å snakke begynte de å måle direkte inn kjerner, som betyr av dem samme CPU-tid i forhold til den virkelige. Siden Linux forstår vekter, men ikke så mye CPU-tid/kjerner, var det nødvendig med en mekanisme for å oversette fra den ene til den andre.

La oss vurdere et enkelt eksempel med en server med 3 CPU-kjerner, hvor tre pods vil bli gitt vekter (500, 1000 og 1500) som enkelt konverteres til de tilsvarende delene av kjernene som er tildelt dem (0,5, 1 og 1,5).

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Hvis du tar en andre server, hvor det vil være dobbelt så mange kjerner (6), og plasserer de samme podene der, kan fordelingen av kjerner enkelt beregnes ved ganske enkelt å multiplisere med 2 (henholdsvis 1, 2 og 3). Men et viktig øyeblikk oppstår når en fjerde pod dukker opp på denne serveren, hvis vekt, for enkelhets skyld, vil være 3000. Den tar bort en del av CPU-ressursene (halvparten av kjernene), og for de gjenværende podene blir de beregnet på nytt (halvert):

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Kubernetes og CPU-ressurser

I Kubernetes måles CPU-ressurser vanligvis i milliadrax, dvs. 0,001 kjerner er tatt som basisvekt. (Det samme i Linux/cgroups-terminologi kalles en CPU-andel, selv om mer presist 1000 millicores = 1024 CPU-andeler.) K8s sikrer at den ikke plasserer flere pods på serveren enn det er CPU-ressurser for summen av vektene til alle pods.

Hvordan skjer dette? Når du legger til en server i en Kubernetes-klynge, rapporteres det hvor mange CPU-kjerner den har tilgjengelig. Og når du oppretter en ny pod, vet Kubernetes-planleggeren hvor mange kjerner denne poden trenger. Dermed vil poden bli tildelt en server hvor det er nok kjerner.

Hva vil skje hvis no forespørselen er spesifisert (det vil si at poden ikke har et definert antall kjerner den trenger)? La oss finne ut hvordan Kubernetes generelt teller ressurser.

For en pod kan du spesifisere både forespørsler (CFS-planlegger) og grenser (husker du trafikklyset?):

  • Hvis de er spesifisert like, blir poden tildelt en QoS-klasse garantert. Dette antallet kjerner som alltid er tilgjengelig for den er garantert.
  • Hvis forespørselen er mindre enn grensen - QoS-klasse sprengbar. De. Vi forventer at en pod, for eksempel, alltid bruker 1 kjerne, men denne verdien er ikke en begrensning for den: noen ganger pod kan bruke mer (når serveren har ledige ressurser for dette).
  • Det er også en QoS-klasse beste innsats – det inkluderer nettopp de podene som forespørselen ikke er spesifisert for. Ressurser gis til dem sist.

Память

Med hukommelse er situasjonen lik, men litt annerledes - tross alt er naturen til disse ressursene annerledes. Generelt er analogien som følger:

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

La oss se hvordan forespørsler implementeres i minnet. La podene leve på serveren, endre minneforbruk, til en av dem blir så stor at den går tom for minne. I dette tilfellet dukker OOM-morderen opp og dreper den største prosessen:

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Dette passer ikke alltid oss, så det er mulig å regulere hvilke prosesser som er viktige for oss og ikke bør avlives. For å gjøre dette, bruk parameteren oom_score_adj.

La oss gå tilbake til QoS-klassene til CPU og tegne en analogi med oom_score_adj-verdiene som bestemmer minneforbruksprioriteter for pods:

  • Den laveste oom_score_adj-verdien for en pod - -998 - betyr at en slik pod skal drepes sist, dette garantert.
  • Den høyeste - 1000 - er beste innsats, slike belger drepes først.
  • For å beregne de gjenværende verdiene (sprengbar) det er en formel, hvis essens koker ned til det faktum at jo flere ressurser en pod har bedt om, jo ​​mindre sannsynlig er det at den blir drept.

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Den andre "vridningen" - limit_in_bytes - for grenser. Med det er alt enklere: vi tildeler ganske enkelt den maksimale mengden utstedt minne, og her (i motsetning til CPU) er det ikke snakk om hvordan man måler det (minne).

Totalt

Hver pod i Kubernetes er gitt requests и limits - begge parametere for CPU og minne:

  1. basert på forespørsler fungerer Kubernetes-planleggeren, som distribuerer pods mellom servere;
  2. basert på alle parametere bestemmes podens QoS-klasse;
  3. Relative vekter beregnes basert på CPU-forespørsler;
  4. CFS-planleggeren er konfigurert basert på CPU-forespørsler;
  5. OOM killer er konfigurert basert på minneforespørsler;
  6. et "trafikklys" er konfigurert basert på CPU-grenser;
  7. Basert på minnegrenser, er en grense konfigurert for cgroup.

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Generelt svarer dette bildet på alle spørsmålene om hvordan hoveddelen av ressursstyring skjer i Kubernetes.

Autoskalering

K8s cluster-autoscaler

La oss forestille oss at hele klyngen allerede er opptatt og en ny pod må opprettes. Selv om poden ikke kan vises, henger den i status Venter. For at den skal vises, kan vi koble en ny server til klyngen eller... installere cluster-autoscaler, som vil gjøre det for oss: bestill en virtuell maskin fra skyleverandøren (ved hjelp av en API-forespørsel) og koble den til klyngen , hvoretter poden legges til.

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Dette er autoskalering av Kubernetes-klyngen, som fungerer utmerket (i vår erfaring). Men som andre steder er det noen nyanser her...

Så lenge vi økte klyngestørrelsen, var alt bra, men hva skjer når klyngen begynte å frigjøre seg? Problemet er at migrering av pods (for å frigjøre verter) er veldig teknisk vanskelig og dyrt i form av ressurser. Kubernetes bruker en helt annen tilnærming.

Vurder en klynge med 3 servere som har Deployment. Den har 6 pods: nå er det 2 for hver server. Av en eller annen grunn ønsket vi å slå av en av serverne. For å gjøre dette bruker vi kommandoen kubectl drain, hvilken:

  • vil forby sending av nye pods til denne serveren;
  • vil slette eksisterende pods på serveren.

Siden Kubernetes er ansvarlig for å opprettholde antall pods (6), er det ganske enkelt vil gjenskape dem på andre noder, men ikke på den som er deaktivert, siden den allerede er merket som utilgjengelig for å være vert for nye pods. Dette er en grunnleggende mekaniker for Kubernetes.

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Det er imidlertid en nyanse her også. I en lignende situasjon, for StatefulSet (i stedet for Deployment), vil handlingene være annerledes. Nå har vi allerede en stateful applikasjon - for eksempel tre pods med MongoDB, hvorav den ene har et slags problem (dataene har blitt ødelagt eller en annen feil som hindrer poden i å starte riktig). Og vi bestemmer oss igjen for å deaktivere én server. Hva vil skje?

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

MongoDB kunne dø fordi den trenger et quorum: for en klynge på tre installasjoner må minst to fungere. Imidlertid dette skjer ikke - Takk til PodDisruptionBudget. Denne parameteren bestemmer det minste nødvendige antallet arbeidskapsler. Å vite at en av MongoDB-podene ikke lenger fungerer, og se at PodDisruptionBudget er satt for MongoDB minAvailable: 2, Kubernetes vil ikke tillate deg å slette en pod.

Bunnlinjen: for at bevegelsen (og faktisk gjenskapingen) av pods skal fungere riktig når klyngen frigjøres, er det nødvendig å konfigurere PodDisruptionBudget.

Horisontal skalering

La oss vurdere en annen situasjon. Det er en applikasjon som kjører som distribusjon i Kubernetes. Brukertrafikk kommer til podene (for eksempel er det tre av dem), og vi måler en viss indikator i dem (for eksempel CPU-belastning). Når belastningen øker, registrerer vi det på en tidsplan og øker antall pods for å distribuere forespørsler.

I dag i Kubernetes trenger ikke dette å gjøres manuelt: en automatisk økning/reduksjon i antall pods er konfigurert avhengig av verdiene til de målte belastningsindikatorene.

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Hovedspørsmålene her er: nøyaktig hva som skal måles и hvordan tolke oppnådde verdier (for å ta en beslutning om å endre antall pods). Du kan måle mye:

Autoskalering og ressursadministrasjon i Kubernetes (oversikt og videorapport)

Hvordan gjøre dette teknisk - samle inn beregninger osv. — Jeg snakket i detalj i rapporten om Overvåking og Kubernetes. Og hovedrådet for å velge de optimale parametrene er eksperiment!

Det er BRUK metode (Utnyttelsesmetning og feil), hvis betydning er som følger. På hvilket grunnlag er det fornuftig å skalere for eksempel php-fpm? Basert på det faktum at arbeiderne går tom, er dette utnyttelse. Og hvis arbeiderne er over og nye forbindelser ikke aksepteres, er dette allerede metning. Begge disse parameterne skal måles, og avhengig av verdiene må skalering utføres.

I stedet for en konklusjon

Rapporten har en fortsettelse: om vertikal skalering og hvordan velge riktige ressurser. Jeg vil snakke om dette i fremtidige videoer vår YouTube - abonner så du ikke går glipp av noe!

Videoer og lysbilder

Video fra forestillingen (44 minutter):

Presentasjon av rapporten:

PS

Andre rapporter om Kubernetes på bloggen vår:

Kilde: www.habr.com

Legg til en kommentar