Kufijtë e CPU dhe mbytja agresive në Kubernetes

Shënim. përkth.: Kjo histori mbresëlënëse e Omio - një grumbullues evropian udhëtimesh - i çon lexuesit nga teoria bazë deri te ndërlikimet praktike magjepsëse të konfigurimit të Kubernetes. Njohja me raste të tilla ju ndihmon jo vetëm të zgjeroni horizontet tuaja, por edhe të parandaloni probleme jo të parëndësishme.

Kufijtë e CPU dhe mbytja agresive në Kubernetes

A ju ka ndodhur ndonjëherë që një aplikacion të ngecë në vend, të mos iu përgjigjet kontrolleve shëndetësore dhe të mos jeni në gjendje të kuptoni pse? Një shpjegim i mundshëm lidhet me kufijtë e kuotave të burimeve të CPU-së. Kjo është ajo për të cilën do të flasim në këtë artikull.

TL; DR:
Ne rekomandojmë fuqimisht çaktivizimin e kufijve të CPU-së në Kubernetes (ose çaktivizimin e kuotave CFS në Kubelet) nëse jeni duke përdorur një version të kernelit Linux me një gabim të kuotave CFS. Në thelb nuk ka serioze dhe i njohur mirë një defekt që çon në mbytje dhe vonesa të tepërta
.

Në Omio e gjithë infrastruktura menaxhohet nga Kubernetes. Të gjitha ngarkesat tona të punës me status dhe pa shtetësi ekzekutohen ekskluzivisht në Kubernetes (ne përdorim Google Kubernetes Engine). Në gjashtë muajt e fundit, ne filluam të vëzhgonim ngadalësime të rastësishme. Aplikacionet ngrijnë ose ndalojnë përgjigjen ndaj kontrolleve shëndetësore, humbasin lidhjen me rrjetin, etj. Kjo sjellje na hutoi për një kohë të gjatë dhe më në fund vendosëm ta merrnim seriozisht problemin.

Përmbledhja e artikullit:

  • Disa fjalë për kontejnerët dhe Kubernetes;
  • Si zbatohen kërkesat dhe limitet e CPU-së;
  • Si funksionon limiti i CPU-së në mjediset me shumë bërthama;
  • Si të gjurmoni mbytjen e CPU;
  • Zgjidhja e problemit dhe nuancat.

Disa fjalë për kontejnerët dhe Kubernetes

Kubernetes është në thelb standardi modern në botën e infrastrukturës. Detyra e tij kryesore është orkestrimi i kontejnerëve.

Konteйnerы

Në të kaluarën, na duhej të krijonim artefakte si Java JAR/WAR, Python Eggs ose ekzekutues për t'u ekzekutuar në serverë. Megjithatë, për t'i bërë ato të funksiononin, duhej bërë punë shtesë: instalimi i mjedisit të funksionimit (Java/Python), vendosja e skedarëve të nevojshëm në vendet e duhura, sigurimi i përputhshmërisë me një version specifik të sistemit operativ, etj. Me fjalë të tjera, vëmendje e kujdesshme duhet t'i kushtohej menaxhimit të konfigurimit (i cili shpesh ishte një burim grindjesh midis zhvilluesve dhe administratorëve të sistemit).

Kontejnerët ndryshuan gjithçka. Tani objekti është një imazh i kontejnerit. Ai mund të përfaqësohet si një lloj skedari i ekzekutueshëm i zgjeruar që përmban jo vetëm programin, por edhe një mjedis ekzekutimi të plotë (Java/Python/...), si dhe skedarët/paketat e nevojshme, të para-instaluar dhe të gatshëm për të vraponi. Kontejnerët mund të vendosen dhe të ekzekutohen në serverë të ndryshëm pa ndonjë hap shtesë.

Përveç kësaj, kontejnerët funksionojnë në mjedisin e tyre të sandboxit. Ata kanë përshtatësin e tyre virtual të rrjetit, sistemin e tyre të skedarëve me akses të kufizuar, hierarkinë e tyre të proceseve, kufizimet e tyre në CPU dhe memorie, etj. E gjithë kjo zbatohet falë një nënsistemi të veçantë të kernelit Linux - hapësirat e emrave.

Kubernetes

Siç u tha më herët, Kubernetes është një orkestrues kontejnerësh. Ajo funksionon kështu: ju i jepni një grup makinerish dhe më pas thoni: "Hej, Kubernetes, le të lëshojmë dhjetë shembuj të kontejnerit tim me 2 procesorë dhe 3 GB memorie secili, dhe t'i mbajmë të funksionojnë!" Kubernetes do të kujdeset për pjesën tjetër. Ai do të gjejë kapacitet të lirë, do të nisë kontejnerët dhe do t'i rifillojë nëse është e nevojshme, do të nxjerrë një përditësim kur ndryshon versionet, etj. Në thelb, Kubernetes ju lejon të abstraktoni komponentin e harduerit dhe bën një shumëllojshmëri të gjerë sistemesh të përshtatshme për vendosjen dhe ekzekutimin e aplikacioneve.

Kufijtë e CPU dhe mbytja agresive në Kubernetes
Kubernetes nga këndvështrimi i laikut

Cilat janë kërkesat dhe kufijtë në Kubernetes

Mirë, ne kemi mbuluar kontejnerët dhe Kubernetes. Ne gjithashtu e dimë se shumë kontejnerë mund të qëndrojnë në të njëjtën makinë.

Një analogji mund të nxirret me një apartament komunal. Merret një ambient i gjerë (makineri/njësi) dhe u jepet me qira disa qiramarrësve (kontenierë). Kubernetes vepron si sekser. Shtrohet pyetja, si t'i ruani qiramarrësit nga konfliktet me njëri-tjetrin? Po sikur njëri prej tyre, le të themi, të vendosë të marrë hua banjën për gjysmën e ditës?

Këtu hyjnë në lojë kërkesat dhe kufizimet. CPU Kërkesa nevojiten vetëm për qëllime planifikimi. Kjo është diçka si një "listë dëshirash" e kontejnerit dhe përdoret për të zgjedhur nyjen më të përshtatshme. Në të njëjtën kohë CPU Limit mund të krahasohet me një marrëveshje qiraje - sapo të zgjedhim një njësi për kontejnerin, nuk mundet shkojnë përtej kufijve të vendosur. Dhe këtu lind problemi...

Si zbatohen kërkesat dhe kufijtë në Kubernetes

Kubernetes përdor një mekanizëm mbytës (duke kapërcyer ciklet e orës) të integruar në kernel për të zbatuar kufijtë e CPU. Nëse një aplikacion e kalon kufirin, aktivizohet frenimi (d.m.th. merr më pak cikle CPU). Kërkesat dhe kufizimet për memorie organizohen ndryshe, kështu që ato janë më të lehta për t'u zbuluar. Për ta bërë këtë, thjesht kontrolloni statusin e fundit të rinisjes së pod: nëse është "OOMKilled". Mbyllja e CPU-së nuk është aq e thjeshtë, pasi K8s i bën metrikat të disponueshme vetëm sipas përdorimit, jo sipas cgrupeve.

Kërkesë CPU

Kufijtë e CPU dhe mbytja agresive në Kubernetes
Si zbatohet kërkesa e CPU-së

Për thjeshtësi, le të shohim procesin duke përdorur një makinë me një CPU me 4 bërthama si shembull.

K8s përdor një mekanizëm të grupit të kontrollit (cgroups) për të kontrolluar alokimin e burimeve (memoria dhe procesori). Një model hierarkik është i disponueshëm për të: fëmija trashëgon kufijtë e grupit prindër. Detajet e shpërndarjes ruhen në një sistem skedarësh virtual (/sys/fs/cgroup). Në rastin e një procesori kjo është /sys/fs/cgroup/cpu,cpuacct/*.

K8s përdor skedarin cpu.share për të ndarë burimet e procesorit. Në rastin tonë, cgroup rrënjë merr 4096 aksione të burimeve të CPU - 100% të fuqisë së disponueshme të procesorit (1 bërthamë = 1024; kjo është një vlerë fikse). Grupi rrënjë shpërndan burimet në mënyrë proporcionale në varësi të aksioneve të pasardhësve të regjistruar në cpu.share, dhe ata nga ana e tyre bëjnë të njëjtën gjë me pasardhësit e tyre etj. Në një nyje tipike Kubernetes, cgrupi rrënjë ka tre fëmijë: system.slice, user.slice и kubepods. Dy nëngrupet e para përdoren për të shpërndarë burimet midis ngarkesave kritike të sistemit dhe programeve të përdoruesve jashtë K8s. I fundit - kubepods — krijuar nga Kubernetes për të shpërndarë burimet midis pods.

Diagrami i mësipërm tregon se nëngrupi i parë dhe i dytë morën secilin 1024 aksione, me nëngrupin kuberpod të alokuar 4096 aksionet Si është e mundur kjo: në fund të fundit, grupi rrënjë ka qasje vetëm në 4096 aksionet, dhe shuma e aksioneve të pasardhësve të saj e tejkalon ndjeshëm këtë numër (6144)? Çështja është se vlera ka kuptim logjik, kështu që planifikuesi Linux (CFS) e përdor atë për të shpërndarë në mënyrë proporcionale burimet e CPU. Në rastin tonë, dy grupet e para marrin 680 aksionet reale (16,6% e 4096), dhe kubepod merr pjesën e mbetur 2736 aksionet Në rast ndërprerjeje, dy grupet e para nuk do të përdorin burimet e alokuara.

Për fat të mirë, planifikuesi ka një mekanizëm për të shmangur humbjen e burimeve të papërdorura të CPU. Ai transferon kapacitetin "boshe" në një grup global, nga i cili shpërndahet tek grupet që kanë nevojë për fuqi shtesë të procesorit (transferimi ndodh në grupe për të shmangur humbjet e rrumbullakosjes). Një metodë e ngjashme zbatohet për të gjithë pasardhësit e pasardhësve.

Ky mekanizëm siguron një shpërndarje të drejtë të fuqisë së procesorit dhe siguron që asnjë proces nuk "vjedh" burimet nga të tjerët.

Kufiri i CPU-së

Përkundër faktit se konfigurimet e kufijve dhe kërkesave në K8 duken të ngjashme, zbatimi i tyre është rrënjësisht i ndryshëm: kjo më mashtruese dhe pjesa më pak e dokumentuar.

K8 angazhohet Mekanizmi i kuotave CFS për të zbatuar kufijtë. Cilësimet e tyre janë të specifikuara në skedarë cfs_period_us и cfs_quota_us në drejtorinë cgroup (skedari ndodhet gjithashtu atje cpu.share).

Ndryshe nga cpu.share, kuota bazohet në Periudha kohore, dhe jo në fuqinë e disponueshme të procesorit. cfs_period_us specifikon kohëzgjatjen e periudhës (epokës) - është gjithmonë 100000 μs (100 ms). Ekziston një opsion për të ndryshuar këtë vlerë në K8, por për momentin është i disponueshëm vetëm në alfa. Planifikuesi përdor epokën për të rifilluar kuotat e përdorura. Skedari i dytë cfs_quota_us, specifikon kohën e disponueshme (kuotën) në çdo epokë. Vini re se është specifikuar gjithashtu në mikrosekonda. Kuota mund të kalojë gjatësinë e epokës; me fjalë të tjera, mund të jetë më i madh se 100 ms.

Le të shohim dy skenarë në makinat me 16 bërthama (lloji më i zakonshëm i kompjuterit që kemi në Omio):

Kufijtë e CPU dhe mbytja agresive në Kubernetes
Skenari 1: 2 fije dhe një kufi 200 ms. Asnjë mbytje

Kufijtë e CPU dhe mbytja agresive në Kubernetes
Skenari 2: 10 fije dhe kufiri 200 ms. Mbyllja fillon pas 20 ms, qasja në burimet e procesorit rifillon pas 80 ms të tjera

Le të themi se keni vendosur kufirin e CPU-së 2 bërthama; Kubernetes do ta përkthejë këtë vlerë në 200 ms. Kjo do të thotë që kontejneri mund të përdorë një maksimum prej 200ms kohë të CPU-së pa mbytje.

Dhe këtu fillon argëtimi. Siç u përmend më lart, kuota e disponueshme është 200 ms. Nëse punoni paralelisht dhjetë fijet në një makinë me 12 bërthama (shih ilustrimin për skenarin 2), ndërkohë që të gjitha nyjet e tjera janë të papunë, kuota do të shterohet në vetëm 20 ms (pasi 10 * 20 ms = 200 ms) dhe të gjitha fijet e këtij pod do të varen » (mbyt) për 80 ms në vijim. E përmendura tashmë defekt në planifikues, për shkak të së cilës ndodh mbytja e tepërt dhe kontejneri nuk mund të përmbushë as kuotën ekzistuese.

Si të vlerësohet mbytja në bishtaja?

Thjesht identifikohuni në pod dhe ekzekutoni cat /sys/fs/cgroup/cpu/cpu.stat.

  • nr_periods — numri i përgjithshëm i periudhave të planifikimit;
  • nr_throttled - numri i periudhave të mbytjes në përbërje nr_periods;
  • throttled_time - koha kumulative e mbytjes në nanosekonda.

Kufijtë e CPU dhe mbytja agresive në Kubernetes

Çfarë po ndodh në të vërtetë?

Si rezultat, marrim mbytje të lartë në të gjitha aplikimet. Ndonjëherë ai është brenda një herë e gjysmë më i fortë se sa llogaritet!

Kjo çon në gabime të ndryshme - dështime të kontrollit të gatishmërisë, ngrirje të kontejnerëve, ndërprerje të lidhjes së rrjetit, ndërprerje kohore brenda thirrjeve të shërbimit. Kjo përfundimisht rezulton në rritje të vonesës dhe shkallë më të lartë gabimi.

Vendimi dhe pasojat

Gjithçka është e thjeshtë këtu. Ne braktisëm kufijtë e CPU-së dhe filluam përditësimin e kernelit të OS në grupe në versionin më të fundit, në të cilin defekti u rregullua. Numri i gabimeve (HTTP 5xx) në shërbimet tona ra menjëherë ndjeshëm:

Gabime HTTP 5xx

Kufijtë e CPU dhe mbytja agresive në Kubernetes
Gabime HTTP 5xx për një shërbim kritik

Koha e përgjigjes p95

Kufijtë e CPU dhe mbytja agresive në Kubernetes
Vonesa e kërkesës kritike të shërbimit, përqindja e 95-të

Kostot operative

Kufijtë e CPU dhe mbytja agresive në Kubernetes
Numri i orëve të rastit të shpenzuara

Cila është kapja?

Siç u tha në fillim të artikullit:

Mund të bëhet një analogji me një apartament komunal... Kubernetes vepron si sekser. Por si t'i ruani qiramarrësit nga konfliktet me njëri-tjetrin? Po sikur njëri prej tyre, le të themi, të vendosë të marrë hua banjën për gjysmën e ditës?

Këtu është kapja. Një enë e pakujdesshme mund të hajë të gjitha burimet e disponueshme të CPU në një makinë. Nëse keni një pirg aplikacionesh inteligjente (për shembull, JVM, Go, Node VM janë konfiguruar siç duhet), atëherë ky nuk është problem: mund të punoni në kushte të tilla për një kohë të gjatë. Por nëse aplikacionet janë të optimizuara dobët ose nuk janë fare të optimizuara (FROM java:latest), situata mund të dalë jashtë kontrollit. Në Omio ne kemi Dockerfiles bazë të automatizuar me cilësime adekuate të paracaktuara për grupin kryesor të gjuhës, kështu që ky problem nuk ekzistonte.

Ne rekomandojmë monitorimin e metrikës PËRDORIMI (përdorimi, ngopja dhe gabimet), vonesat e API dhe normat e gabimit. Sigurohuni që rezultatet të përmbushin pritshmëritë.

Referencat

Kjo është historia jonë. Materialet e mëposhtme ndihmuan shumë për të kuptuar se çfarë po ndodhte:

Raportet e gabimeve të Kubernetes:

A keni hasur probleme të ngjashme në praktikën tuaj apo keni përvojë në lidhje me mbytjen në mjedise prodhimi me kontejnerë? Ndani historinë tuaj në komente!

PS nga përkthyesi

Lexoni edhe në blogun tonë:

Burimi: www.habr.com

Shto një koment