Ni Kubernetes ytelsestips

Ni Kubernetes ytelsestips

Hei alle sammen! Mitt navn er Oleg Sidorenkov, jeg jobber hos DomClick som leder for infrastrukturteamet. Vi har brukt Kubik i produksjon i mer enn tre år, og i løpet av denne tiden har vi opplevd mange forskjellige interessante øyeblikk med den. I dag skal jeg fortelle deg hvordan du, med den riktige tilnærmingen, kan presse enda mer ytelse ut av vanilje Kubernetes for klyngen din. Klar jevn fart!

Dere vet alle godt at Kubernetes er et skalerbart åpen kildekodesystem for containerorkestrering; vel, eller 5 binærfiler som virker magi ved å administrere livssyklusen til mikrotjenestene dine i et servermiljø. I tillegg er det et ganske fleksibelt verktøy som kan settes sammen som Lego for maksimal tilpasning for ulike oppgaver.

Og alt ser ut til å være i orden: kast servere inn i klyngen som ved i en brannkasse, og du vil ikke kjenne noen sorg. Men hvis du er for miljøet, vil du tenke: "Hvordan kan jeg holde bålet brennende og skåne skogen?" Med andre ord, hvordan finne måter å forbedre infrastruktur og redusere kostnader på.

1. Overvåk team- og applikasjonsressurser

Ni Kubernetes ytelsestips

En av de vanligste, men effektive metodene er innføring av forespørsler/grenser. Del applikasjoner etter navnerom og navnerom etter utviklingsteam. Før distribusjon, angi applikasjonsverdier for forbruk av prosessortid, minne og flyktig lagring.

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

Gjennom erfaring kom vi til konklusjonen: du bør ikke blåse opp forespørsler fra grensene med mer enn to ganger. Volumet til klyngen beregnes basert på forespørsler, og hvis du gir applikasjoner en forskjell i ressurser, for eksempel 5-10 ganger, så forestill deg hva som vil skje med noden din når den blir fylt med pods og plutselig får belastning. Ikke noe bra. Ved et minimum, struping og maksimalt, vil du si farvel til arbeideren og få en syklisk belastning på de gjenværende nodene etter at podene begynner å bevege seg.

I tillegg med hjelp limitranges I starten kan du angi ressursverdier for beholderen - 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

Ikke glem å begrense navneromsressurser slik at ett team ikke kan overta alle ressursene 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 det fremgår av beskrivelsen resourcequotas, hvis ops-teamet ønsker å distribuere pods som vil forbruke ytterligere 10 cpu, vil ikke planleggeren tillate dette og vil gi en feilmelding:

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 å løse et slikt problem kan du skrive et verktøy, for eksempel som dette, i stand til å lagre og forplikte tilstanden til kommandoressurser.

2. Velg den optimale fillagringen

Ni Kubernetes ytelsestips

Her vil jeg gjerne berøre emnet vedvarende volumer og diskundersystemet til Kubernetes-arbeidernoder. Jeg håper at ingen bruker "kuben" på en HDD i produksjon, men noen ganger er en vanlig SSD ikke lenger nok. Vi støtt på et problem der loggene drepte disken på grunn av I/O-operasjoner, og det er ikke mange løsninger:

  • Bruk SSD-er med høy ytelse eller bytt til NVMe (hvis du administrerer din egen maskinvare).

  • Reduser loggingsnivået.

  • Gjør "smart" balansering av belger som raper disken (podAntiAffinity).

Skjermen ovenfor viser hva som skjer under nginx-ingress-controller til disken når access_logs-logging er aktivert (~12 tusen logger/sek). Denne tilstanden kan selvfølgelig føre til forringelse av alle applikasjoner på denne noden.

Når det gjelder PV, dessverre, jeg har ikke prøvd alt typer Vedvarende volumer. Bruk det beste alternativet som passer deg. Historisk har det skjedd i vårt land at en liten del av tjenester krever RWX-volumer, og for lenge siden begynte de å bruke NFS-lagring til denne oppgaven. Billig og... nok. Selvfølgelig spiste han og jeg dritt - velsigne deg, men vi lærte å justere det, og hodet mitt gjør ikke vondt lenger. Og hvis mulig, flytt til S3-objektlagring.

3. Samle optimaliserte bilder

Ni Kubernetes ytelsestips

Det er best å bruke beholderoptimaliserte bilder slik at Kubernetes kan hente dem raskere og utføre dem mer effektivt. 

Optimalisert betyr at bildene:

  • inneholde bare én applikasjon eller utføre bare én funksjon;

  • liten i størrelse, fordi store bilder overføres dårligere over nettverket;

  • ha helse- og beredskapsendepunkter som lar Kubernetes iverksette tiltak i tilfelle nedetid;

  • bruk containervennlige operativsystemer (som Alpine eller CoreOS), som er mer motstandsdyktige mot konfigurasjonsfeil;

  • bruk flertrinnsbygg slik at du bare kan distribuere kompilerte applikasjoner og ikke de medfølgende kildene.

Det finnes mange verktøy og tjenester som lar deg sjekke og optimalisere bilder på farten. Det er viktig å alltid holde dem oppdatert og testet for sikkerhets skyld. Som et resultat får du:

  1. Redusert nettverksbelastning på hele klyngen.

  2. Reduserer oppstartstid for containere.

  3. Mindre størrelse på hele Docker-registeret.

4. Bruk DNS-buffer

Ni Kubernetes ytelsestips

Hvis vi snakker om høy belastning, er livet ganske elendig uten å justere klyngens DNS-system. En gang i tiden støttet Kubernetes-utviklerne deres kube-dns-løsning. Det ble også implementert her, men denne programvaren var ikke spesielt innstilt og ga ikke den nødvendige ytelsen, selv om det så ut til å være en enkel oppgave. Så dukket det opp coredns, som vi byttet til og ikke hadde noen sorg; det ble senere standard DNS-tjeneste i K8s. På et tidspunkt vokste vi til 40 tusen rps til DNS-systemet, og denne løsningen ble også utilstrekkelig. Men tilfeldigvis kom Nodelocaldns ut, aka node local cache, aka NodeLocal DNSCache.

Hvorfor bruker vi dette? Det er en feil i Linux-kjernen som, når flere anrop gjennom conntrack NAT over UDP, fører til en rasebetingelse for oppføringer i conntrack-tabeller, og en del av trafikken gjennom NAT går tapt (hver tur gjennom tjenesten er NAT). Nodelocaldns løser dette problemet ved å kvitte seg med NAT og oppgradere tilkoblingen til TCP til oppstrøms DNS, samt lokalt bufre oppstrøms DNS-spørringer (inkludert en kort 5-sekunders negativ hurtigbuffer).

5. Skaler pods horisontalt og vertikalt automatisk

Ni Kubernetes ytelsestips

Kan du med sikkerhet si at alle mikrotjenestene dine er klare for en to til tredobling av belastningen? Hvordan allokere ressurser til applikasjonene dine på riktig måte? Å holde et par pods i gang utover arbeidsbelastningen kan være overflødig, men å holde dem rygg mot rygg risikerer nedetid fra en plutselig økning i trafikken til tjenesten. Tjenester som f.eks Horisontal Pod Autoscaler и Vertical Pod Autoscaler.

VPA lar deg automatisk øke forespørsler/begrensninger for beholderne i poden avhengig av faktisk bruk. Hvordan kan det være nyttig? Hvis du har pods som av en eller annen grunn ikke kan skaleres horisontalt (noe som ikke er helt pålitelig), kan du prøve å overlate endringer i ressursene til VPA. Funksjonen er et anbefalingssystem basert på historiske og nåværende data fra metrisk-serveren, så hvis du ikke ønsker å endre forespørsler/grenser automatisk, kan du ganske enkelt overvåke de anbefalte ressursene for containerne dine og optimere innstillingene for å spare CPU og minne i klyngen.

Ni Kubernetes ytelsestipsBildet er tatt fra https://levelup.gitconnected.com/kubernetes-autoscaling-101-cluster-autoscaler-horizontal-pod-autoscaler-and-vertical-pod-2a441d9ad231

Planleggeren i Kubernetes er alltid basert på forespørsler. Uansett hvilken verdi du legger der, vil planleggeren søke etter en passende node basert på den. Grenseverdiene er nødvendige for at kuben skal forstå når den skal strupe eller drepe poden. Og siden den eneste viktige parameteren er forespørselsverdien, vil VPA jobbe med den. Hver gang du skalerer en applikasjon vertikalt, definerer du hva forespørslene skal være. Hva vil skje med grensene da? Denne parameteren vil også skaleres proporsjonalt.

Her er for eksempel de vanlige podinnstillingene:

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

Anbefalingsmotoren bestemmer at applikasjonen din krever 300m CPU og 500Mi for å kjøre ordentlig. Du får følgende innstillinger:

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

Som nevnt ovenfor er dette proporsjonal skalering basert på forespørsler/grenser-forholdet i manifestet:

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

  • Minne: 250Mi → 500Mi: forhold 1:2.

når det gjelder HPA, da er operasjonsmekanismen mer gjennomsiktig. Beregninger som CPU og minne er terskelverdi, og hvis gjennomsnittet av alle replikaer overskrider terskelen, skaleres applikasjonen med +1 sub til verdien faller under terskelen eller til maksimalt antall replikaer er nådd.

Ni Kubernetes ytelsestipsBildet er tatt fra https://levelup.gitconnected.com/kubernetes-autoscaling-101-cluster-autoscaler-horizontal-pod-autoscaler-and-vertical-pod-2a441d9ad231

I tillegg til de vanlige beregningene som CPU og minne, kan du sette terskler på dine egendefinerte beregninger fra Prometheus og jobbe med dem hvis du tror det er den mest nøyaktige indikasjonen på når du skal skalere applikasjonen din. Når applikasjonen stabiliserer seg under den angitte metriske terskelen, vil HPA begynne å skalere pods ned til minimumsantallet av replikaer eller til belastningen når den spesifiserte terskelen.

6. Ikke glem Node Affinity og Pod Affinity

Ni Kubernetes ytelsestips

Ikke alle noder kjører på samme maskinvare, og ikke alle pods trenger å kjøre dataintensive applikasjoner. Kubernetes lar deg angi spesialisering av noder og pods ved hjelp av Nodetilhørighet и Pod-tilhørighet.

Hvis du har noder som er egnet for beregningsintensive operasjoner, er det for maksimal effektivitet bedre å knytte applikasjoner til de tilsvarende nodene. For å gjøre dette bruk nodeSelector med en nodeetikett.

La oss si at du har to noder: en med CPUType=HIGHFREQ og et stort antall raske kjerner, en annen med MemoryType=HIGHMEMORY mer minne og raskere ytelse. Den enkleste måten er å tilordne distribusjon til en node HIGHFREQved å legge til seksjonen spec denne velgeren:

…
nodeSelector:
	CPUType: HIGHFREQ

En dyrere og spesifikk måte å gjøre dette på er å bruke nodeAffinity i feltet affinity seksjon spec. Det er to alternativer:

  • requiredDuringSchedulingIgnoredDuringExecution: hard innstilling (planleggeren vil distribuere pods kun på spesifikke noder (og ingen andre steder));

  • preferredDuringSchedulingIgnoredDuringExecution: myk innstilling (planleggeren vil prøve å distribuere til spesifikke noder, og hvis det mislykkes, vil den prøve å distribuere til neste tilgjengelige node).

Du kan spesifisere en spesifikk syntaks for å administrere nodeetiketter, for eksempel In, NotIn, Exists, DoesNotExist, Gt eller Lt. Men husk at komplekse metoder i lange lister med etiketter vil bremse beslutningstaking i kritiske situasjoner. Med andre ord, hold det enkelt.

Som nevnt ovenfor lar Kubernetes deg angi affiniteten til de gjeldende podene. Det vil si at du kan sørge for at enkelte poder fungerer sammen med andre poder i samme tilgjengelighetssone (relevant for skyer) eller noder.

В podAffinity felt affinity seksjon spec de samme feltene er tilgjengelige som ved nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution и preferredDuringSchedulingIgnoredDuringExecution. Den eneste forskjellen er det matchExpressions binder podene til en node som allerede kjører en pod med den etiketten.

Kubernetes tilbyr også et felt podAntiAffinity, som tvert imot ikke binder poden til en node med spesifikke pods.

Om uttrykk nodeAffinity Det samme rådet kan gis: prøv å holde reglene enkle og logiske, ikke prøv å overbelaste pod-spesifikasjonen med et komplekst sett med regler. Det er veldig enkelt å lage en regel som ikke samsvarer med betingelsene til klyngen, noe som skaper unødvendig belastning på planleggeren og reduserer den totale ytelsen.

7. Smekker og toleranser

Det er en annen måte å administrere planleggeren på. Hvis du har en stor klynge med hundrevis av noder og tusenvis av mikrotjenester, så er det veldig vanskelig å ikke la visse pods være vert for bestemte noder.

Mekanismen for smuss – forbudte regler – hjelper med dette. For eksempel, i visse scenarier kan du forby visse noder fra å kjøre pods. For å bruke flekker på en spesifikk node må du bruke alternativet taint i kubectl. Spesifiser nøkkelen og verdien, og deretter tast like NoSchedule eller NoExecute:

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

Det er også verdt å merke seg at smussmekanismen støtter tre hovedeffekter: NoSchedule, NoExecute и PreferNoSchedule.

  • NoSchedule betyr at det foreløpig ikke vil være noen tilsvarende oppføring i pod-spesifikasjonen tolerations, vil den ikke kunne distribueres på noden (i dette eksemplet node10).

  • PreferNoSchedule - forenklet versjon NoSchedule. I dette tilfellet vil planleggeren prøve å ikke tildele pods som ikke har en samsvarende oppføring tolerations per node, men dette er ikke en vanskelig begrensning. Hvis det ikke er ressurser i klyngen, vil pods begynne å distribueres på denne noden.

  • NoExecute - denne effekten utløser umiddelbar evakuering av pods som ikke har en tilsvarende inngang tolerations.

Interessant nok kan denne oppførselen kanselleres ved å bruke tolerasjonsmekanismen. Dette er praktisk når det er en "forbudt" node og du bare trenger å plassere infrastrukturtjenester på den. Hvordan gjøre det? Tillat bare de belgene som det er en passende toleranse for.

Slik ser pod-spesifikasjonen ut:

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

Dette betyr ikke at den neste omdistribueringen vil falle på denne spesielle noden, dette er ikke Node Affinity-mekanismen og nodeSelector. Men ved å kombinere flere funksjoner, kan du oppnå svært fleksible planleggerinnstillinger.

8. Angi Pod-distribusjonsprioritet

Bare fordi du har pods tilordnet noder betyr ikke det at alle pods må behandles med samme prioritet. Det kan for eksempel være lurt å distribuere noen pods før andre.

Kubernetes tilbyr forskjellige måter å konfigurere Pod Priority og Preemption på. Innstillingen består av flere deler: objekt PriorityClass og feltbeskrivelser priorityClassName i pod-spesifikasjonen. La oss se på 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 skaper PriorityClass, gi den et navn, beskrivelse og verdi. Den høyere value, jo høyere prioritet. Verdien kan være et hvilket som helst 32-bits heltall mindre enn eller lik 1 000 000 000. Høyere verdier er reservert for oppdragskritiske systempoder som vanligvis ikke kan unngås. Forskyvning vil kun skje hvis en høyprioritert pod ikke har noe sted å snu, da vil noen av podene fra en bestemt node bli evakuert. Hvis denne mekanismen er for stiv for deg, kan du legge til alternativet preemptionPolicy: Never, og da vil det ikke være noen forkjøpsrett, poden vil stå først i køen og vente på at planleggeren finner ledige ressurser for den.

Deretter lager vi en pod der vi angir 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 lage så mange prioriterte klasser du vil, selv om det anbefales å ikke la deg rive med av dette (for eksempel begrense deg til lav, middels og høy prioritet).

Dermed kan du om nødvendig øke effektiviteten ved å distribuere kritiske tjenester som nginx-ingress-controller, coredns, etc.

9. Optimaliser ETCD-klyngen

Ni Kubernetes ytelsestips

ETCD kan kalles hjernen til hele klyngen. Det er veldig viktig å opprettholde driften av denne databasen på et høyt nivå, siden operasjonshastigheten i Cube avhenger av den. En ganske standard, og samtidig god løsning vil være å beholde ETCD-klyngen på masternodene for å ha en minimumsforsinkelse til kube-apiserveren. Hvis du ikke kan gjøre dette, plasser ETCD så nærme som mulig, med god båndbredde mellom deltakerne. Vær også oppmerksom på hvor mange noder fra ETCD som kan falle ut uten å skade klyngen

Ni Kubernetes ytelsestips

Husk at overdreven økning av antall medlemmer i en klynge kan øke feiltoleransen på bekostning av ytelsen, alt bør være med måte.

Hvis vi snakker om å sette opp tjenesten, er det noen anbefalinger:

  1. Ha god maskinvare, basert på størrelsen på klyngen (du kan lese her).

  2. Juster noen parametere hvis du har spredt en klynge mellom et par DC-er eller nettverket ditt og disker lar mye å være ønsket (du kan lese her).

Konklusjon

Denne artikkelen beskriver punktene som teamet vårt prøver å overholde. Dette er ikke en trinnvis beskrivelse av handlinger, men alternativer som kan være nyttige for å optimalisere klyngeoverhead. Det er tydelig at hver klynge er unik på sin måte, og konfigurasjonsløsninger kan variere mye, så det ville vært interessant å få tilbakemelding på hvordan du overvåker Kubernetes-klyngen og hvordan du forbedrer ytelsen. Del opplevelsen din i kommentarene, det vil være interessant å vite.

Kilde: www.habr.com