CPU-limiidid ja agressiivne throttling Kubernetes'is

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.

CPU-limiidid ja agressiivne throttling Kubernetes'is

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 on saadaval there is a serious and well-known 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.

CPU-limiidid ja agressiivne throttling Kubernetes'is
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

CPU-limiidid ja agressiivne throttling Kubernetes'is
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 CFS kvota mehhanismi 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):

CPU-limiidid ja agressiivne throttling Kubernetes'is
Stsenaarium 1: 2 voogu ja 200 ms limiit. Ilma trottlemiseta

CPU-limiidid ja agressiivne throttling Kubernetes'is
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 planeerija viga, 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.

CPU-limiidid ja agressiivne throttling Kubernetes'is

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

CPU-limiidid ja agressiivne throttling Kubernetes'is
Ühe kriitilise teenuse HTTP 5xx vead

p95 vastamisaeg

CPU-limiidid ja agressiivne throttling Kubernetes'is
Kriitilise teenuse pÀringute viivitus, 95. protsentil

Ekspluatatsioonikulud

CPU-limiidid ja agressiivne throttling Kubernetes'is
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 USE (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:

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

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster