Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

27 april på konferensen strejk 2019, som en del av avsnittet "DevOps" gavs rapporten "Autoskalning och resurshantering i Kubernetes". Den talar om hur du kan använda K8s för att säkerställa hög tillgänglighet för dina applikationer och säkerställa toppprestanda.

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Av tradition är vi glada att kunna presentera video av rapporten (44 minuter, mycket mer informativ än artikeln) och huvudsammanfattningen i textform. Gå!

Låt oss analysera ämnet för rapporten ord för ord och börja från slutet.

Kubernetes

Låt oss säga att vi har Docker-containrar på vår värd. För vad? För att säkerställa repeterbarhet och isolering, vilket i sin tur möjliggör enkel och bra driftsättning, CI/CD. Vi har många sådana fordon med containrar.

Vad ger Kubernetes i det här fallet?

  1. Vi slutar tänka på dessa maskiner och börjar arbeta med "molnet" kluster av containrar eller baljor (grupper av behållare).
  2. Dessutom tänker vi inte ens på enskilda kapslar, utan hanterar merоstörre grupper. Sådan primitiver på hög nivå låt oss säga att det finns en mall för att köra en viss arbetsbelastning, och här är det antal instanser som krävs för att köra den. Om vi ​​senare ändrar mallen kommer alla instanser att ändras.
  3. Med deklarativt API Istället för att utföra en sekvens av specifika kommandon, beskriver vi "världens struktur" (i YAML), som skapas av Kubernetes. Och återigen: när beskrivningen ändras kommer även dess faktiska visning att ändras.

Resurshantering

CPU

Låt oss köra nginx, php-fpm och mysql på servern. Dessa tjänster kommer faktiskt att ha ännu fler processer igång, som var och en kräver datorresurser:

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)
(siffrorna på bilden är "papegojor", varje processs abstrakta behov av datorkraft)

För att göra det lättare att arbeta med detta är det logiskt att kombinera processer i grupper (till exempel alla nginx-processer till en grupp ”nginx”). Ett enkelt och självklart sätt att göra detta är att lägga varje grupp i en container:

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

För att fortsätta måste du komma ihåg vad en container är (i Linux). Deras utseende möjliggjordes tack vare tre nyckelfunktioner i kärnan, implementerade för ganska länge sedan: kapacitet, namnrymder и cgrupper. Och ytterligare utveckling underlättades av andra tekniker (inklusive bekväma "skal" som Docker):

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

I rapportens sammanhang är vi bara intresserade av cgrupper, eftersom kontrollgrupper är den del av funktionaliteten hos containrar (Docker, etc.) som implementerar resurshantering. Processer kombinerade till grupper, som vi ville, är kontrollgrupper.

Låt oss återgå till CPU-kraven för dessa processer, och nu för grupper av processer:

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)
(Jag upprepar att alla siffror är ett abstrakt uttryck för behovet av resurser)

Samtidigt har själva processorn en viss ändlig resurs (i exemplet är detta 1000), som alla kan sakna (summan av alla gruppers behov är 150+850+460=1460). Vad kommer att hända i det här fallet?

Kärnan börjar distribuera resurser och gör det "rättvist", vilket ger samma mängd resurser till varje grupp. Men i det första fallet finns det fler av dem än vad som behövs (333>150), så överskottet (333-150=183) förblir i reserv, som också är lika fördelat mellan två andra containrar:

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Som ett resultat: den första behållaren hade tillräckligt med resurser, den andra – den hade inte tillräckligt med resurser, den tredje – den hade inte tillräckligt med resurser. Detta är resultatet av handlingar "ärlig" schemaläggare i Linux - CFS. Dess funktion kan justeras med hjälp av uppdraget vikt var och en av behållarna. Till exempel, så här:

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Låt oss titta på fallet med brist på resurser i den andra behållaren (php-fpm). Alla containerresurser fördelas lika mellan processer. Som ett resultat fungerar masterprocessen bra, men alla arbetare saktar ner och får mindre än hälften av vad de behöver:

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Så här fungerar CFS-schemaläggaren. Vi kommer vidare att kalla de vikter som vi tilldelar containrar förfrågningar. Varför det är så - se vidare.

Låt oss titta på hela situationen från andra sidan. Som ni vet leder alla vägar till Rom, och i fallet med en dator, till processorn. En CPU, många uppgifter - du behöver ett trafikljus. Det enklaste sättet att hantera resurser är "trafikljus": de gav en process en fast åtkomsttid till CPU, sedan nästa osv.

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Detta tillvägagångssätt kallas hårda kvoter (hårt begränsande). Låt oss komma ihåg det helt enkelt som gränser. Men om du distribuerar gränser till alla containrar uppstår ett problem: mysql körde längs vägen och vid någon tidpunkt tog dess behov av CPU slut, men alla andra processer tvingas vänta tills CPU:n på tomgång.

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Låt oss återgå till Linux-kärnan och dess interaktion med processorn - den övergripande bilden är som följer:

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

cgroup har två inställningar - i huvudsak är dessa två enkla "vändningar" som låter dig bestämma:

  1. vikt för container (förfrågningar) är aktier;
  2. procent av den totala CPU-tiden för att arbeta med containeruppgifter (gränser). höjd.

Hur mäter man CPU?

Det finns olika sätt:

  1. Vad är papegojor, ingen vet - du måste förhandla varje gång.
  2. ränta tydligare, men relativt: 50% av en server med 4 kärnor och med 20 kärnor är helt olika saker.
  3. Du kan använda de som redan nämnts vikt, som Linux känner till, men de är också relativa.
  4. Det mest lämpliga alternativet är att mäta datorresurser i sekunder. De där. i sekunder processortid i förhållande till sekunder realtid: 1 sekund processortid gavs per 1 riktig sekund - detta är en hel CPU-kärna.

För att göra det ännu lättare att prata började man mäta direkt in kärnor, vilket betyder med dem samma CPU-tid i förhållande till den riktiga. Eftersom Linux förstår vikter, men inte så mycket CPU-tid/kärnor, behövdes en mekanism för att översätta från den ena till den andra.

Låt oss överväga ett enkelt exempel med en server med 3 CPU-kärnor, där tre pods kommer att ges vikter (500, 1000 och 1500) som enkelt konverteras till motsvarande delar av kärnorna som tilldelats dem (0,5, 1 och 1,5).

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Om du tar en andra server, där det kommer att finnas dubbelt så många kärnor (6), och placerar samma pods där, kan fördelningen av kärnor enkelt beräknas genom att helt enkelt multiplicera med 2 (1, 2 respektive 3). Men ett viktigt ögonblick inträffar när en fjärde pod dyker upp på den här servern, vars vikt, för enkelhetens skull, kommer att vara 3000. Den tar bort en del av CPU-resurserna (halva kärnorna), och för de återstående podarna räknas de om (halveras):

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Kubernetes och CPU-resurser

I Kubernetes mäts CPU-resurser vanligtvis i milliadrax, dvs. 0,001 kärnor tas som basvikt. (Samma sak i Linux/cgroups-terminologi kallas en CPU-andel, även om, mer exakt, 1000 millicores = 1024 CPU-andelar.) K8s ser till att den inte placerar fler poddar på servern än att det finns CPU-resurser för summan av vikterna för alla pods.

Hur går det till? När du lägger till en server i ett Kubernetes-kluster rapporteras det hur många CPU-kärnor den har tillgängliga. Och när du skapar en ny pod vet Kubernetes schemaläggare hur många kärnor denna pod kommer att behöva. Således kommer podden att tilldelas en server där det finns tillräckligt med kärnor.

Vad händer om ingen är begäran specificerad (dvs. podden har inte ett definierat antal kärnor som den behöver)? Låt oss ta reda på hur Kubernetes i allmänhet räknar resurser.

För en pod kan du ange både förfrågningar (CFS-schemaläggare) och gränser (minns du trafikljuset?):

  • Om de anges lika, tilldelas podden en QoS-klass garanterat. Detta antal kärnor som alltid är tillgängliga för den är garanterad.
  • Om begäran är mindre än gränsen - QoS-klass sprängbar. De där. Vi förväntar oss att en pod, till exempel, alltid använder 1 kärna, men detta värde är inte en begränsning för det: ibland pod kan använda mer (när servern har lediga resurser för detta).
  • Det finns också en QoS-klass bästa ansträngningen — Det inkluderar just de pods för vilka begäran inte specificeras. Resurser ges till dem sist.

Память

Med minnet är situationen likartad, men något annorlunda - trots allt är naturen hos dessa resurser annorlunda. I allmänhet är analogin följande:

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Låt oss se hur förfrågningar implementeras i minnet. Låt poddarna leva på servern, ändra minnesförbrukningen, tills en av dem blir så stor att det tar slut på minne. I det här fallet dyker OOM-mördaren upp och dödar den största processen:

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Detta passar oss inte alltid så det går att reglera vilka processer som är viktiga för oss och inte ska dödas. För att göra detta, använd parametern oom_score_adj.

Låt oss återgå till QoS-klasserna för CPU:n och dra en analogi med oom_score_adj-värdena som bestämmer minnesförbrukningsprioriteterna för pods:

  • Det lägsta oom_score_adj-värdet för en pod - -998 - betyder att en sådan pod ska dödas sist, detta garanterat.
  • Den högsta - 1000 - är bästa ansträngningen, sådana baljor dödas först.
  • För att beräkna de återstående värdena (sprängbar) det finns en formel, vars essens går ut på att ju mer resurser en pod har begärt, desto mindre sannolikt är det att den dödas.

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Den andra "twist" - limit_in_bytes - för gränser. Med det är allt enklare: vi tilldelar helt enkelt den maximala mängden utfärdat minne, och här (till skillnad från CPU) är det ingen fråga om hur man mäter det (minne).

Totalt

Varje pod i Kubernetes ges requests и limits - båda parametrarna för CPU och minne:

  1. baserat på förfrågningar fungerar Kubernetes schemaläggare, som distribuerar pods mellan servrar;
  2. baserat på alla parametrar bestäms poddens QoS-klass;
  3. Relativa vikter beräknas baserat på CPU-förfrågningar;
  4. CFS-schemaläggaren är konfigurerad baserat på CPU-förfrågningar;
  5. OOM killer konfigureras baserat på minnesförfrågningar;
  6. ett "trafikljus" är konfigurerat baserat på CPU-gränser;
  7. Baserat på minnesgränser konfigureras en gräns för cgroup.

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

I allmänhet svarar den här bilden på alla frågor om hur huvuddelen av resurshanteringen sker i Kubernetes.

Automatisk skalning

K8s kluster-autoscaler

Låt oss föreställa oss att hela klustret redan är upptaget och en ny pod måste skapas. Även om podden inte kan visas, hänger den i status Avvaktan. För att den ska dyka upp kan vi ansluta en ny server till klustret eller... installera cluster-autoscaler, som gör det åt oss: beställ en virtuell maskin från molnleverantören (med en API-begäran) och anslut den till klustret , varefter podden kommer att läggas till.

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Detta är automatisk skalning av Kubernetes-klustret, vilket fungerar utmärkt (enligt vår erfarenhet). Men som på andra ställen finns det några nyanser här...

Så länge vi ökade klustrets storlek var allt bra, men vad händer när klustret började frigöra sig? Problemet är att migrera pods (för att frigöra värdar) är mycket tekniskt svårt och dyrt i termer av resurser. Kubernetes använder ett helt annat tillvägagångssätt.

Överväg ett kluster med 3 servrar som har Deployment. Den har 6 pods: nu finns det 2 för varje server. Av någon anledning ville vi stänga av en av servrarna. För att göra detta använder vi kommandot kubectl drain, som:

  • kommer att förbjuda att skicka nya pods till denna server;
  • kommer att ta bort befintliga pods på servern.

Eftersom Kubernetes ansvarar för att upprätthålla antalet pods (6), är det helt enkelt kommer att återskapa dem på andra noder, men inte på den som är inaktiverad, eftersom den redan är markerad som otillgänglig för nya pods. Detta är en grundläggande mekanik för Kubernetes.

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Det finns dock en nyans även här. I en liknande situation, för StatefulSet (istället för Deployment), kommer åtgärderna att vara annorlunda. Nu har vi redan en stateful applikation - till exempel tre poddar med MongoDB, varav en har något slags problem (data har blivit korrupta eller ett annat fel som hindrar podden från att starta korrekt). Och vi bestämmer oss igen för att inaktivera en server. Vad kommer att hända?

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

MongoDB skulle kunna dö eftersom det behöver ett beslutfört: för ett kluster med tre installationer måste minst två fungera. Dock detta händer inte - tack vare PodDisruptionBudget. Denna parameter bestämmer det minsta nödvändiga antalet arbetskapslar. Att veta att en av MongoDB-podarna inte längre fungerar och att se att PodDisruptionBudget är inställd för MongoDB minAvailable: 2, Kubernetes tillåter dig inte att ta bort en pod.

Summa summarum: för att rörelsen (och faktiskt återskapandet) av poddar ska fungera korrekt när klustret släpps, är det nödvändigt att konfigurera PodDisruptionBudget.

Horisontell skalning

Låt oss överväga en annan situation. Det finns ett program som körs som distribution i Kubernetes. Användartrafik kommer till dess pods (till exempel finns det tre av dem), och vi mäter en viss indikator i dem (säg CPU-belastning). När belastningen ökar registrerar vi det enligt ett schema och ökar antalet poddar för att distribuera förfrågningar.

Idag i Kubernetes behöver detta inte göras manuellt: en automatisk ökning/minskning av antalet pods konfigureras beroende på värdena för de uppmätta belastningsindikatorerna.

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Huvudfrågorna här är: exakt vad man ska mäta и hur man tolkar erhållna värden (för att fatta beslut om att ändra antalet pods). Du kan mäta mycket:

Autoskalning och resurshantering i Kubernetes (översikt och videorapport)

Hur man gör detta tekniskt - samla in mätvärden osv. — Jag talade i detalj i rapporten om Övervakning och Kubernetes. Och huvudrådet för att välja de optimala parametrarna är experimentera!

Det finns ANVÄND metod (Användningsmättnad och fel), vars innebörd är följande. På vilken grund är det vettigt att skala till exempel php-fpm? Baserat på det faktum att arbetarna tar slut, är detta utnyttjande. Och om arbetarna är över och nya anslutningar inte accepteras, är detta redan det mättnad. Båda dessa parametrar måste mätas och beroende på värdena måste skalning utföras.

I stället för en slutsats

Rapporten har en fortsättning: om vertikal skalning och hur man väljer rätt resurser. Jag kommer att prata om detta i framtida videor vår YouTube - prenumerera så att du inte missar!

Videor och bilder

Video från föreställningen (44 minuter):

Presentation av rapporten:

PS

Andra rapporter om Kubernetes på vår blogg:

Källa: will.com

Lägg en kommentar