Kubernetes: Snabba upp dina tjänster genom att ta bort CPU-gränser

Tillbaka 2016 vi på Buffer bytte till Kubernetes, och nu arbetar cirka 60 noder (på AWS) och 1500 containrar på vårt k8s-kluster som hanteras av sparka. Men vi gick över till mikrotjänster genom att trial and error, och även efter flera års arbete med k8s står vi fortfarande inför nya problem. I det här inlägget kommer vi att prata om processorbegränsningar: varför vi tyckte att de var bra praxis och varför de inte blev så bra.

Processorbegränsningar och strypning

Precis som många andra Kubernetes-användare, Google rekommenderar starkt att du ställer in CPU-gränser. Utan en sådan inställning kan behållare i en nod ta upp all processorkraft, vilket i sin tur orsakar viktiga Kubernetes-processer (t.ex. kubelet) kommer att sluta svara på förfrågningar. Att sätta CPU-gränser är därför ett bra sätt att skydda dina noder.

Processorgränser anger en behållare till den maximala CPU-tid den kan använda för en viss period (standard är 100 ms), och behållaren kommer aldrig att överskrida denna gräns. I Kubernetes för strypning behållare och förhindra att den överskrider gränsen, används ett specialverktyg CFS-kvot, men dessa artificiella CPU-gränser skadar prestandan och ökar svarstiden för dina behållare.

Vad kan hända om vi inte sätter processorgränser?

Tyvärr fick vi själva möta detta problem. Varje nod har en process som ansvarar för att hantera containrar kubelet, och han slutade svara på förfrågningar. När detta händer kommer noden att gå in i tillståndet NotReady, och behållare från den kommer att omdirigeras någon annanstans och skapa samma problem på nya noder. Inget idealiskt scenario, minst sagt.

Manifestation av problemet med strypning och respons

Nyckelmåttet för containerspårning är trottling, den visar hur många gånger din behållare har strypts. Vi noterade med intresse förekomsten av strypning i vissa behållare, oavsett om processorbelastningen var extrem eller inte. Som ett exempel, låt oss ta en titt på en av våra huvudsakliga API:er:

Kubernetes: Snabba upp dina tjänster genom att ta bort CPU-gränser

Som du kan se nedan har vi satt gränsen till 800m (0.8 eller 80 % kärna), och toppvärden når som bäst 200m (20 % kärna). Det verkar som om vi fortfarande har gott om processorkraft innan vi stryper tjänsten...

Kubernetes: Snabba upp dina tjänster genom att ta bort CPU-gränser
Du kanske har märkt att även när processorbelastningen är under de angivna gränserna - betydligt under - så sker strypning fortfarande.

Inför detta upptäckte vi snart flera resurser (problem på github, Presentation om zadano, inlägg på omio) om nedgången i prestanda och svarstid för tjänster på grund av strypning.

Varför ser vi strypning vid låg CPU-belastning? Den korta versionen är: "det finns en bugg i Linux-kärnan som orsakar onödig strypning av behållare med specificerade processorgränser." Om du är intresserad av problemets natur kan du läsa presentationen (video и text alternativ) av Dave Chiluk.

Ta bort CPU-begränsningar (med extrem försiktighet)

Efter långa diskussioner beslutade vi att ta bort processorrestriktioner från alla tjänster som direkt eller indirekt påverkade kritisk funktionalitet för våra användare.

Beslutet var inte lätt eftersom vi värdesätter stabiliteten i vårt kluster högt. Tidigare har vi redan experimenterat med instabiliteten i vårt kluster, och då förbrukade tjänsterna för mycket resurser och saktade ner arbetet i hela deras nod. Nu var allt något annorlunda: vi hade en klar förståelse för vad vi förväntade oss av våra kluster, samt en bra strategi för att implementera de planerade förändringarna.

Kubernetes: Snabba upp dina tjänster genom att ta bort CPU-gränser
Affärskorrespondens i en angelägen fråga.

Hur skyddar du dina noder när restriktioner hävs?

Isolering av "obegränsade" tjänster:

Tidigare har vi redan sett några noder hamna i ett tillstånd notReady, främst på grund av tjänster som förbrukade för mycket resurser.

Vi beslutade att placera sådana tjänster i separata ("märkta") noder så att de inte stör "relaterade" tjänster. Som ett resultat, genom att markera några noder och lägga till toleransparametern till "icke-relaterade" tjänster, uppnådde vi större kontroll över klustret, och det blev lättare för oss att identifiera problem med noder. För att själv utföra liknande processer kan du bekanta dig med dokumentation.

Kubernetes: Snabba upp dina tjänster genom att ta bort CPU-gränser

Tilldela en korrekt processor och minnesbegäran:

Vår största rädsla var att processen skulle förbruka för mycket resurser och noden skulle sluta svara på förfrågningar. Eftersom vi nu (tack vare Datadog) tydligt kunde övervaka alla tjänster i vårt kluster, analyserade jag flera månaders drift av de som vi planerade att utse som "orelaterade". Jag ställde helt enkelt in den maximala CPU-användningen med en marginal på 20%, och tilldelade därmed utrymme i noden i fall k8s försöker tilldela andra tjänster till noden.

Kubernetes: Snabba upp dina tjänster genom att ta bort CPU-gränser

Som du kan se i grafen har den maximala belastningen på processorn nått 242m CPU-kärnor (0.242 processorkärnor). För en processorförfrågan räcker det att ta ett nummer som är något större än detta värde. Observera att eftersom tjänsterna är användarcentrerade, sammanfaller toppbelastningsvärdena med trafiken.

Gör samma sak med minnesanvändning och frågor, och voila - allt är klart! För större säkerhet kan du lägga till automatisk skalning av horisontell pod. Således, varje gång resursbelastningen är hög, kommer automatisk skalning att skapa nya pods, och kubernetes kommer att distribuera dem till noder med ledigt utrymme. Om det inte finns något utrymme kvar i själva klustret kan du ställa in en varning för dig själv eller konfigurera tillägget av nya noder genom deras autoskalning.

Av minusen är det värt att notera att vi förlorade i "behållarens täthet", dvs. antal containrar som körs på en nod. Vi kan också ha många "avslappningar" vid låg trafiktäthet, och det finns också en chans att du når en hög processorbelastning, men autoskalningsnoder bör hjälpa till med det senare.

Resultat

Jag är glad över att kunna publicera dessa utmärkta resultat från experiment under de senaste veckorna; vi har redan sett betydande förbättringar i responsen för alla modifierade tjänster:

Kubernetes: Snabba upp dina tjänster genom att ta bort CPU-gränser

Vi uppnådde de bästa resultaten på vår hemsida (buffer.com), där accelererade tjänsten in tjugotvå gånger!

Kubernetes: Snabba upp dina tjänster genom att ta bort CPU-gränser

Är Linux-kärnfelet fixat?

Ja, Felet har redan åtgärdats och fixen har lagts till i kärnan distributioner version 4.19 och senare.

Dock vid läsning kubernetes problem på github för den andra september 2020 vi stöter fortfarande på omnämnanden av några Linux-projekt med en liknande bugg. Jag tror att vissa Linux-distributioner fortfarande har denna bugg och arbetar bara med att fixa det.

Om din distributionsversion är lägre än 4.19 rekommenderar jag att du uppdaterar till den senaste, men du bör i alla fall försöka ta bort processorrestriktionerna och se om strypningen kvarstår. Nedan kan du se en ofullständig lista över Kubernetes-hanteringstjänster och Linux-distributioner:

  • Debian: fix integrerad i den senaste versionen av distributionen, busteroch ser ganska fräsch ut (Augusti 2020). Vissa tidigare versioner kan också fixas.
  • Ubuntu: fix integrerad i senaste versionen Ubuntu Focal Fossa 20.04
  • EKS har fixat ännu i december 2019. Om din version är lägre än detta bör du uppdatera AMI.
  • kops: Från juni 2020 у kops 1.18+ Huvudvärdbilden kommer att vara Ubuntu 20.04. Om din version av kops är äldre kan du behöva vänta på en fix. Vi själva väntar nu.
  • GKE (Google Cloud): Fix integrerad i januari 2020, men det finns problem med strypningen observeras fortfarande.

Vad ska man göra om korrigeringen löste strypningsproblemet?

Jag är inte säker på att problemet är helt löst. När vi kommer till kärnversionen med fixen kommer jag att testa klustret och uppdatera inlägget. Om någon redan har uppdaterat skulle jag vara intresserad av att läsa dina resultat.

Slutsats

  • Om du arbetar med Docker-behållare under Linux (oavsett Kubernetes, Mesos, Swarm eller andra), kan dina behållare förlora prestanda på grund av strypning;
  • Försök att uppdatera till den senaste versionen av din distribution i hopp om att felet redan har åtgärdats;
  • Att ta bort processorgränser kommer att lösa problemet, men detta är en farlig teknik som bör användas med extrem försiktighet (det är bättre att först uppdatera kärnan och jämföra resultaten);
  • Om du har tagit bort CPU-gränser, övervaka noggrant din CPU och minnesanvändning och se till att dina CPU-resurser överskrider din förbrukning;
  • Ett säkert alternativ skulle vara att autoskala pods för att skapa nya pods vid hög hårdvarubelastning, så att kubernetes tilldelar dem till lediga noder.

Jag hoppas att det här inlägget hjälper dig att förbättra prestandan för dina containersystem.

PS Här författaren korresponderar med läsare och kommentatorer (på engelska).


Källa: will.com

Lägg en kommentar