MĂ€rkus tĂ”lke kohta.: see this instructive story of Omio â a European travel aggregator â guides readers from basic theory to fascinating practical nuances in Kubernetes configuration. Familiarity with such cases helps not only to broaden horizons but also to prevent non-trivial problems.

Have you ever faced the issue where an application got 'stuck', stopped responding to health check requests, and you couldnât understand the reason for its behavior? One possible explanation is related to CPU resource quota limits. That will be the topic of this article.
TL;DR:
We strongly recommend abandoning CPU limits in Kubernetes (or disabling CFS quotas in Kubelet) if using a Linux kernel version with a CFS quota bug. In the kernel there is a serious and bug that leads to excessive throttling and delays.
At Omio, the entire infrastructure is managed by Kubernetes.All our stateful and stateless workloads operate exclusively on Kubernetes (we use Google Kubernetes Engine). Over the past six months, we have begun to observe random lags. Applications hang or stop responding to health checks, lose network connectivity, etc. Such behavior long left us puzzled, and finally, we decided to tackle the problem head-on.
Summary of the article:
- A few words about containers and Kubernetes;
- How CPU requests and limits are implemented;
- How CPU limit works in multi-core environments;
- How to monitor CPU throttling;
- Solution to the problem and nuances.
A few words about containers and Kubernetes
Kubernetes, in essence, is the modern standard in the world of infrastructure. Its primary task is the orchestration of containers.
Konteinerid
In the past, we had to create artifacts like Java JARs/WARs, Python Eggs, or executables for subsequent launch on servers. However, to make them operate, extra work was needed: setting up a runtime environment (Java/Python), placing the necessary files in the right locations, ensuring compatibility with a specific version of the operating system, etc. In other words, great attention had to be paid to configuration management (which often caused friction between developers and system administrators).
Containers changed everything. NĂŒĂŒd on artefaktiks konteineripilt. Seda vĂ”ib kujutada kui laienenud kĂ€ivitusfaili, mis sisaldab mitte ainult programmi, vaid ka tĂ€ielikku kĂ€ituskeskkonda (Java/Python/...); samuti vajalikke faile/pakette, mis on eelnevalt installitud ja kĂ€ivitamiseks valmis. Konteinerit saab juurutada ja kĂ€ivitada erinevatel serveritel ilma tĂ€iendavate toiminguteta.
Lisaks töötavad konteinerid omaette keskkonnas. Neil on oma virtuaalne vĂ”rgukaart, oma failisĂŒsteem piiratud juurdepÀÀsuga, oma protsessihierarhia, oma piirangud CPU ja mĂ€lu osas jne. KĂ”ik see on ellu viidud tĂ€nu Linuxi tuuma erilise alamsĂŒsteemi â nimealad (namespaces) â kaudu.
Kubernetes
Kuidas varem mainitud, on Kubernetes konteinerite orkestrator. See töötab jĂ€rgmiselt: te annate sellele hulk masinaid ja ĂŒtlete: âHei, Kubernetes, kĂ€ivita kĂŒmme minu konteinerisikapti 2 protsessoriga ja 3 GB mĂ€luga igaĂŒhele ning hoia neid töökorras!â. Kubernetes hoolitseb ĂŒlejÀÀnud eest. See leiab vabad ressursid, kĂ€ivitab konteinerid ja vajadusel taaskĂ€ivitab need, viib lĂ€bi uuendusi versioonide vahetumisel jne. Essentsiaalselt vĂ”imaldab Kubernetes abstraheerida riistvara komponendist ja muudab kĂ”ik erinevad sĂŒsteemid rakenduste juurutamiseks ja töötamiseks sobivaks.

Kubernetes tavainimese vaatepunktist
Mis on requestâid ja limitâid Kuberneteses
Okei, oleme aru saanud konteineritest ja Kubernetesest. Teame ka, et mitmed konteinerid vĂ”ivad olla ĂŒhel masinal.
Seda saab vĂ”rrelda kommunaalkorteriga. VĂ”etakse avar ruum (masinad/sĂ”lmed) ja antakse mitmele ĂŒĂŒrnikule (konteineritele) ĂŒĂŒrile. Kubernetes mĂ€ngib kinnisvaramaakleri rolli. TĂ”ukub kĂŒsimus, kuidas hoiduda ĂŒĂŒrnike konfliktidest omavahel? Mis juhtub, kui ĂŒks neist soovib nĂ€iteks vannituba pooleks pĂ€evaks enda valdusesse vĂ”tta?
Siin astuvad mĂ€ngu requestâid ja limitâid. CPU PĂ€ring on vajalik ainult planeerimiseks. See on midagi nagu konteineri âsoovide nimekiriâ, mida kasutatakse sobivaima sĂ”lme leidmiseks. Samas vĂ”ib CPU Piirang vĂ”rrelda ĂŒĂŒrilepinguga â kohe kui oleme leidnud sĂ”lme konteinerile, siis ei saa ĂŒletada kehtestatud piire. Ja siin tekib probleem...
Kuidas on Kubernetesis rakendatud request'e ja limit'e
Kubernetes kasutab CPU limit'ide rakendamiseks tuumikus sisseehitatud throttling'u mehhanismi. Kui rakendus ĂŒletab limiidi, aktiveeritakse throttling (ehk see saab vĂ€hem CPU tsĂŒkleid). MĂ€lu request'id ja limit'id on korraldatud teisiti, seega on neid lihtsam tuvastada. Selleks piisab, kui kontrollida pod'i viimast taaskĂ€ivitamise olekut: ei ole see «OOMKilled». CPU throttlinguga ei ole kĂ”ik nii lihtne, kuna K8s teeb kĂ€tte ainult kasutamise metrikate, mitte cgroups'i pĂ”hjal.
CPU Request

Kuidas on rakendatud CPU request
Kerguse huvides vaatame protsessi nÀitel, kus on 4-tuuma CPU.
K8s kasutab ressursside (mĂ€lu ja CPU) jaotamise juhtimiseks cgroups'i mehhanismi. Sellel on hierarhiline mudel: alamhari pĂ€rib vanema grupi limit'id. Jagamise ĂŒksikasjad salvestatakse virtuaalsesse failisĂŒsteemi (/sys/fs/cgroup). Protsessori puhul on see /sys/fs/cgroup/cpu,cpuacct/*.
K8s kasutab faili cpu.share protsessori ressursside jaotamiseks. Meie juhus moraali jĂ€rgi, juurt kontrollrĂŒhm saab 4096 ressursi CPU osa â 100% kĂ€tte saadava CPU vĂ”imsusest (1 sĂŒdamik = 1024; see on fikseeritud vÀÀrtus). Juurt rĂŒhm jaotab ressursse proportsionaalselt vastavalt alamrĂŒhmade osadele, mis on mÀÀratud cpu.share, ja need omakorda kĂ€ituvad oma jĂ€rglastega sarnaselt jne. TĂŒĂŒpilises Kubernetes'i sĂ”lmes on juurrĂŒhmadel kolm jĂ€rglast: system.slice, user.slice ja kubepods. Kaks esimest alamhulka kasutatakse ressursside jaotamiseks kriitiliste sĂŒsteemikoormuste ja kasutajaprogrammide vahel vĂ€ljaspool K8s. Viimane â kubepods â luuakse Kubernetes'i poolt, et jaotada ressursse pod'ide vahel.
Ălaltoodud skeemilt on nĂ€ha, et esimesed ja teised alamhulgad on saanud kumbki 1024 osa, samas kui kubepod'ile on mÀÀratud 4096 osasid. Kuidas on see vĂ”imalik: juurrĂŒhmale on ju kĂ€tte saadavad vaid 4096 osi, kuid selle jĂ€rglaste osade summa ĂŒletab seda arvu mĂ€rgatavalt (6144)? Asja on selles, et vÀÀrtus on loogiliselt mĂ”tteline, seega kasutab Linux'i planeerija (CFS) seda CPU ressursside proportsionaalseks jaotamiseks. Meie juhul saavad esimesed kaks grupi kummagi 680 reaalset osa (16,6% 4096-st), samas kui kubepod saab ĂŒlejÀÀnud 2736 osasid. Ooteseisus ei kasuta kaks esimest gruppi mÀÀratud ressursse.
Ănneks on planeerijas mehhanism, mis vĂ”imaldab vĂ€ltida kasutamata CPU ressursside kaotamist. See suunab "seisvaid" vĂ”imsusi globaalsetesse fondidesse, kust neid jaotatakse gruppidele, mis vajavad tĂ€iendavaid protsessori vĂ”imsusi (edastus toimub partiidena, et vĂ€ltida ĂŒmardamisest tingitud kaotusi). Sarnast meetodit rakendatakse ka kĂ”ikidele jĂ€reltulijatele.
See mehhanism tagab Ă”iglaselt protsessori vĂ”imsuste jaotuse ja jĂ€lgib, et ĂŒkski protsess ei "varastaks" ressursse teistelt.
CPU Limiteerimine
Kuigi K8s-i limitide ja requestide konfiguratsioonid nÀevad vÀlja sarnased, on nende rakendamine kardinaalselt erinev: see on kÔige eksitavam ja kÔige vÀhem dokumenteeritud osa.
K8s rakendab limiitide rakendamiseks. Nende seaded mÀÀratakse failidesse cfs_period_us ja cfs_quota_us cgroup kataloogis (seal asub ka fail cpu.share).
Erinevalt cpu.share, kvota pĂ”hineb aja perioodil, mitte saadaval oleva protsessori vĂ”imsusel. cfs_period_us mÀÀrab perioodi (ajastu) kestuse - see on alati 100000 mikrosekundit (100 ms). K8s-il on vĂ”imalus seda vÀÀrtust muuta, kuid see on hetkel saadaval vaid alfa-versioonis. Planeerija kasutab ajastu lĂ”ppemise aega kasutatud kvotade uuesti kĂ€ivitamiseks. Teine fail, cfs_quota_us, mÀÀrab iga ajastu saadaval oleva aja (kvota). Pange tĂ€hele, et see on samuti mÀÀratud mikrosekundites. Kvota vĂ”ib olla pikem kui ajastu kestus; teisisĂ”nu, see vĂ”ib ĂŒletada 100 ms.
Vaadakem kahte stsenaariumi 16- tuumaga masinatel (kĂ”ige levinum arvutitĂŒĂŒp meie Omios):

Stsenaarium 1: 2 voogu ja 200 ms limiit. Ilma trottlemiseta

Stsenaarium 2: 10 voogu ja 200 ms limiit. Trottlemine algab pÀrast 20 ms, juurdepÀÀs protsessori ressurssidele taastub veel 80 ms pÀrast
Oletame, et olete seadistanud CPU limiidi 2 tuumadele; Kubernetes tÔlgendab seda vÀÀrtust kui 200 ms. See tÀhendab, et konteiner vÔib kasutada maksimaalselt 200 ms protsessoriaega ilma trottlemiseta.
Ja siin hakkavad asjad huvitavaks minema. Nagu öeldud, on saadaval kvota 200 ms. Kui teil on samal ajal töötamas kĂŒmme voogu 12-tuumaga masinas (vt stsenaariumi 2 joonist), siis kui kĂ”ik muud pod'id seisavad, kahaneb kvota vaid 20 ms pĂ€rast (kuna 10 * 20 ms = 200 ms), ja kĂ”ik selle pod'i vood "peatatakse" (throttle) jĂ€rgmise 80 ms. Olukorda halvendab juba mainitud , mille tĂ”ttu tekib liigset trottlingut ja konteiner ei suuda isegi olemasolevat kvooti kasutada.
Kuidas hinnata trottlingut pod'ides?
Lihtsalt siseneda pod'isse ja kÀivitada cat /sys/fs/cgroup/cpu/cpu.stat.
-
nr_periodsâ planeerija koguperioodide arv; -
nr_throttledâ trottlingperioodide arv,nr_periods; -
throttled_timeâ kogutud trottlinguaeg nanosekundites.

Mis tegelikult toimub?
KokkuvĂ”ttes saame kĂ”ikides rakendustes kĂ”rge trottlingu. MĂ”nikord on see ĂŒhe ja poole korra tugevam kui arvutatud!
See toob kaasa erinevaid vigu â valmisoleku kontrollide (readiness) tĂ”rkeid, konteinerite seisakuid, vĂ”rguĂŒhenduste katkestusi, teenusekĂ”nede aegumisi. LĂ”ppkokkuvĂ”ttes kajastub see suurenenud viivituses ja vigade arvu tĂ”usus.
Lahendus ja tagajÀrjed
Siin on kÔik lihtne. Me loobusime CPU piirangutest ja tegelesime OS-i tuuma vÀrskendamisega klastrites vÀrskesse versiooni, kus viga oli parandatud. Vigade (HTTP 5xx) arv meie teenustes langes kohe oluliselt:
HTTP 5xx vead

Ăhe kriitilise teenuse HTTP 5xx vead
p95 vastamisaeg

Kriitilise teenuse pÀringute viivitus, 95. protsentil
Ekspluatatsioonikulud

Kulutatud eksemplarituurid
Mis on petukÀik?
Nagu artikli alguses öeldi:
Seda saab vĂ”rrelda ĂŒhiselt kasutatava korteriga... Kubernetes mĂ€ngib kinnisvaramaakleri rolli. Aga kuidas hoida ĂŒĂŒrnikud vĂ€ltimast konflikte omavahel? Mis siis, kui keegi neist otsustab nĂ€iteks hĂ”ivata vannituba pooleks pĂ€evaks?
Siin peitubki trik. Ăks lohakas konteiner vĂ”ib kĂ”rvaldada kĂ”ik saadaolevad protsessori ressursid masinas. Kui teil on korralik rakenduste virn (nt korralikult seadistatud JVM, Go, Node VM), siis pole see probleem: sellistes tingimustes saab töötada pikka aega. Kuid kui rakendused on halvasti optimeeritud vĂ”i ĂŒldse mitte optimeeritud (FROM java:latest), vĂ”ib situatsioon minna kontrolli alt vĂ€lja. Omios on meil automatiseeritud pĂ”hised Dockerfile'id, millel on mĂ”istlikud vaikeseaded peamiste keelte virna jaoks, seetĂ”ttu ei olnud sellist probleemi.
Soovitame jĂ€lgida mÔÔdikuid (kasutamine, kĂŒllastus ja vead), API viivituste ja vigade sageduse osas. Veenduge, et tulemused vastavad ootustele.
Viidatud lingid
See on meie lugu. JÀrgmised materjalid aitasid oluliselt mÔista, mis toimub:
- ;
- ;
- ;
- ;
- â otsige âcpu throttlingâ.
Kubernetes'i viga aruanded:
- ;
- ;
- .
Kas olete kogenud sarnaseid probleeme vÔi on teil kogemusi, mis on seotud konteineriseeritud tootmis keskkondade trottimisega? Jagage oma lugu kommentaarides!
P.S. tÔlkijalt
Lugege ka meie blogist:
- «»;
- «»;
- «».
Allikas: habr.com
