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
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.
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:
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:
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
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
Ë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:
Ngarkesa e reduktuar e rrjetit në të gjithë grupin.
Zvogëlimi i kohës së nisjes së kontejnerit.
Madhësia më e vogël e të gjithë regjistrit tuaj Docker.
4. Përdorni një memorie të fshehtë DNS
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
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.
Imazhi 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.
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.
Imazhi 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
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:
Vlen gjithashtu të përmendet se mekanizmi i njollosjes mbështet tre efekte kryesore: NoSchedule, NoExecute и PreferNoSchedule.
NoScheduledo 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.
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 PriorityClassdhe përshkrimet e fushës priorityClassNamenë 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
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.
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:
Keni një pajisje të mirë, bazuar në madhësinë e grupit (mund të lexoni këtu).
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.