Ni Kubernetes Performance Tips

Ni Kubernetes Performance Tips

Hej alle! Mit navn er Oleg Sidorenkov, jeg arbejder hos DomClick som infrastrukturteamleder. Vi har brugt terningen til salg i mere end tre år, og i løbet af denne tid har vi oplevet mange forskellige interessante øjeblikke med den. I dag vil jeg fortælle dig, hvordan du med den rigtige tilgang kan presse endnu mere ydeevne ud af vanilje Kubernetes til din klynge. Klar, parat, start!

I ved alle godt, at Kubernetes er et skalerbart open source-system til containerorkestrering; godt, eller 5 binære filer, der gør magi ved at styre livscyklussen for dine mikrotjenester i et servermiljø. Derudover er dette et ret fleksibelt værktøj, der kan samles som en Lego-konstruktør for maksimal tilpasning til forskellige opgaver.

Og alt ser ud til at være i orden: Smid servere ind i klyngen, som brænde i en brændekasse, og kender ikke sorgen. Men hvis du er for miljøet, så vil du tænke: “Hvordan kan jeg holde ilden i brændeovnen og fortryde skoven?”. Med andre ord, hvordan man finder måder at forbedre infrastrukturen og reducere omkostningerne.

1. Hold styr på team- og applikationsressourcer

Ni Kubernetes Performance Tips

En af de mest banale, men effektive metoder er indførelse af anmodninger/grænser. Adskil applikationer efter navnerum og navnerum efter udviklingsteams. Indstil applikationen, før du implementerer værdier for forbrug af processortid, hukommelse, kortvarig lagring.

resources:
   requests:
     memory: 2Gi
     cpu: 250m
   limits:
     memory: 4Gi
     cpu: 500m

Af erfaring kom vi til den konklusion: det er ikke værd at puste anmodninger fra grænser med mere end to gange. Klyngestørrelsen beregnes ud fra anmodninger, og hvis du indstiller applikationen til en forskel i ressourcer, for eksempel med 5-10 gange, så forestil dig, hvad der vil ske med din node, når den bliver fyldt med pods og pludselig får en belastning. Intet godt. Som et minimum, regulering, og som et maksimum, sig farvel til arbejderen og få en cyklisk belastning på resten af ​​noderne, efter at bælgerne begynder at bevæge sig.

Derudover med hjælp limitranges du kan indstille ressourceværdier for containeren i starten - minimum, maksimum og standard:

➜  ~ kubectl describe limitranges --namespace ops
Name:       limit-range
Namespace:  ops
Type        Resource           Min   Max   Default Request  Default Limit  Max Limit/Request Ratio
----        --------           ---   ---   ---------------  -------------  -----------------------
Container   cpu                50m   10    100m             100m           2
Container   ephemeral-storage  12Mi  8Gi   128Mi            4Gi            -
Container   memory             64Mi  40Gi  128Mi            128Mi          2

Husk at begrænse navnerumsressourcerne, så én kommando ikke kan tage alle ressourcerne i klyngen:

➜  ~ kubectl describe resourcequotas --namespace ops
Name:                   resource-quota
Namespace:              ops
Resource                Used          Hard
--------                ----          ----
limits.cpu              77250m        80
limits.memory           124814367488  150Gi
pods                    31            45
requests.cpu            53850m        80
requests.memory         75613234944   150Gi
services                26            50
services.loadbalancers  0             0
services.nodeports      0             0

Som du kan se af beskrivelsen resourcequotas, hvis ops-kommandoen ønsker at implementere pods, der vil forbruge yderligere 10 cpu, så vil planlæggeren ikke tillade det at blive gjort og vil udstede en fejl:

Error creating: pods "nginx-proxy-9967d8d78-nh4fs" is forbidden: exceeded quota: resource-quota, requested: limits.cpu=5,requests.cpu=5, used: limits.cpu=77250m,requests.cpu=53850m, limited: limits.cpu=10,requests.cpu=10

For at løse et lignende problem kan du skrive et værktøj, for eksempel som dette, som kan lagre og forpligte kommandoressourcernes tilstand.

2. Vælg den bedste fillagring

Ni Kubernetes Performance Tips

Her vil jeg gerne berøre emnet vedvarende volumener og diskundersystemet af Kubernetes-arbejderknudepunkter. Jeg håber, at ingen bruger "Terningen" på HDD'en i produktionen, men nogle gange er endda en almindelig SSD ikke nok. Vi stod over for et sådant problem, at logfilerne dræbte disken af ​​I/O-operationer, og der er ikke ret mange løsninger her:

  • Brug højtydende SSD'er eller skift til NVMe (hvis du administrerer din egen hardware).

  • Reducer niveauet af logning.

  • Lav "smart" afbalancering af bælg, der raper disken (podAntiAffinity).

Skærmbilledet ovenfor viser, hvad der sker under nginx-ingress-controller med en disk, når access_logs er aktiveret (~12k logs/sek). En sådan tilstand kan selvfølgelig føre til nedbrydning af alle applikationer på denne node.

Med hensyn til PV, desværre, jeg har ikke prøvet alt. typer Vedvarende mængder. Brug den bedste løsning, der passer dig. Det er historisk set sket i vores land, at en lille del af tjenesterne har brug for RWX-volumener, og for lang tid siden begyndte de at bruge NFS-lager til denne opgave. Billig og ... nok. Selvfølgelig spiste vi lort med ham - vær sund, men vi lærte at tune ham, og hans hoved gør ikke længere ondt. Og hvis det er muligt, skift til S3-objektlagring.

3. Byg optimerede billeder

Ni Kubernetes Performance Tips

Det er bedst at bruge containeroptimerede billeder, så Kubernetes kan hente dem hurtigere og udføre dem mere effektivt. 

Optimering betyder, at billeder:

  • kun indeholde én applikation eller kun udføre én funktion;

  • lille størrelse, fordi store billeder overføres værre over netværket;

  • have sundheds- og parathedsslutpunkter, som Kubernetes kan bruge til at handle i tilfælde af nedetid;

  • brug containervenlige operativsystemer (som Alpine eller CoreOS), der er mere modstandsdygtige over for konfigurationsfejl;

  • brug multi-stage builds, så du kun kan implementere kompilerede applikationer og ikke de medfølgende kilder.

Der er mange værktøjer og tjenester, der giver dig mulighed for at tjekke og optimere billeder på farten. Det er vigtigt altid at holde dem opdaterede og sikre. Som et resultat får du:

  1. Reduceret netværksbelastning på hele klyngen.

  2. Nedsat containerstarttid.

  3. Mindre størrelse af hele din Docker-registrering.

4. Brug en DNS-cache

Ni Kubernetes Performance Tips

Hvis vi taler om høje belastninger, så uden at tune klyngens DNS-system, er livet ret elendigt. Engang understøttede Kubernetes-udviklerne deres kube-dns-løsning. Det blev også implementeret i vores land, men denne software tunede ikke særlig ind og gav ikke den krævede ydeevne, selvom opgaven tilsyneladende er enkel. Så dukkede coredns op, som vi skiftede til og kendte ikke sorg, senere blev det standard DNS-tjenesten i K8s. På et tidspunkt voksede vi op til 40 tusind rps til DNS-systemet, og denne løsning var heller ikke nok. Men ved et heldigt tilfælde kom Nodelocaldns ud, aka node local cache, aka NodeLocal DNSCache.

Hvorfor bruger vi det? Der er en fejl i Linux-kernen, der, når flere adgange gennem conntrack NAT over UDP, fører til en race-betingelse for skrivning til conntrack-tabellerne, og en del af trafikken gennem NAT går tabt (hver tur gennem tjenesten er NAT). Nodelocaldns løser dette problem ved at slippe af med NAT og opgradere til TCP-forbindelse til upstream-DNS, samt cache upstream-DNS-forespørgsler lokalt (inklusive en kort 5 sekunders negativ cache).

5. Skaler bælg vandret og lodret automatisk

Ni Kubernetes Performance Tips

Kan du med sikkerhed sige, at alle dine mikrotjenester er klar til en to- til tredobling af belastningen? Hvordan allokerer man ressourcer korrekt til dine applikationer? At holde et par pods kørende ud over arbejdsbyrden kan være overflødigt, og at holde dem ryg mod ryg risikerer nedetid fra en pludselig stigning i trafikken til tjenesten. Den gyldne middelvej hjælper med at opnå besværgelsen af ​​multiplikation såsom tjenester som Horisontal Pod Autoscaler и Vertical Pod Autoscaler.

VPA giver dig mulighed for automatisk at hæve anmodningerne/grænserne for dine beholdere i en pod baseret på faktisk brug. Hvordan kan det være nyttigt? Hvis du har Pods, der af en eller anden grund ikke kan skaleres ud horisontalt (hvilket ikke er helt pålideligt), så kan du prøve at stole på, at VPA ændrer sine ressourcer. Dens funktion er et anbefalingssystem baseret på historiske og aktuelle data fra metric-server, så hvis du ikke ønsker at ændre anmodninger/grænser automatisk, kan du blot overvåge de anbefalede ressourcer til dine containere og optimere indstillingerne for at spare CPU og hukommelse i klyngen.

Ni Kubernetes Performance TipsBillede taget fra https://levelup.gitconnected.com/kubernetes-autoscaling-101-cluster-autoscaler-horizontal-pod-autoscaler-and-vertical-pod-2a441d9ad231

Planlæggeren i Kubernetes er altid baseret på anmodninger. Uanset hvilken værdi du sætter der, vil skemalæggeren lede efter en passende node baseret på den. Grænseværdien er påkrævet af kublet for at vide, hvornår man skal drosle eller dræbe en pod. Og da den eneste vigtige parameter er anmodningsværdien, vil VPA arbejde med den. Når du skalerer din applikation lodret, definerer du, hvilke anmodninger der skal være. Og hvad sker der så med grænserne? Denne parameter vil også blive proportionalt skaleret.

For eksempel, her er de typiske pod-indstillinger:

resources:
   requests:
     memory: 250Mi
     cpu: 200m
   limits:
     memory: 500Mi
     cpu: 350m

Anbefalingsmotoren bestemmer, at din applikation skal bruge 300 m CPU og 500 Mi for at køre korrekt. Du får disse indstillinger:

resources:
   requests:
     memory: 500Mi
     cpu: 300m
   limits:
     memory: 1000Mi
     cpu: 525m

Som nævnt ovenfor er dette proportional skalering baseret på anmodninger/grænser-forholdet i manifestet:

  • CPU: 200m → 300m: forhold 1:1.75;

  • Hukommelse: 250Mi → 500Mi: 1:2-forhold.

med hensyn til HPA, så er driftsmekanismen mere gennemsigtig. Tærskler er indstillet for metrikker såsom processor og hukommelse, og hvis gennemsnittet af alle replikaer overstiger tærsklen, skaleres applikationen med +1 pod, indtil værdien falder under tærsklen, eller indtil det maksimale antal replikaer er nået.

Ni Kubernetes Performance TipsBillede taget fra https://levelup.gitconnected.com/kubernetes-autoscaling-101-cluster-autoscaler-horizontal-pod-autoscaler-and-vertical-pod-2a441d9ad231

Ud over de sædvanlige målinger som CPU og hukommelse, kan du indstille tærskler på dine brugerdefinerede Prometheus-målinger og arbejde med dem, hvis du føler, at dette er den mest nøjagtige måde at bestemme, hvornår du skal skalere din applikation. Når applikationen stabiliserer sig under den specificerede metriske tærskel, begynder HPA at skalere pods ned til det mindste antal replikaer, eller indtil belastningen når den specificerede tærskel.

6. Glem ikke om node-affinitet og pod-affinitet

Ni Kubernetes Performance Tips

Ikke alle noder kører på den samme hardware, og ikke alle pods behøver at køre computerkrævende applikationer. Kubernetes giver dig mulighed for at specificere specialiseringen af ​​noder og pods ved hjælp af Node affinitet и Pod affinitet.

Hvis du har noder, der er egnede til computerintensive operationer, er det bedre at binde applikationer til de relevante noder for maksimal effektivitet. For at gøre dette, brug nodeSelector med node label.

Lad os sige, at du har to noder: en med CPUType=HIGHFREQ og et stort antal hurtige kerner, en anden med MemoryType=HIGHMEMORY mere hukommelse og hurtigere ydeevne. Den nemmeste måde er at tildele en pod-implementering til en node HIGHFREQved at tilføje til afsnittet spec en vælger som denne:

…
nodeSelector:
	CPUType: HIGHFREQ

En mere bekostelig og specifik måde at gøre dette på er at bruge nodeAffinity i marken affinity afsnit spec. Der er to muligheder:

  • requiredDuringSchedulingIgnoredDuringExecution: hård indstilling (planlæggeren vil kun implementere pods på specifikke noder (og ingen andre steder));

  • preferredDuringSchedulingIgnoredDuringExecution: blød indstilling (planlæggeren vil forsøge at implementere til specifikke noder, og hvis det mislykkes, vil den forsøge at implementere til den næste tilgængelige node).

Du kan angive en specifik syntaks til styring af node-etiketter, f.eks. In, NotIn, Exists, DoesNotExist, Gt eller Lt. Husk dog, at komplekse metoder i lange lister af etiketter vil bremse beslutningstagningen i kritiske situationer. Med andre ord, komplicer ikke for meget.

Som nævnt ovenfor giver Kubernetes dig mulighed for at indstille bindingen af ​​aktuelle pods. Det vil sige, at du kan få bestemte pods til at arbejde sammen med andre pods i samme tilgængelighedszone (relevant for skyer) eller noder.

В podAffinity felter affinity afsnit spec de samme felter er tilgængelige som i tilfælde af nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution и preferredDuringSchedulingIgnoredDuringExecution. Den eneste forskel er det matchExpressions vil binde pods til en node, der allerede kører en pod med denne etiket.

Mere Kubernetes tilbyder et felt podAntiAffinity, som derimod ikke binder en pod til en node med specifikke pods.

Om udtryk nodeAffinity det samme råd kan gives: prøv at holde reglerne enkle og logiske, prøv ikke at overbelaste pod-specifikationen med et komplekst sæt regler. Det er meget nemt at oprette en regel, der ikke matcher klyngens betingelser, hvilket lægger ekstra belastning på planlæggeren og forringer den samlede ydeevne.

7. Pletter og tolerationer

Der er en anden måde at styre planlæggeren på. Hvis du har en stor klynge med hundredvis af noder og tusindvis af mikrotjenester, er det meget svært at forhindre visse pods i at blive hostet af bestemte noder.

Mekanismen for pletter - forbud mod regler - hjælper med dette. For eksempel kan du forhindre visse noder i at køre pods i visse scenarier. Brug indstillingen for at anvende farve på en specifik node taint i kubectl. Angiv nøgle og værdi, og så taint like NoSchedule eller NoExecute:

$ kubectl taint nodes node10 node-role.kubernetes.io/ingress=true:NoSchedule

Det er også værd at bemærke, at angrebsmekanismen understøtter tre hovedeffekter: NoSchedule, NoExecute и PreferNoSchedule.

  • NoSchedule betyder, at indtil der er en tilsvarende post i pod-specifikationen tolerations, kan den ikke implementeres til noden (i dette eksempel node10).

  • PreferNoSchedule - forenklet version NoSchedule. I dette tilfælde vil planlæggeren forsøge ikke at tildele pods, der ikke har en matchende post. tolerations pr. node, men dette er ikke en hård grænse. Hvis der ikke er nogen ressourcer i klyngen, begynder pods at installere på denne node.

  • NoExecute - denne effekt udløser en øjeblikkelig evakuering af pods, der ikke har en matchende indgang tolerations.

Mærkeligt nok kan denne adfærd fortrydes ved hjælp af tolerationsmekanismen. Dette er praktisk, når der er en "forbudt" node, og du kun skal placere infrastrukturtjenester på den. Hvordan gør man det? Tillad kun de bælg, for hvilke der er en passende tolerance.

Sådan ser pod-specifikationen ud:

spec:
   tolerations:
     - key: "node-role.kubernetes.io/ingress"
        operator: "Equal"
        value: "true"
        effect: "NoSchedule"

Dette betyder ikke, at poden under den næste omplacering vil ramme præcis denne node, dette er ikke Node Affinity-mekanismen og nodeSelector. Men ved at kombinere flere funktioner, kan du opnå en meget fleksibel planlægningsopsætning.

8. Indstil Pod-implementeringsprioritet

Bare fordi du har konfigureret pod-to-node-bindinger, betyder det ikke, at alle pods skal behandles med samme prioritet. For eksempel vil du måske implementere nogle Pods før andre.

Kubernetes tilbyder forskellige måder at indstille Pod Priority og Preemption på. Indstillingen består af flere dele: objekt PriorityClass og feltbeskrivelser priorityClassName i pod-specifikationen. Overvej et eksempel:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 99999
globalDefault: false
description: "This priority class should be used for very important pods only"

Vi skaber PriorityClass, giv den et navn, beskrivelse og værdi. Jo højere value, jo højere prioritet. Værdien kan være et hvilket som helst 32-bit heltal mindre end eller lig med 1. Højere værdier er reserveret til missionskritiske systempods, som typisk ikke kan undgås. Udsættelsen vil kun ske, hvis den højprioriterede pod ikke har nogen steder at vende sig om, så vil nogle af poderne fra en bestemt node blive evakueret. Hvis denne mekanisme er for stiv til dig, kan du tilføje muligheden preemptionPolicy: Never, og så vil der ikke være nogen præemption, poden vil være den første i køen og vente på, at planlæggeren finder ledige ressourcer til den.

Dernæst opretter vi en pod, hvori vi angiver navnet priorityClassName:

apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    role: myrole
 spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
          protocol: TCP
  priorityClassName: high-priority
          

Du kan oprette lige så mange prioriterede klasser, som du vil, selvom det anbefales ikke at lade dig rive med af dette (f.eks. begrænse dig selv til lav, mellem og høj prioritet).

Således kan du, hvis det er nødvendigt, øge effektiviteten af ​​at implementere kritiske tjenester, såsom nginx-ingress-controller, coredns osv.

9. Optimer din ETCD-klynge

Ni Kubernetes Performance Tips

ETCD kan kaldes hjernen i hele klyngen. Det er meget vigtigt at opretholde driften af ​​denne database på et højt niveau, da hastigheden af ​​operationer i "kuben" afhænger af det. En ret standard, og samtidig en god løsning ville være at beholde en ETCD-klynge på masterknuderne for at have en minimumsforsinkelse til kube-apiserver. Hvis dette ikke er muligt, så placer ETCD'en så tæt som muligt, med god båndbredde mellem deltagerne. Vær også opmærksom på, hvor mange noder fra ETCD der kan falde ud uden at skade klyngen.

Ni Kubernetes Performance Tips

Husk på, at en for stor stigning i antallet af deltagere i klyngen kan øge fejltolerancen på bekostning af ydeevnen, alt skal være med måde.

Hvis vi taler om at konfigurere tjenesten, så er der få anbefalinger:

  1. Har god hardware, baseret på klyngens størrelse (du kan læse her).

  2. Juster et par parametre, hvis du har spredt en klynge mellem et par DC'er eller dit netværk, og diske lader meget tilbage at ønske (du kan læse her).

Konklusion

Denne artikel beskriver de punkter, som vores team forsøger at overholde. Dette er ikke en trin-for-trin beskrivelse af handlinger, men muligheder, der kan være nyttige til at optimere overhead af en klynge. Det er tydeligt, at hver klynge er unik på sin egen måde, og tuningløsninger kan variere meget, så det ville være interessant at få feedback fra dig: hvordan overvåger du din Kubernetes-klynge, hvordan forbedrer du dens ydeevne. Del din oplevelse i kommentarerne, det vil være interessant at vide det.

Kilde: www.habr.com