CPU apribojimai ir agresyvus Kubernetes droselis

Pastaba. vert.: Ši akis atverianti „Omio“ – Europos kelionių agregato – istorija nukelia skaitytojus nuo pagrindinės teorijos iki patrauklių praktinių „Kubernetes“ konfigūracijos subtilybių. Susipažinimas su tokiais atvejais padeda ne tik praplėsti akiratį, bet ir užkirsti kelią nereikšmingoms problemoms.

CPU apribojimai ir agresyvus Kubernetes droselis

Ar jums kada nors užstrigo programa, nustojote reaguoti į sveikatos patikrinimus ir negalėjote suprasti, kodėl? Vienas iš galimų paaiškinimų yra susijęs su procesoriaus išteklių kvotos apribojimais. Apie tai ir kalbėsime šiame straipsnyje.

Lt; DR:
Primygtinai rekomenduojame išjungti procesoriaus apribojimus Kubernetes (arba išjungti CFS kvotas Kubelet), jei naudojate Linux branduolio versiją su CFS kvotos klaida. Šerdyje yra rimtas ir garsus klaida, dėl kurios per daug stabdoma ir vėluojama
.

Omio mieste visą infrastruktūrą valdo Kubernetes. Visi mūsų būsenos ir be būsenos darbo krūviai veikia tik „Kubernetes“ (naudojame „Google Kubernetes Engine“). Per pastaruosius šešis mėnesius pradėjome stebėti atsitiktinius sulėtėjimus. Programos užstringa arba nustoja reaguoti į sveikatos patikrinimus, praranda ryšį su tinklu ir pan. Toks elgesys mus ilgai glumino, ir galiausiai nusprendėme rimtai žiūrėti į problemą.

Straipsnio santrauka:

  • Keletas žodžių apie konteinerius ir Kubernetes;
  • Kaip įgyvendinamos CPU užklausos ir limitai;
  • Kaip CPU limitas veikia kelių branduolių aplinkoje;
  • Kaip sekti procesoriaus droselį;
  • Problemos sprendimas ir niuansai.

Keletas žodžių apie konteinerius ir Kubernetes

„Kubernetes“ iš esmės yra modernus standartas infrastruktūros pasaulyje. Pagrindinė jo užduotis – konteinerių orkestravimas.

Konteineriai

Anksčiau mes turėjome kurti artefaktus, tokius kaip Java JAR / WAR, Python Eggs arba vykdomuosius failus, kad jie veiktų serveriuose. Tačiau, kad jie veiktų, reikėjo atlikti papildomus darbus: įdiegti runtime aplinką (Java/Python), patalpinti reikiamus failus į reikiamas vietas, užtikrinti suderinamumą su konkrečia operacinės sistemos versija ir kt. Kitaip tariant, reikėjo daug dėmesio skirti konfigūracijos valdymui (dėl to dažnai kildavo nesutarimų tarp kūrėjų ir sistemos administratorių).

Konteineriai viską pakeitė. Dabar artefaktas yra konteinerio vaizdas. Jis gali būti pavaizduotas kaip išplėstinis vykdomasis failas, kuriame yra ne tik programa, bet ir visavertė vykdymo aplinka (Java/Python/...), taip pat reikalingi failai/paketai, iš anksto įdiegti ir paruošti darbui. paleisti. Konteinerius galima įdiegti ir paleisti skirtinguose serveriuose be jokių papildomų veiksmų.

Be to, konteineriai veikia savo smėlio dėžės aplinkoje. Jie turi savo virtualų tinklo adapterį, savo failų sistemą su ribota prieiga, savo procesų hierarchiją, savo procesoriaus ir atminties apribojimus ir tt Visa tai įgyvendinama specialios Linux branduolio posistemės – vardų erdvių dėka.

Kubernetes

Kaip minėta anksčiau, „Kubernetes“ yra konteinerių orkestrantas. Tai veikia taip: jūs suteikiate jai mašinų telkinį ir sakote: „Ei, Kubernetes, paleiskime dešimt mano konteinerio egzempliorių su 2 procesoriais ir 3 GB atminties kiekviename ir tęsime juos veikti!“ Visa kita pasirūpins Kubernetes. Jis suras laisvą talpą, paleis konteinerius ir prireikus juos paleis iš naujo, pakeis naujinimą ir pan. Iš esmės „Kubernetes“ leidžia abstrahuoti aparatūros komponentą ir leidžia įvairioms sistemoms diegti ir paleisti programas.

CPU apribojimai ir agresyvus Kubernetes droselis
Kubernetes pasauliečio požiūriu

Kas yra Kubernetes užklausos ir apribojimai

Gerai, mes uždengėme konteinerius ir „Kubernetes“. Taip pat žinome, kad vienoje mašinoje gali būti keli konteineriai.

Galima padaryti analogiją su komunaliniu butu. Paimamos erdvios patalpos (mašinos/blokai) ir išnuomojamos keliems nuomininkams (konteineriams). „Kubernetes“ veikia kaip maklerio pareigas. Kyla klausimas, kaip apsaugoti nuomininkus nuo konfliktų tarpusavyje? O jei vienas iš jų, tarkime, nuspręstų pasiskolinti vonios kambarį pusei dienos?

Čia atsiranda prašymai ir apribojimai. CPU Prašymas reikalingas tik planavimo tikslais. Tai yra kažkas panašaus į konteinerio „norų sąrašą“ ir jis naudojamas tinkamiausiam mazgui pasirinkti. Tuo pačiu metu CPU Apriboti galima palyginti su nuomos sutartimi – kai tik pasirenkame konteinerio vienetą, negali peržengti nustatytas ribas. Ir čia iškyla problema...

Kaip užklausos ir apribojimai įgyvendinami „Kubernetes“.

„Kubernetes“ naudoja branduolyje įmontuotą droselio mechanizmą (praleidžiant laikrodžio ciklus), kad įgyvendintų procesoriaus apribojimus. Jei programa viršija ribą, įjungiamas droselis (t. y. ji gauna mažiau procesoriaus ciklų). Atminties užklausos ir apribojimai organizuojami skirtingai, todėl juos lengviau aptikti. Norėdami tai padaryti, tiesiog patikrinkite paskutinio bloko paleidimo iš naujo būseną: ar ji „OOMKilled“. CPU reguliavimas nėra toks paprastas, nes K8s metriką pateikia tik pagal naudojimą, o ne pagal cgrupes.

CPU užklausa

CPU apribojimai ir agresyvus Kubernetes droselis
Kaip įgyvendinama CPU užklausa

Paprastumo dėlei pažiūrėkime į procesą, kaip pavyzdį naudodami mašiną su 4 branduolių CPU.

K8s naudoja valdymo grupės mechanizmą (cgroups), kad galėtų kontroliuoti išteklių (atminties ir procesoriaus) paskirstymą. Jai galimas hierarchinis modelis: vaikas paveldi pirminės grupės ribas. Informacija apie platinimą yra saugoma virtualioje failų sistemoje (/sys/fs/cgroup). Procesoriaus atveju tai yra /sys/fs/cgroup/cpu,cpuacct/*.

K8s naudoja failą cpu.share paskirstyti procesoriaus išteklius. Mūsų atveju šakninė cgrupė gauna 4096 CPU resursų dalis – 100% turimos procesoriaus galios (1 branduolys = 1024; tai fiksuota reikšmė). Šakninė grupė paskirsto išteklius proporcingai priklausomai nuo registruotų palikuonių dalių cpu.share, o jie savo ruožtu tą patį daro su savo palikuonimis ir pan. Tipiškame Kubernetes mazge šakninė cgrupė turi tris vaikus: system.slice, user.slice и kubepods. Pirmieji du pogrupiai naudojami paskirstyti išteklius tarp kritinių sistemos apkrovų ir vartotojo programų už K8 ribų. Paskutinis - kubepods - sukūrė Kubernetes, kad paskirstytų išteklius tarp ankščių.

Aukščiau pateiktoje diagramoje parodyta, kad pirmasis ir antrasis pogrupiai gavo kiekvieną 1024 akcijų, paskirstant kuberpod pogrupį 4096 akcijų Kaip tai įmanoma: juk šakninė grupė turi prieigą tik prie 4096 akcijų, o jos palikuonių akcijų suma gerokai viršija šį skaičių (6144)? Esmė ta, kad reikšmė turi logišką prasmę, todėl Linux planuotojas (CFS) naudoja ją proporcingai paskirstydamas procesoriaus išteklius. Mūsų atveju gauna pirmos dvi grupės 680 realių akcijų (16,6 proc. iš 4096), o likusias atitenka kubepod 2736 akcijų Prastovos atveju pirmosios dvi grupės nenaudos skirtų išteklių.

Laimei, planuotojas turi mechanizmą, leidžiantį išvengti nepanaudotų procesoriaus išteklių švaistymo. Jis perkelia „neaktyvią“ talpą į pasaulinį telkinį, iš kurio paskirstomas grupėms, kurioms reikia papildomos procesoriaus galios (perdavimas vyksta partijomis, kad būtų išvengta apvalinimo nuostolių). Panašus metodas taikomas visiems palikuonių palikuonims.

Šis mechanizmas užtikrina teisingą procesoriaus galios paskirstymą ir užtikrina, kad joks procesas „nepavogs“ išteklių iš kitų.

CPU limitas

Nepaisant to, kad K8s apribojimų ir užklausų konfigūracijos atrodo panašios, jų įgyvendinimas kardinaliai skiriasi: tai labiausiai klaidinanti ir mažiausiai dokumentuota dalis.

K8s užsiima CFS kvotos mechanizmas įgyvendinti ribas. Jų nustatymai nurodyti failuose cfs_period_us и cfs_quota_us cgroup kataloge (failas taip pat yra ten cpu.share).

Skirtingai nuo cpu.share, kvota yra pagrįsta laiko tarpas, o ne esant turimai procesoriaus galiai. cfs_period_us nurodo periodo (epochos) trukmę – ji visada yra 100000 μs (100 ms). Yra galimybė pakeisti šią reikšmę K8s, tačiau kol kas ji pasiekiama tik alfa versijoje. Planavimo priemonė naudoja epochą, kad iš naujo paleistų panaudotas kvotas. Antrasis failas cfs_quota_us, nurodo galimą laiką (kvotą) kiekvienoje epochoje. Atkreipkite dėmesį, kad jis taip pat nurodomas mikrosekundėmis. Kvota gali viršyti epochos ilgį; kitaip tariant, jis gali būti didesnis nei 100 ms.

Pažvelkime į du scenarijus 16 branduolių kompiuteriuose (labiausiai paplitęs kompiuteris, kurį turime Omio):

CPU apribojimai ir agresyvus Kubernetes droselis
1 scenarijus: 2 gijos ir 200 ms riba. Jokio droselio

CPU apribojimai ir agresyvus Kubernetes droselis
2 scenarijus: 10 gijų ir 200 ms riba. Droselis prasideda po 20 ms, prieiga prie procesoriaus išteklių atnaujinama dar po 80 ms

Tarkime, kad nustatėte procesoriaus limitą 2 branduoliai; „Kubernetes“ išvers šią reikšmę į 200 ms. Tai reiškia, kad konteineris gali naudoti daugiausia 200 ms procesoriaus laiko be droselio.

Ir čia prasideda linksmybės. Kaip minėta pirmiau, turima kvota yra 200 ms. Jei dirbate lygiagrečiai dešimt gijos 12 branduolių įrenginyje (žr. 2 scenarijaus iliustraciją), kol visi kiti blokai neveikia, kvota bus išnaudota vos per 20 ms (nes 10 * 20 ms = 200 ms), o visos šio podelio gijos kabės » (droselis) kitas 80 ms. Jau minėtas planavimo klaida, dėl ko įvyksta per didelis droselis ir konteineris net negali įvykdyti esamos kvotos.

Kaip įvertinti droselį ankštyse?

Tiesiog prisijunkite prie pod ir vykdykite cat /sys/fs/cgroup/cpu/cpu.stat.

  • nr_periods — bendras planavimo laikotarpių skaičius;
  • nr_throttled — trumpų kompozicijos laikotarpių skaičius nr_periods;
  • throttled_time — kaupiamasis droselio laikas nanosekundėmis.

CPU apribojimai ir agresyvus Kubernetes droselis

Kas iš tikrųjų vyksta?

Dėl to visose programose gauname didelį droselį. Kartais jis būna pusantro karto stipresnis nei apskaičiuotas!

Dėl to atsiranda įvairių klaidų – pasirengimo tikrinimo gedimų, konteinerių užšalimo, tinklo ryšio nutrūkimų, serviso iškvietimų pertraukų. Tai galiausiai padidina delsą ir didesnį klaidų lygį.

Sprendimas ir pasekmės

Čia viskas paprasta. Atsisakėme procesoriaus apribojimų ir pradėjome atnaujinti OS branduolį grupėse į naujausią versiją, kurioje klaida buvo ištaisyta. Klaidų (HTTP 5xx) skaičius mūsų paslaugose iškart smarkiai sumažėjo:

HTTP 5xx klaidos

CPU apribojimai ir agresyvus Kubernetes droselis
HTTP 5xx klaidos vienai svarbiai paslaugai

Atsakymo laikas p95

CPU apribojimai ir agresyvus Kubernetes droselis
Kritinės paslaugos užklausos delsa, 95 procentilis

Eksploatacinės išlaidos

CPU apribojimai ir agresyvus Kubernetes droselis
Išleistų egzempliorių valandų skaičius

Kas yra sugautas?

Kaip rašoma straipsnio pradžioje:

Galima vesti analogiją su komunaliniu butu... Kubernetes atlieka maklerio pareigas. Tačiau kaip apsaugoti nuomininkus nuo konfliktų tarpusavyje? O jei vienas iš jų, tarkime, nuspręstų pasiskolinti vonios kambarį pusei dienos?

Štai ir laimikis. Vienas neatsargus konteineris gali suvalgyti visus turimus mašinos procesoriaus išteklius. Jei turite išmaniųjų programų krūvą (pavyzdžiui, JVM, Go, Node VM yra tinkamai sukonfigūruoti), tai nėra problema: tokiomis sąlygomis galite dirbti ilgą laiką. Bet jei programos yra blogai optimizuotos arba visai neoptimizuotos (FROM java:latest), padėtis gali tapti nekontroliuojama. „Omio“ turime automatizuotus bazinius „Dockerfiles“ su tinkamais numatytais pagrindiniais kalbų rinkiniais, todėl šios problemos nebuvo.

Rekomenduojame stebėti metrikas NAUDOJIMAS (naudojimas, prisotinimas ir klaidos), API vėlavimai ir klaidų dažnis. Užtikrinkite, kad rezultatai atitiktų lūkesčius.

Nuorodos

Tai mūsų istorija. Šios medžiagos labai padėjo suprasti, kas vyksta:

„Kubernetes“ klaidų ataskaitos:

Ar savo praktikoje susidūrėte su panašiomis problemomis arba turite patirties, susijusios su droseliu konteinerinėse gamybos aplinkose? Pasidalinkite savo istorija komentaruose!

PS iš vertėjo

Taip pat skaitykite mūsų tinklaraštyje:

Šaltinis: www.habr.com

Добавить комментарий