ShĂ«n. pĂ«rkth.: kjo storie e mĂ«simdhĂ«nies nga Omio â njĂ« grumbullues europian i udhĂ«timeve â e kalon lexuesin nga teoria bazĂ« nĂ« nuancat emocionuese praktike tĂ« konfigurimit tĂ« Kubernetes. Njohja me raste tĂ« tilla ndihmon jo vetĂ«m nĂ« zgjerimin e horizontit, por gjithashtu nĂ« parandaluar problemet jo triviale.

A keni pasur ndonjëherë përvojën që aplikacioni të 'ngjiste' në vend, të ndalte përgjigjen ndaj kërkesave për kontrollin e gjendjes (health checks) dhe nuk e kuptonit arsyen e këtij sjelljeje? Një nga shpjegimet e mundshme lidhet me kufirin e kuotave për burimet e CPU. Kjo do të jetë tema e këtij arti.
TL;DR:
Ne theksojmë fortesë për t'u hequr dorë nga kufijtë e CPU në Kubernetes (ose për të çaktivizuar kuotat CFS në Kubelet), nëse po përdoret një version i bërthamës Linux me gabime në kuotat CFS. Në bërthamë është një gabim serioz dhe që çon në shtrëngime të tepruara dhe vonesa.
Në Omio e gjithë infrastruktura menaxhohet nga Kubernetes. Të gjitha ngarkesat tona stateful dhe stateless punojnë ekskluzivisht në Kubernetes (përdorim Google Kubernetes Engine). Gjatë gjashtë muajve të fundit, kemi vënë re ngadalësime të rastësishme. Aplikacionet ngecin ose ndalojnë së përgjiguri ndaj kontrollëve të gjendjes, humbin lidhjen me rrjetin, etj. Një sjellje e tillë na ka futur në një ngërç për një kohë të gjatë, dhe përfundimisht ne vendosëm të merremi me problemin për një kohë të gjatë.
Përmbledhje e artikullit:
- Disa fjalë rreth kontejnerëve dhe Kubernetes;
- Si janë zbatuar kërkesat dhe kufijtë e CPU;
- Si funksionon kufiri i CPU në ambientet me disa bërthama;
- Si të monitoroni shtrëngimin e CPU;
- Zgjidhja e problemit dhe nuancat.
Disa fjalë rreth kontejnerëve dhe Kubernetes
Kubernetes, në thelb, është standardi modern në botën e infrastrukturës. Detyra e tij kryesore është orkestrimi i kontejnerëve.
Kontenierët
Në të kaluarën, na ka dashur të krijojmë artefakte të tilla si Java JAR'e/WAR'e, Python Egg'e ose skedarë ekzekutivë për t'i filluar më pas në servera. Megjithatë, për t'i bërë ata të funksionojnë, ishte e nevojshme të bëhej punë shtesë: të instalohej ambienti ekzekutiv (Java/Python), të vendoseshin skedarët e nevojshëm në vende të duhura, të sigurohej përputhshmëria me një version të caktuar të sistemit operativ, etj. Në fjalë të tjera, duhej kushtuar vëmendje të madhe menaxhimit të konfigurimeve (çka shpesh shërbente si shkak i mosmarrëveshjeve midis zhvilluesve dhe administratorëve të sistemeve).
KontejnerĂ«t e kanĂ« ndryshuar gjithçka. Tani tani bĂ«het njĂ« imazh kontejneri. Ai mund tĂ« paraqitet si njĂ« lloj skedari ekzekutiv tĂ« zgjeruar, i cili pĂ«rmban jo vetĂ«m programin, por edhe njĂ« mjedis tĂ« plotĂ« ekzekutimi (Java/Python/âŠ), si dhe skedarĂ«t/paketat e nevojshme, tĂ« instaluar paraprakisht dhe gati pĂ«r tu nisur. KonteinerĂ«t mund tĂ« vendosen dhe fillohen nĂ« servera tĂ« ndryshĂ«m pa veprime tĂ« tjera shtesĂ«.
PĂ«r mĂ« tepĂ«r, konteinerĂ«t funksionojnĂ« nĂ« njĂ« ambient-kuti tĂ« vetin. Ata kanĂ« njĂ« adapter tĂ« vet virtual tĂ« rrjetit, njĂ« sistem skedari me qasje tĂ« kufizuar, hierarkinĂ« e tyre tĂ« proceseve, kufizime pĂ«r CPU dhe memorie etj. TĂ« gjitha kĂ«to realizohen falĂ« njĂ« nĂ«n-sistemi tĂ« veçantĂ« tĂ« bĂ«rthamĂ«s Linux â namespaces (hapĂ«sirat e emrave).
Kubernetes
Siç u tha mĂ« parĂ«, Kubernetes Ă«shtĂ« orkestruesi i konteinerĂ«ve. Ai funksionon kĂ«shtu: ju i ofroni atij njĂ« grup makinash dhe mĂ« pas thoni: âHej, Kubernetes, nis dhjetĂ« kopje tĂ« konteinerit tim me 2 procesorĂ« dhe 3 GB memorie pĂ«r secilin, dhe mbaji ato nĂ« gjendje pune!â. Kubernetes merr pĂ«rsipĂ«r gjithçka tjetĂ«r. Ai do tĂ« gjejĂ« kapacitete tĂ« lira, do tĂ« nisĂ« konteinerĂ«t dhe do tâi ristartojĂ« sipas nevojĂ«s, do tĂ« nxjerrĂ« pĂ«rditĂ«sime kur tĂ« ndryshojnĂ« versionet etj. NĂ« thelb, Kubernetes lejon tĂ« disassociosh prej pĂ«rbĂ«rĂ«sit harduerik dhe bĂ«n qĂ« tĂ« gjithĂ« diversitetin e sistemeve tĂ« jetĂ« tĂ« pĂ«rdorshĂ«m pĂ«r vendosjen dhe funksionimin e aplikacioneve.

Kubernetes nga pikëpamja e një personi të zakonshëm
ĂfarĂ« janĂ« request-at dhe limit-et nĂ« Kubernetes
Mirë, ne u morëm vesh me konteinerët dhe Kubernetes. Po ashtu, ne dimë se disa konteinerë mund të jenë në të njëjtën makinë.
Mund tĂ« bĂ«het njĂ« analogji me njĂ« apartament me shumĂ« dhoma. Merret njĂ« ambient i gjerĂ« (makinat/nodet) dhe jepet me qira disa tĂ« dhĂ«nĂ«sve (konteinerĂ«ve). Kubernetes vepron si agjenti imobiliar. Lind pyetja, si t'i mbash qiramarrĂ«sit tĂ« mos konflitkojnĂ« me njĂ«ri-tjetrin? ĂfarĂ« ndodh nĂ«se njĂ«ri prej tyre, le tĂ« themi, vendos tĂ« zĂ«rĂ« banjĂ«n pĂ«r gjysmĂ« dite?
KĂ«tu futen nĂ« lojĂ« request-at dhe limit-et. CPU Request Ă«shtĂ« e nevojshme ekskluzivisht pĂ«r planifikim. ĂshtĂ« diçka si njĂ« "listĂ« dĂ«shirash" e konteinerit, dhe pĂ«rdoret pĂ«r tĂ« zgjedhur nodin mĂ« tĂ« pĂ«rshtatshĂ«m. NĂ« tĂ« njĂ«jtĂ«n kohĂ«, CPU Limit mund tĂ« krahasohet me njĂ« kontratĂ« qiraje â sa herĂ« qĂ« ne zgjedhim njĂ« nod pĂ«r konteinerin, ai nuk mund tĂ« kalojĂ« kufijtĂ« e vendosur.
Si si realizohen request'ët dhe limit'ët në Kubernetes
Kubernetes përdor një mekanizëm të integruar të throttling (përjashtimi i cikleve) për të implementuar limit'ët e CPU. Nëse aplikacioni kalon limitin, aktivizohet throttling (dmth. merr më pak cikle CPU). Request'ët dhe limit'ët për memorie janë organizuar ndryshe, prandaj janë më të lehtë për t'u zbuluar. Mjafton të kontrolloni statusin e fundit të rinovimit të pod'it: a është ai "OOMKilled". Me throttling-un e CPU, nuk është kaq e thjeshtë, pasi K8s bën të disponueshme vetëm metrikat e përdorimit, e jo për cgroups.
CPU Request

Si është implementuar CPU request
Për thjeshtësi, le të marrim një proces si shembull me një makinë me CPU 4-në.
K8s përdor mekanizmin e grupit të kontrollit (cgroups) për të menaxhuar shpërndarjen e burimeve (memorie dhe procesor). Për të, është e disponueshme një model hierarkik: fëmija trashëgon limit'ët e grupit prind. Detajet e shpërndarjes ruhen në sistemin virtual të skedave (/sys/fs/cgroup). Në rastin e procesorit, kjo është /sys/fs/cgroup/cpu,cpuacct/*.
K8s pĂ«rdor skedarin cpu.share pĂ«r shpĂ«rndarjen e burimeve tĂ« procesorit. NĂ« rastin tonĂ«, grupi kryesor i kontrollit merr 4096 pjesĂ« tĂ« burimeve tĂ« CPU â 100% e kapacitetit tĂ« disponueshĂ«m tĂ« procesorit (1 bĂ«rthamĂ« = 1024; kjo Ă«shtĂ« njĂ« vlerĂ« fixe). Grupi kryesor shpĂ«rndan burimet nĂ« mĂ«nyrĂ« proporcionale nĂ« varĂ«si tĂ« pjesĂ«ve tĂ« fĂ«mijĂ«ve tĂ« tij, tĂ« shkruara nĂ« cpu.share, dhe ata, nga ana e tyre, veprojnĂ« njĂ«soj me fĂ«mijĂ«t e tyre, etj. NĂ« njĂ« nyje tipike Kubernetes, grupi kryesor i kontrollit ka tre fĂ«mijĂ«: system.slice, user.slice dhe kubepods. Dy grupet e para pĂ«rdoren pĂ«r shpĂ«rndarjen e burimeve midis ngarkesave sistemike kritike dhe programeve tĂ« pĂ«rdoruesit jashtĂ« K8s. E fundit â kubepods â krijohet nga Kubernetes pĂ«r tĂ« shpĂ«rndarĂ« burimet midis pod'Ă«ve.
Në skemën e mësipërme, shihet se grupet e para dhe të dyta morën nga 1024 pjesë, ndërsa grupit kubepod i janë ndarë 4096 pjesë. Si është e mundur kjo: për shkak se grupit kryesor i janë të disponueshme vetëm 4096 pjesë, ndërsa shuma e pjesëve të fëmijëve të tij tejkalon ndjeshëm këtë numër (6144)? Arsyeja është se vlera ka kuptim logjik, prandaj planifikuesi Linux (CFS) e përdor atë për shpërndarjen proporcionale të burimeve të CPU. Në rastin tonë, dy grupet e para marrin nga 680 pjesë të vërteta (16.6% e 4096), ndërsa kubepod merr pjesët e mbetura 2736 pjesë. Në rastin e papunësisë, grupet e para nuk do të përdorin burimet e ndara.
Fatke, planifikuesi ka një mekanizëm që parandalon humbjen e burimeve të padisponueshme CPU. Ai transferon kapacitetet "në pritje" në një pool global, nga i cili ato shpërndahen në grupe që kanë nevojë për kapacitet shtesë të procesorit (transferimi ndodh në grupe për të shmangur humbjet nga rroundimi). Një metodë e ngjashme aplikohet gjithashtu për të gjitha pasardhësit e pasardhësve.
Ky mekanizëm siguron një shpërndarje të drejtë të kapaciteteve të procesorit dhe siguron që asnjë proces të mos "vjedhë" burime nga të tjerët.
Kufiri i CPU
Megjithëse konfigurimet e limitit dhe kërkesave në K8s duken të ngjashme, realizimi i tyre është radikalisht i ndryshëm: kjo është pjesa më mashtruese dhe më pak e dokumentuar.
K8s angazhon për të realizuar limitet. Konfigurimet e tyre përcaktohen në skedarët cfs_period_us dhe cfs_quota_us në direktorinë cgroup (aty ndodhet gjithashtu skedari cpu.share).
Në dallim nga cpu.share, kuota bazohet në periudhën e kohës, dhe jo në kapacitetin e disponueshëm të procesorit. cfs_period_us përcakton vazhdimësinë e periudhës (epokës) - kjo gjithmonë është 100000 ”s (100 ms). Në K8s ka mundësi për të ndryshuar këtë vlerë, megjithatë ajo është deri tani e disponueshme vetëm në versionin alfa. Planifikuesi përdor epokën për të rinisur kuotat e përdorura. Skedari tjetër, cfs_quota_us, përcakton kohën e disponueshme (kuotën) në çdo epokë. Kini mend për atë që gjithashtu jepet në mikrosekonda. Kuota mund të tejkalojë vazhdimësinë e epokës; në fjalë të tjera, ajo mund të jetë më shumë se 100 ms.
Le të shqyrtojmë dy skenarë në makinat me 16 bërthama (tipi më i zakonshëm i kompjuterëve te ne në Omio):

Skenari 1: 2 flukse dhe limit prej 200 ms. Pa trottling.

Skenari 2: 10 flukse dhe limit prej 200 ms. Trottling fillon pas 20 ms, aksesin në burimet e CPU rikthehet pas 80 ms.
Supozoni se keni vendosur kufirin e CPU në 2 bërthama; Kubernetes do ta konvertojë këtë vlerë në 200 ms. Kjo do të thotë që kontejneri mund të përdorë maksimumi 200 ms kohë procesori pa trottling.
Dhe kĂ«tu fillon argĂ«timi. Siç u tha mĂ« sipĂ«r, kuota e disponueshme Ă«shtĂ« 200 ms. NĂ«se keni 10 flukse qĂ« punojnĂ« paralelisht nĂ« njĂ« makinĂ« me 12 bĂ«rthama (shih ilustrimin e skenarit 2), ndĂ«rsa tĂ« gjithĂ« podâĂ«t e tjerĂ« janĂ« nĂ« pritje, kuota do tĂ« shterojĂ« pas vetĂ«m 20 ms (pasi 10 * 20 ms = 200 ms), dhe tĂ« gjithĂ« flukset e kĂ«tij podâi do tĂ« "ngelin" (trottling) rrjedhje nĂ« njĂ« makinĂ« me 12 bĂ«rthama (shih ilustrimin pĂ«r skenarin 2), ndĂ«rsa tĂ« gjithĂ« pod'Ă«t e tjerĂ« janĂ« nĂ« pritje, kuota do tĂ« ndalet pas vetĂ«m 20 ms (pasi 10 * 20 ms = 200 ms), dhe tĂ« gjitha rrjedhjet e kĂ«tij pod'i do tĂ« "ngjiten" (throttle) pĂ«r 80 ms tĂ« ardhshĂ«m. SituatĂ«n e rĂ«ndon edhe mĂ« shumĂ«, ndryshe nga pĂ«rmendur , pĂ«r shkak tĂ« cilit ndodh trottlizim i tepruar dhe kontejneri nuk mund tĂ« prodhojĂ« as edhe kuotĂ«n e vet.
Si ta vlerĂ«sojmĂ« trottlizimin nĂ« podâave?
Mjafton të hyni në pod dhe të ekzekutoni cat /sys/fs/cgroup/cpu/cpu.stat.
-
nr_periodsâ numri total i periudhave tĂ« planifikuesit; -
nr_throttledâ numri i periudhave tĂ« trottlizuara nĂ« pĂ«rbĂ«rjenr_periods; -
throttled_timeâ koha e totalizuar e trottlizuar nĂ« nanosekonda.

ĂfarĂ« po ndodh nĂ« tĂ« vĂ«rtetĂ«?
Si rezultat, ne kemi një trottlizim të lartë në të gjithë aplikacionet. Ndonjëherë është në një e gjysmë herë më e fortë se sa e pritur!
Kjo çon në gabime të ndryshme - dështime të kontrolleve të gatishmërisë (readiness), ngritje të kontejnerëve, ndërprerje të lidhjeve rrjet, kohëzgjatje brenda thirrjeve shërbimore. Në fund, kjo shfaqet si një rritje e vonesave dhe një rritje e numrit të gabimeve.
Zgjidhja dhe pasojat
Këtu është e thjeshtë. Ne u privuam nga kufizimet e CPU-së dhe merremi me përditësimin e bërthamës së OS në klasterët me versionin më të ri, ku u korrigjua bug-u. Numri i gabimeve (HTTP 5xx) në shërbimet tona ra menjëherë ndjeshëm:
Gabimet HTTP 5xx

Gabimet HTTP 5xx të një shërbimi kritik
Koha e përgjigjes p95

Vonesa e kërkesave të një shërbimi kritik, percentili 95
Shpenzimet operative

Numri i orëve të shpenzuara për instance
Në çfarë gracke?
Siç u tha në fillim të artikullit:
Mund tĂ« bĂ«het njĂ« analogji me njĂ« apartament komunal... Kubernetes vepron si agjenti imobiliar. Por si tĂ« mbash qiraxhinjtĂ« tĂ« mos mĂ«ndojnĂ« me njĂ«ri-tjetrin? ĂfarĂ« nĂ«se njĂ«ri prej tyre, le tĂ« themi, vendos tĂ« zĂ«rĂ« banjĂ«n pĂ«r gjysmĂ« dite?
Këtu është gracka. Një kontejner i pakujdesshëm mund të përthithë të gjithë burimet e disponueshme të procesorit në makinë. Nëse keni një grumbull të kujdesshëm aplikacionesh (për shembull, JVM të konfiguruara siç duhet, Go, Node VM), atëherë kjo nuk është një problem: mund të punoni në këto kushte për një kohë të gjatë. Por nëse aplikacionet janë optimizuar keq ose fare nuk janë optimizuar (FROM java:latest), situata mund të dalë jashtë kontrollit. Ne në Omio kemi Dockerfile bazikë të automatizuar me konfigurime të arsyeshme në default për grumbullin e gjuhëve kryesore, prandaj një problem i tillë nuk ka ekzistuar.
Ne rekomandojmë të vëzhgoni metrikat (përdorimi, ngopja dhe gabimet), vonesat e API dhe frekuenca e shfaqjes së gabimeve. Sigurohuni që rezultatet të përputhen me pritshmëritë.
Linket
Kjo është historia jonë. Materialet në vijim ndihmuan shumë për të kuptuar atë që po ndodh:
- ;
- ;
- ;
- ;
- â kĂ«rkoni "cpu throttling".
Raportet për gabime të Kubernetes:
- ;
- ;
- .
A keni përjetuar probleme të ngjashme në praktikën tuaj ose keni përvojë që lidhet me trottlimin në mjedise prodhimi të kontejnerizuara? Ndani historinë tuaj në komentet!
P.S. nga përkthyesi
Lexoni gjithashtu në blogun tonë:
- «»;
- «»;
- «».
Burimi: habr.com
