Limity CPU a agresívne obmedzovanie v Kubernetes

Poznámka. preklad.: Táto pútavá história Omio – európskeho cestovateľského agregátora – prevedie čitateľov od základnej teórie k fascinujúcim praktickým zložitostiam konfigurácie Kubernetes. Znalosť takýchto prípadov pomáha nielen rozširovať obzory, ale aj predchádzať netriviálnym problémom.

Limity CPU a agresívne obmedzovanie v Kubernetes

Zažili ste už niekedy, že sa aplikácia zasekne na mieste, prestane reagovať na zdravotné kontroly a neviete prísť na to, prečo? Jedno z možných vysvetlení súvisí s limitmi kvóty prostriedkov CPU. To je to, o čom budeme hovoriť v tomto článku.

TL; DR:
Dôrazne odporúčame vypnúť limity CPU v Kubernetes (alebo vypnúť kvóty CFS v Kubelet), ak používate verziu linuxového jadra s chybou kvóty CFS. V jadre tam je vážne a dobre známy chyba, ktorá vedie k nadmernému škrteniu a oneskoreniam
.

V Omio celú infraštruktúru spravuje Kubernetes. Všetky naše stavové a bezstavové úlohy bežia výhradne na Kubernetes (používame Google Kubernetes Engine). V posledných šiestich mesiacoch sme začali pozorovať náhodné spomalenia. Aplikácie zamrznú alebo prestanú reagovať na zdravotné kontroly, stratia pripojenie k sieti atď. Toto správanie nás dlho mátlo a nakoniec sme sa rozhodli brať problém vážne.

Zhrnutie článku:

  • Pár slov o kontajneroch a Kubernetes;
  • Ako sú implementované požiadavky a limity CPU;
  • Ako funguje limit CPU vo viacjadrových prostrediach;
  • Ako sledovať obmedzovanie CPU;
  • Riešenie problému a nuansy.

Pár slov o kontajneroch a Kubernetes

Kubernetes je v podstate moderný štandard vo svete infraštruktúry. Jeho hlavnou úlohou je orchestrácia kontajnerov.

kontajnery

V minulosti sme museli vytvárať artefakty ako Java JAR/WAR, Python Eggs alebo spustiteľné súbory, ktoré sa spúšťali na serveroch. Aby však fungovali, bolo potrebné vykonať ďalšie práce: nainštalovať runtime prostredie (Java/Python), umiestniť potrebné súbory na správne miesta, zabezpečiť kompatibilitu s konkrétnou verziou operačného systému atď. Inými slovami, riadeniu konfigurácie (ktorá bola často zdrojom sporov medzi vývojármi a systémovými administrátormi) sa musela venovať veľká pozornosť.

Kontajnery zmenili všetko. Teraz je artefaktom obrázok kontajnera. Môže byť reprezentovaný ako akýsi rozšírený spustiteľný súbor obsahujúci nielen program, ale aj plnohodnotné spúšťacie prostredie (Java/Python/...), ako aj potrebné súbory/balíky, predinštalované a pripravené na behať. Kontajnery je možné nasadiť a spustiť na rôznych serveroch bez akýchkoľvek ďalších krokov.

Okrem toho kontajnery fungujú vo vlastnom prostredí sandboxu. Majú vlastný virtuálny sieťový adaptér, vlastný súborový systém s obmedzeným prístupom, vlastnú hierarchiu procesov, vlastné obmedzenia na CPU a pamäť atď. To všetko je implementované vďaka špeciálnemu subsystému linuxového jadra - namespaces.

Kubernetes

Ako už bolo uvedené, Kubernetes je kontajnerový orchestrátor. Funguje to takto: dáte mu skupinu počítačov a potom poviete: „Ahoj, Kubernetes, spustíme desať inštancií môjho kontajnera s 2 procesormi a 3 GB pamäte a necháme ich v chode!“ O zvyšok sa postará Kubernetes. Nájde voľnú kapacitu, spustí kontajnery a v prípade potreby ich reštartuje, spustí aktualizáciu pri zmene verzií atď. Kubernetes vám v podstate umožňuje abstrahovať hardvérový komponent a robí širokú škálu systémov vhodných na nasadenie a spustenie aplikácií.

Limity CPU a agresívne obmedzovanie v Kubernetes
Kubernetes z pohľadu laika

Čo sú požiadavky a limity v Kubernetes

Dobre, zakryli sme kontajnery a Kubernetes. Vieme tiež, že na tom istom stroji môže byť umiestnených viacero kontajnerov.

Analógiu možno nakresliť so spoločným bytom. Priestranné priestory (stroje/jednotky) sa berú a prenajímajú viacerým nájomcom (kontajnery). Kubernetes pôsobí ako realitná maklérka. Vynára sa otázka, ako udržať nájomníkov pred vzájomnými konfliktmi? Čo ak sa jeden z nich, povedzme, rozhodne požičať si kúpeľňu na pol dňa?

Tu prichádzajú do úvahy požiadavky a limity. CPU žiadosť potrebné výlučne na účely plánovania. Je to niečo ako „zoznam želaní“ kontajnera a používa sa na výber najvhodnejšieho uzla. Zároveň CPU limit možno prirovnať k nájomnej zmluve - akonáhle vyberieme jednotku pre kontajner, nemôže prekračovať stanovené hranice. A tu nastáva problém...

Ako sa implementujú požiadavky a limity v Kubernetes

Kubernetes používa škrtiaci mechanizmus (preskakovanie hodinových cyklov) zabudovaný do jadra na implementáciu limitov CPU. Ak aplikácia prekročí limit, bude povolené obmedzenie (t. j. dostane menej cyklov CPU). Požiadavky a limity na pamäť sú organizované odlišne, takže ich možno ľahšie odhaliť. Ak to chcete urobiť, skontrolujte stav posledného reštartu modulu: či je „OOMKilled“. Škrtenie procesora nie je také jednoduché, pretože K8s sprístupňuje metriky iba podľa použitia, nie podľa skupín cgroups.

Požiadavka CPU

Limity CPU a agresívne obmedzovanie v Kubernetes
Ako je implementovaná požiadavka CPU

Pre jednoduchosť sa pozrime na proces s použitím stroja so 4-jadrovým CPU ako príklad.

K8s používa mechanizmus riadiacej skupiny (cgroups) na riadenie alokácie zdrojov (pamäť a procesor). K dispozícii je pre ňu hierarchický model: dieťa zdedí limity rodičovskej skupiny. Podrobnosti o distribúcii sú uložené vo virtuálnom súborovom systéme (/sys/fs/cgroup). V prípade procesora je to tak /sys/fs/cgroup/cpu,cpuacct/*.

K8s používa súbor cpu.share na pridelenie zdrojov procesora. V našom prípade koreňová cgroup získa 4096 zdieľaní zdrojov CPU – 100 % dostupného výkonu procesora (1 jadro = 1024; toto je pevná hodnota). Koreňová skupina rozdeľuje zdroje proporcionálne v závislosti od podielov potomkov registrovaných v cpu.share, a oni zasa robia to isté so svojimi potomkami atď. Na typickom uzle Kubernetes má koreňová skupina cgroup tri deti: system.slice, user.slice и kubepods. Prvé dve podskupiny sa používajú na distribúciu zdrojov medzi kritické zaťaženie systému a používateľské programy mimo K8. Posledný - kubepods — vytvorené spoločnosťou Kubernetes na distribúciu zdrojov medzi modulmi.

Vyššie uvedený diagram ukazuje, že prvá a druhá podskupina dostali každý 1024 akcií s pridelenou podskupinou kuberpod 4096 akcií Ako je to možné: koniec koncov, koreňová skupina má prístup iba k 4096 podielov a súčet podielov jej potomkov tento počet výrazne prevyšuje (6144)? Ide o to, že hodnota dáva logický zmysel, takže plánovač Linuxu (CFS) ju používa na proporcionálne prideľovanie zdrojov CPU. V našom prípade dostávajú prvé dve skupiny 680 skutočné akcie (16,6 % zo 4096 XNUMX) a zvyšok dostáva kubepod 2736 akcií V prípade výpadku prvé dve skupiny nevyužijú pridelené zdroje.

Našťastie má plánovač mechanizmus, ktorý zabráni plytvaniu nevyužitými prostriedkami CPU. Prenáša „nečinnú“ kapacitu do globálnej oblasti, z ktorej sa distribuuje do skupín, ktoré potrebujú dodatočný výkon procesora (prenos prebieha v dávkach, aby sa predišlo stratám pri zaokrúhľovaní). Podobná metóda sa aplikuje na všetkých potomkov potomkov.

Tento mechanizmus zaisťuje spravodlivé rozdelenie výkonu procesora a zabezpečuje, že žiadny proces „nekradne“ zdroje iným.

Limit CPU

Napriek tomu, že konfigurácie limitov a požiadaviek v K8 vyzerajú podobne, ich implementácia je radikálne odlišná: toto najviac zavádzajúce a najmenej zdokumentovaná časť.

K8s sa zapája mechanizmus kvót CFS implementovať limity. Ich nastavenia sú špecifikované v súboroch cfs_period_us и cfs_quota_us v adresári cgroup (súbor sa tam tiež nachádza cpu.share).

Na rozdiel od cpu.share, kvóta je založená na časový úseka nie na dostupnom výkone procesora. cfs_period_us určuje trvanie periódy (epochy) - je to vždy 100000 100 μs (8 ms). V KXNUMXs je možnosť zmeniť túto hodnotu, ale zatiaľ je dostupná len v alpha. Plánovač používa epochu na reštartovanie použitých kvót. Druhý súbor cfs_quota_us, určuje dostupný čas (kvótu) v každej epoche. Všimnite si, že sa uvádza aj v mikrosekundách. Kvóta môže presiahnuť dĺžku epochy; inými slovami, môže byť väčšia ako 100 ms.

Pozrime sa na dva scenáre na 16-jadrových strojoch (najbežnejší typ počítača, ktorý máme v Omio):

Limity CPU a agresívne obmedzovanie v Kubernetes
Scenár 1: 2 vlákna a limit 200 ms. Žiadne škrtenie

Limity CPU a agresívne obmedzovanie v Kubernetes
Scenár 2: 10 vlákien a limit 200 ms. Obmedzenie začína po 20 ms, prístup k zdrojom procesora sa obnoví po ďalších 80 ms

Povedzme, že ste nastavili limit CPU na 2 jadrá; Kubernetes prevedie túto hodnotu na 200 ms. To znamená, že kontajner môže využívať maximálne 200 ms CPU času bez škrtenia.

A tu začína zábava. Ako je uvedené vyššie, dostupná kvóta je 200 ms. Ak pracujete paralelne desať vlákna na 12-jadrovom počítači (pozri obrázok pre scenár 2), kým sú všetky ostatné moduly nečinné, kvóta sa vyčerpá len za 20 ms (pretože 10 * 20 ms = 200 ms) a všetky vlákna tohto modulu budú visieť » (plyn) počas nasledujúcich 80 ms. Už spomínané chyba plánovača, kvôli čomu dochádza k nadmernému škrteniu a kontajner nedokáže naplniť ani existujúcu kvótu.

Ako vyhodnotiť škrtenie v strukoch?

Stačí sa prihlásiť do modulu a spustiť cat /sys/fs/cgroup/cpu/cpu.stat.

  • nr_periods — celkový počet období plánovača;
  • nr_throttled — počet škrtených období v zložení nr_periods;
  • throttled_time — kumulatívny skrátený čas v nanosekundách.

Limity CPU a agresívne obmedzovanie v Kubernetes

Čo sa to vlastne deje?

Výsledkom je vysoké škrtenie vo všetkých aplikáciách. Niekedy je in jeden a pol krát silnejšie ako vypočítané!

To vedie k rôznym chybám - zlyhaniam kontroly pripravenosti, zamrznutiu kontajnera, prerušeniu sieťového pripojenia, časovým limitom v rámci servisných hovorov. To v konečnom dôsledku vedie k zvýšeniu latencie a vyššej chybovosti.

Rozhodnutie a dôsledky

Všetko je tu jednoduché. Opustili sme limity CPU a začali sme aktualizovať jadro OS v klastroch na najnovšiu verziu, v ktorej bola chyba opravená. Počet chýb (HTTP 5xx) v našich službách okamžite výrazne klesol:

Chyby HTTP 5xx

Limity CPU a agresívne obmedzovanie v Kubernetes
Chyby HTTP 5xx pre jednu kritickú službu

Čas odozvy p95

Limity CPU a agresívne obmedzovanie v Kubernetes
Kritická latencia žiadosti o službu, 95. percentil

Prevádzkové náklady

Limity CPU a agresívne obmedzovanie v Kubernetes
Počet hodín strávených inštanciou

Čo je to úlovok?

Ako je uvedené na začiatku článku:

Dá sa nakresliť analógia s obecným bytom... Kubernetes pôsobí ako realitný maklér. Ako však ochrániť nájomníkov pred vzájomnými konfliktmi? Čo ak sa jeden z nich, povedzme, rozhodne požičať si kúpeľňu na pol dňa?

Tu je háčik. Jeden neopatrný kontajner môže zjesť všetky dostupné zdroje CPU na počítači. Ak máte inteligentný zásobník aplikácií (napríklad JVM, Go, Node VM sú správne nakonfigurované), potom to nie je problém: v takýchto podmienkach môžete pracovať dlhú dobu. Ale ak sú aplikácie zle optimalizované alebo nie sú optimalizované vôbec (FROM java:latest), situácia sa môže vymknúť spod kontroly. V Omio máme automatizované základné súbory Dockerfiles s primeranými predvolenými nastaveniami pre hlavný jazykový zásobník, takže tento problém neexistoval.

Odporúčame sledovať metriky POUŽITIE (použitie, saturácia a chyby), oneskorenia API a chybovosť. Uistite sa, že výsledky spĺňajú očakávania.

referencie

Toto je náš príbeh. Nasledujúce materiály výrazne pomohli pochopiť, čo sa deje:

Hlásenia chýb Kubernetes:

Stretli ste sa vo svojej praxi s podobnými problémami alebo máte skúsenosti s škrtením v kontajnerových výrobných prostrediach? Podeľte sa o svoj príbeh v komentároch!

PS od prekladateľa

Prečítajte si aj na našom blogu:

Zdroj: hab.com

Pridať komentár