Nëntë këshilla për performancën e Kubernetes

Nëntë këshilla për performancën e Kubernetes

Pershendetje te gjitheve! Emri im është Oleg Sidorenkov, unë punoj në DomClick si udhëheqës i ekipit të infrastrukturës. Ne kemi më shumë se tre vjet që e përdorim Kubin për shitje dhe gjatë kësaj kohe kemi përjetuar shumë momente të ndryshme interesante me të. Sot do t'ju tregoj se si, me qasjen e duhur, mund të shtrydhni edhe më shumë performancë nga Kubernetes vanilje për grupin tuaj. Gati, vazhdo!

Ju të gjithë e dini shumë mirë se Kubernetes është një sistem i shkallëzueshëm me burim të hapur për orkestrimin e kontejnerëve; mirë, ose 5 binare që bëjnë magji duke menaxhuar ciklin e jetës së mikroshërbimeve tuaja në një mjedis serveri. Përveç kësaj, ky është një mjet mjaft fleksibël që mund të montohet si një konstruktor Lego për personalizim maksimal për detyra të ndryshme.

Dhe gjithçka duket të jetë mirë: hidhni serverët në grup, si dru zjarri në një kuti zjarri dhe mos e njihni pikëllimin. Por nëse jeni për mjedisin, atëherë do të mendoni: "Si mund ta mbaj zjarrin në sobë dhe të pendohem për pyllin?". Me fjalë të tjera, si të gjenden mënyra për të përmirësuar infrastrukturën dhe për të ulur kostot.

1. Mbani gjurmët e ekipit dhe burimeve të aplikacionit

Nëntë këshilla për performancën e Kubernetes

Një nga metodat më banale por efektive është futja e kërkesave/kufizimeve. Ndani aplikacionet sipas hapësirave të emrave dhe hapësirat e emrave sipas ekipeve të zhvillimit. Vendosni aplikacionin përpara se të vendosni vlerat për konsumin e kohës së procesorit, kujtesës, ruajtjes kalimtare.

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

Nga përvoja, arritëm në përfundimin: nuk ia vlen të fryhen kërkesat nga kufijtë më shumë se dy herë. Madhësia e grupit llogaritet në bazë të kërkesave, dhe nëse e vendosni aplikacionin në një ndryshim në burime, për shembull, me 5-10 herë, atëherë imagjinoni se çfarë do të ndodhë me nyjen tuaj kur ajo të jetë e mbushur me pods dhe papritmas merr një ngarkesë. Asgjë e mirë. Në minimum, mbytje dhe në maksimum, thuajini lamtumirë punëtorit dhe merrni një ngarkesë ciklike në pjesën tjetër të nyjeve pasi bishtajat të fillojnë të lëvizin.

Përveç kësaj, me ndihmën limitranges mund të vendosni vlerat e burimeve për kontejnerin në fillim - minimale, maksimale dhe të paracaktuara:

➜  ~ 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

Mos harroni të kufizoni burimet e hapësirës së emrave në mënyrë që një komandë të mos mund të marrë të gjitha burimet e grupit:

➜  ~ 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

Siç mund ta shihni nga përshkrimi resourcequotas, nëse komanda ops dëshiron të vendosë pods që do të konsumojnë 10 CPU të tjera, atëherë planifikuesi nuk do ta lejojë atë të bëhet dhe do të lëshojë një gabim:

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

Për të zgjidhur një problem të ngjashëm, mund të shkruani një mjet, për shembull, si этот, i cili mund të ruajë dhe angazhojë gjendjen e burimeve të komandës.

2. Zgjidhni ruajtjen më të mirë të skedarëve

Nëntë këshilla për performancën e Kubernetes

Këtu do të doja të prekja temën e vëllimeve të vazhdueshme dhe nënsistemit të diskut të nyjeve të punëtorëve Kubernetes. Shpresoj që askush të mos përdorë "Cube" në HDD në prodhim, por ndonjëherë edhe një SSD e rregullt tashmë nuk është e mjaftueshme. Ne u përballëm me një problem të tillë që regjistrat po vrisnin diskun nga operacionet I / O, dhe nuk ka shumë zgjidhje këtu:

  • Përdorni SSD me performancë të lartë ose kaloni në NVMe (nëse menaxhoni harduerin tuaj).

  • Ulni nivelin e prerjeve.

  • Bëni balancim "të zgjuar" të bishtajave që përdhunojnë diskun (podAntiAffinity).

Pamja e mësipërme e ekranit tregon se çfarë ndodh nën kontrolluesin nginx-ingress me një disk kur aksesi_logs është i aktivizuar (~12 mijë regjistra/sek). Një gjendje e tillë, natyrisht, mund të çojë në degradimin e të gjitha aplikacioneve në këtë nyje.

Sa për PV, mjerisht, nuk kam provuar gjithçka. llojet Vëllime të vazhdueshme. Përdorni opsionin më të mirë që ju përshtatet. Historikisht në vendin tonë ka ndodhur që një pjesë e vogël e shërbimeve të kenë nevojë për vëllime RWX dhe shumë kohë më parë ata filluan të përdorin ruajtjen NFS për këtë detyrë. Lirë dhe ... mjaftueshëm. Sigurisht, ne hëngrëm mut me të - ji i shëndetshëm, por mësuam si ta akordojmë dhe koka e tij nuk dhemb më. Dhe nëse është e mundur, kaloni në ruajtjen e objekteve S3.

3. Ndërtoni imazhe të optimizuara

Nëntë këshilla për performancën e Kubernetes

Është më mirë të përdorni imazhe të optimizuara për kontejnerë në mënyrë që Kubernetes t'i marrë ato më shpejt dhe t'i ekzekutojë ato në mënyrë më efikase. 

Optimizimi do të thotë që imazhet:

  • përmbajnë vetëm një aplikacion ose kryejnë vetëm një funksion;

  • madhësia e vogël, sepse imazhet e mëdha transmetohen më keq në rrjet;

  • të ketë pika përfundimtare shëndetësore dhe gatishmërie që Kubernetes mund t'i përdorë për të ndërmarrë veprime në rast të ndërprerjes;

  • përdorni sisteme operative miqësore me kontejnerët (si Alpine ose CoreOS) që janë më rezistente ndaj gabimeve të konfigurimit;

  • përdorni ndërtime me shumë faza në mënyrë që të mund të vendosni vetëm aplikacione të përpiluara dhe jo burimet shoqëruese.

Ka shumë mjete dhe shërbime që ju lejojnë të kontrolloni dhe optimizoni imazhet në fluturim. Është e rëndësishme t'i mbani ato gjithmonë të përditësuara dhe të sigurta. Si rezultat, ju merrni:

  1. Ngarkesa e reduktuar e rrjetit në të gjithë grupin.

  2. Zvogëlimi i kohës së nisjes së kontejnerit.

  3. Madhësia më e vogël e të gjithë regjistrit tuaj Docker.

4. Përdorni një memorie të fshehtë DNS

Nëntë këshilla për performancën e Kubernetes

Nëse flasim për ngarkesa të larta, atëherë pa akorduar sistemin DNS të grupit, jeta është mjaft e keqe. Njëherë e një kohë, zhvilluesit e Kubernetes mbështetën zgjidhjen e tyre kube-dns. Ai u zbatua edhe në vendin tonë, por ky softuer nuk u akordua veçanërisht dhe nuk dha performancën e kërkuar, megjithëse, siç duket, detyra është e thjeshtë. Pastaj u shfaqën coredns, në të cilat ne kaluam dhe nuk e dinim pikëllimin, më vonë u bë shërbimi i paracaktuar DNS në K8. Në një moment, ne u rritëm deri në 40 mijë rps në sistemin DNS, dhe kjo zgjidhje gjithashtu nuk ishte e mjaftueshme. Por, me një shans me fat, Nodelocaldns doli, aka node local cache, aka NodeLocal DNSCache.

Pse po e përdorim? Ekziston një gabim në kernelin Linux që, kur akseset e shumëfishta përmes conntrack NAT mbi UDP, çojnë në një kusht gare për të shkruar në tabelat e kontrack, dhe një pjesë e trafikut përmes NAT humbet (çdo udhëtim përmes Shërbimit është NAT). Nodelocaldns e zgjidh këtë problem duke hequr qafe NAT-in dhe duke përmirësuar në lidhjen TCP në DNS në rrjedhën e sipërme, si dhe duke ruajtur në memorien e pyetjeve DNS në rrjedhën e sipërme në nivel lokal (duke përfshirë një memorie të shkurtër negative prej 5 sekondash).

5. Shkallëzimi automatikisht i bishtajave horizontalisht dhe vertikalisht

Nëntë këshilla për performancën e Kubernetes

A mund të thoni me besim se të gjitha mikroshërbimet tuaja janë gati për një rritje dy ose trefish të ngarkesës? Si të shpërndani siç duhet burimet për aplikacionet tuaja? Mbajtja e disa grupeve të punës mbi ngarkesën e punës mund të jetë e tepërt, dhe mbajtja e tyre prapa rrezikon ndërprerjen nga një rritje e papritur e trafikut në shërbim. Mesatarja e artë ndihmon për të arritur magjinë e shumëzimit shërbime të tilla si Autoshkallëzues Horizontal Pod и Vertical Pod Autoscaler.

VPA ju lejon të rritni automatikisht kërkesat/kufijtë e kontejnerëve tuaj në një pod bazuar në përdorimin aktual. Si mund të jetë i dobishëm? Nëse keni Pods që për ndonjë arsye nuk mund të zvogëlohen horizontalisht (gjë që nuk është plotësisht e besueshme), atëherë mund të përpiqeni t'i besoni VPA-së për të ndryshuar burimet e saj. Karakteristika e tij është një sistem rekomandimi i bazuar në të dhënat historike dhe aktuale nga serveri metrikë, kështu që nëse nuk dëshironi të ndryshoni kërkesat/kufizimet automatikisht, thjesht mund të monitoroni burimet e rekomanduara për kontejnerët tuaj dhe të optimizoni cilësimet për të kursyer CPU dhe memorie në grup.

Nëntë këshilla për performancën e KubernetesImazhi i marrë nga https://levelup.gitconnected.com/kubernetes-autoscaling-101-cluster-autoscaler-horizontal-pod-autoscaler-and-vertical-pod-2a441d9ad231

Programuesi në Kubernetes bazohet gjithmonë në kërkesa. Çfarëdo vlere që vendosni atje, planifikuesi do të kërkojë një nyje të përshtatshme bazuar në të. Vlera e kufirit i nevojitet kubletit për të ditur se kur duhet të mbyt ose të vrasë një pod. Dhe meqenëse i vetmi parametër i rëndësishëm është vlera e kërkesave, VPA do të punojë me të. Sa herë që e shkallëzoni aplikacionin tuaj vertikalisht, ju përcaktoni se cilat duhet të jenë kërkesat. Dhe çfarë do të ndodhë me kufijtë atëherë? Ky parametër gjithashtu do të shkallëzohet proporcionalisht.

Për shembull, këtu janë cilësimet tipike të pod:

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

Motori i rekomandimeve përcakton që aplikacioni juaj ka nevojë për 300 m CPU dhe 500 Mi për të funksionuar siç duhet. Do të merrni këto cilësime:

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

Siç u përmend më lart, ky është një shkallëzim proporcional i bazuar në raportin e kërkesave/kufizimeve në manifest:

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

  • Kujtesa: 250 Mi → 500 Mi: raporti 1:2.

Lidhur me HPA, atëherë mekanizmi i funksionimit është më transparent. Pragjet vendosen për metrikat si procesori dhe memoria, dhe nëse mesatarja e të gjitha kopjeve tejkalon pragun, atëherë aplikacioni shkallëzohet me +1 pod derisa vlera të bjerë nën pragun, ose derisa të arrihet numri maksimal i kopjeve.

Nëntë këshilla për performancën e KubernetesImazhi i marrë nga https://levelup.gitconnected.com/kubernetes-autoscaling-101-cluster-autoscaler-horizontal-pod-autoscaler-and-vertical-pod-2a441d9ad231

Përveç matjeve të zakonshme si CPU dhe Kujtesa, mund të vendosni pragje në metrikat tuaja të personalizuara të Prometheus dhe të punoni me to nëse mendoni se kjo është mënyra më e saktë për të përcaktuar se kur duhet të shkallëzoni aplikacionin tuaj. Pasi aplikacioni të stabilizohet nën pragun e specifikuar metrikë, HPA do të fillojë të zvogëlojë Pods në numrin minimal të kopjeve ose derisa ngarkesa të arrijë pragun.

6. Mos harroni për Afinitetin e Nyjeve dhe Afinitetin Pod

Nëntë këshilla për performancën e Kubernetes

Jo të gjitha nyjet funksionojnë në të njëjtin harduer dhe jo të gjitha podet kanë nevojë të ekzekutojnë aplikacione intensive llogaritëse. Kubernetes ju lejon të specifikoni specializimin e nyjeve dhe podeve duke përdorur Afiniteti i nyjeve и Pod Afiniteti.

Nëse keni nyje të përshtatshme për operacione intensive llogaritëse, atëherë për efikasitet maksimal, është më mirë të lidhni aplikacionet me nyjet e duhura. Për ta bërë këtë, përdorni nodeSelector me etiketën e nyjës.

Le të themi se keni dy nyje: njëra me CPUType=HIGHFREQ dhe një numër i madh bërthamash të shpejta, një tjetër me MemoryType=HIGHMEMORY më shumë memorie dhe performancë më të shpejtë. Mënyra më e lehtë është të caktoni një vendosje pod në një nyje HIGHFREQduke shtuar në seksion spec një përzgjedhës si ky:

…
nodeSelector:
	CPUType: HIGHFREQ

Një mënyrë më e kushtueshme dhe specifike për ta bërë këtë është përdorimi nodeAffinity në terren affinity seksioni spec. Ka dy opsione:

  • requiredDuringSchedulingIgnoredDuringExecution: vendosje e vështirë (programuesi do të vendosë pods vetëm në nyje specifike (dhe askund tjetër));

  • preferredDuringSchedulingIgnoredDuringExecution: cilësim i butë (planifikuesi do të përpiqet të vendoset në nyje specifike, dhe nëse dështon, do të përpiqet të vendoset në nyjen tjetër të disponueshme).

Ju mund të specifikoni një sintaksë specifike për menaxhimin e etiketave të nyjeve, për shembull, In, NotIn, Exists, DoesNotExist, Gt ose Lt. Sidoqoftë, mbani mend se metodat komplekse në listat e gjata të etiketave do të ngadalësojnë vendimmarrjen në situata kritike. Me fjalë të tjera, mos e ndërlikoni shumë.

Siç u përmend më lart, Kubernetes ju lejon të vendosni lidhjen e pods aktuale. Kjo do të thotë, ju mund të bëni që pods të caktuara të funksionojnë së bashku me pods të tjerë në të njëjtën zonë disponueshmërie (e rëndësishme për retë) ose nyje.

В podAffinity fushat affinity seksioni spec të njëjtat fusha janë të disponueshme si në rastin e nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution и preferredDuringSchedulingIgnoredDuringExecution. I vetmi ndryshim është se matchExpressions do të lidhë pods me një nyje që tashmë po ekzekuton një pod me atë etiketë.

Më shumë Kubernetes ofron një fushë podAntiAffinity, e cila, në të kundërt, nuk lidh një pod me një nyje me pods specifike.

Rreth shprehjeve nodeAffinity Mund të jepet e njëjta këshillë: përpiquni t'i mbani rregullat të thjeshta dhe logjike, mos u përpiqni të mbingarkoni specifikimet e pod me një grup rregullash komplekse. Është shumë e lehtë të krijosh një rregull që nuk përputhet me kushtet e grupit, duke vënë ngarkesë shtesë në planifikues dhe duke degraduar performancën e përgjithshme.

7. Njollat ​​& Tolerimet

Ekziston një mënyrë tjetër për të menaxhuar planifikuesin. Nëse keni një grup të madh me qindra nyje dhe mijëra mikroshërbime, është shumë e vështirë të parandaloni që pods të caktuara të priten nga nyje të caktuara.

Mekanizmi i njollave - rregullat ndaluese - ndihmon për këtë. Për shembull, mund të parandaloni që nyje të caktuara të ekzekutojnë pods në skenarë të caktuar. Për të aplikuar njollë në një nyje specifike, përdorni opsionin taint në kubectl. Specifikoni çelësin dhe vlerën dhe më pas ngjyrosni si NoSchedule ose NoExecute:

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

Vlen gjithashtu të përmendet se mekanizmi i njollosjes mbështet tre efekte kryesore: NoSchedule, NoExecute и PreferNoSchedule.

  • NoSchedule do të thotë që derisa të ketë një hyrje përkatëse në specifikimin e pod tolerations, nuk mund të vendoset në nyje (në këtë shembull node10).

  • PreferNoSchedule - version i thjeshtuar NoSchedule. Në këtë rast, programuesi do të përpiqet të mos ndajë pods që nuk kanë një hyrje që përputhet. tolerations për nyje, por ky nuk është një kufi i vështirë. Nëse nuk ka burime në grup, atëherë pods do të fillojnë të vendosen në këtë nyje.

  • NoExecute - ky efekt shkakton një evakuim të menjëhershëm të bishtajave që nuk kanë hyrje të përshtatshme tolerations.

Çuditërisht, kjo sjellje mund të zhbëhet duke përdorur mekanizmin e tolerimit. Kjo është e përshtatshme kur ka një nyje "të ndaluar" dhe ju duhet të vendosni vetëm shërbimet e infrastrukturës në të. Si ta bëjmë atë? Lejoni vetëm ato bishtaja për të cilat ka një tolerancë të përshtatshme.

Ja se si do të duken specifikimet e pod:

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

Kjo nuk do të thotë që gjatë rishpërndarjes së radhës, pod do të godasë pikërisht këtë nyje, ky nuk është mekanizmi Node Affinity dhe nodeSelector. Por duke kombinuar disa veçori, mund të arrini një konfigurim shumë fleksibël të planifikuesit.

8. Cakto prioritetin e vendosjes së pod

Vetëm për shkak se keni konfiguruar lidhjet pod-to-node nuk do të thotë që të gjitha pod-të duhet të trajtohen me të njëjtin prioritet. Për shembull, mund të dëshironi të vendosni disa Pods përpara të tjerëve.

Kubernetes ofron mënyra të ndryshme për të vendosur Prioritetin e Pod-it dhe Parandalimin. Vendosja përbëhet nga disa pjesë: objekt PriorityClass dhe përshkrimet e fushës priorityClassName në specifikimin e pod. Konsideroni një shembull:

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"

Ne krijojmë PriorityClass, jepini një emër, përshkrim dhe vlerë. Sa më i lartë value, aq më i lartë është prioriteti. Vlera mund të jetë çdo numër i plotë 32-bit më i vogël ose i barabartë me 1. Vlerat më të larta janë të rezervuara për grupet e sistemit kritik të misionit, të cilat zakonisht nuk mund të parandalohen. Dëbimi do të ndodhë vetëm nëse pod me prioritet të lartë nuk ka ku të rrotullohet, atëherë disa nga pods nga një nyje e caktuar do të evakuohen. Nëse ky mekanizëm është shumë i ngurtë për ju, atëherë mund të shtoni opsionin preemptionPolicy: Never, dhe më pas nuk do të ketë asnjë parandalim, pod do të jetë i pari në radhë dhe do të presë që planifikuesi të gjejë burime falas për të.

Tjetra, ne krijojmë një pod, në të cilin specifikojmë emrin 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
          

Ju mund të krijoni sa më shumë klasa prioritare që dëshironi, megjithëse rekomandohet të mos tërhiqeni me këtë (të themi, kufizoni veten në përparësi të ulët, të mesme dhe të lartë).

Kështu, nëse është e nevojshme, mund të rrisni efikasitetin e vendosjes së shërbimeve kritike, si nginx-ingress-controller, coredns, etj.

9. Optimizoni grupin tuaj ETCD

Nëntë këshilla për performancën e Kubernetes

ETCD mund të quhet truri i të gjithë grupit. Është shumë e rëndësishme të ruhet funksionimi i kësaj baze të dhënash në një nivel të lartë, pasi shpejtësia e operacioneve në "Cube" varet nga ajo. Një zgjidhje mjaft standarde, dhe në të njëjtën kohë, një zgjidhje e mirë do të ishte mbajtja e një grupi ETCD në nyjet kryesore në mënyrë që të ketë një vonesë minimale në kube-apiserver. Nëse kjo nuk është e mundur, atëherë vendoseni ETCD sa më afër që të jetë e mundur, me gjerësi të mirë brezi midis pjesëmarrësve. Kushtojini vëmendje gjithashtu se sa nyje nga ETCD mund të bien pa dëmtuar grupin.

Nëntë këshilla për performancën e Kubernetes

Mbani në mend se një rritje e tepruar e numrit të pjesëmarrësve në grup mund të rrisë tolerancën e gabimeve në kurriz të performancës, gjithçka duhet të jetë në moderim.

Nëse flasim për vendosjen e shërbimit, atëherë ka disa rekomandime:

  1. Keni një pajisje të mirë, bazuar në madhësinë e grupit (mund të lexoni këtu).

  2. Rregulloni disa parametra nëse keni shpërndarë një grup midis një çifti DC ose rrjetit tuaj dhe disqeve lënë shumë për të dëshiruar (mund të lexoni këtu).

Përfundim

Ky artikull përshkruan pikat me të cilat ekipi ynë përpiqet të respektojë. Ky nuk është një përshkrim hap pas hapi i veprimeve, por opsione që mund të jenë të dobishme për të optimizuar shpenzimet e përgjithshme të një grupi. Është e qartë se çdo grup është unik në mënyrën e vet, dhe zgjidhjet e akordimit mund të ndryshojnë shumë, kështu që do të ishte interesante të merrnit komente nga ju: si e monitoroni grupin tuaj Kubernetes, si e përmirësoni performancën e tij. Ndani përvojën tuaj në komente, do të jetë interesante ta dini.

Burimi: www.habr.com