Limity CPU a agresivní omezování v Kubernetes

Poznámka. přel.: Tato poutavá historie Omio – evropského cestovního agregátora – vede čtenáře od základní teorie k fascinujícím praktickým složitostem konfigurace Kubernetes. Znalost takových případů pomáhá nejen rozšířit vaše obzory, ale také předcházet netriviálním problémům.

Limity CPU a agresivní omezování v Kubernetes

Stalo se vám někdy, že se aplikace zasekla na místě, přestala reagovat na zdravotní kontroly a nebyli jste schopni zjistit proč? Jedno z možných vysvětlení souvisí s limity kvóty prostředků CPU. To je to, o čem budeme mluvit v tomto článku.

TL; DR:
Důrazně doporučujeme deaktivovat limity CPU v Kubernetes (nebo deaktivovat kvóty CFS v Kubelet), pokud používáte verzi linuxového jádra s chybou kvóty CFS. V jádru je k dispozici vážné a dobře známý chyba, která vede k nadměrnému škrcení a zpoždění
.

V Omiu celou infrastrukturu spravuje Kubernetes. Všechny naše stavové a bezstavové úlohy běží výhradně na Kubernetes (používáme Google Kubernetes Engine). V posledním půlroce jsme začali pozorovat náhodná zpomalení. Aplikace zamrznou nebo přestanou reagovat na kontroly stavu, ztratí připojení k síti atd. Toto chování nás dlouho mátlo a nakonec jsme se rozhodli vzít tento problém vážně.

Shrnutí článku:

  • Pár slov o kontejnerech a Kubernetes;
  • Jak jsou implementovány požadavky a limity CPU;
  • Jak funguje limit CPU ve vícejádrových prostředích;
  • Jak sledovat omezování CPU;
  • Řešení problému a nuance.

Pár slov o kontejnerech a Kubernetes

Kubernetes je v podstatě moderní standard ve světě infrastruktury. Jeho hlavním úkolem je orchestrace kontejnerů.

kontejnery

V minulosti jsme museli vytvářet artefakty jako Java JAR/WAR, Python Eggs nebo spustitelné soubory pro běh na serverech. Aby však fungovaly, bylo nutné provést další práci: nainstalovat runtime prostředí (Java/Python), umístit potřebné soubory na správná místa, zajistit kompatibilitu s konkrétní verzí operačního systému atd. Jinými slovy, správě konfigurace bylo třeba věnovat pečlivou pozornost (která byla často zdrojem sporů mezi vývojáři a správci systému).

Kontejnery vše změnily. Nyní je artefaktem obrázek kontejneru. Může být reprezentován jako jakýsi rozšířený spustitelný soubor obsahující nejen program, ale také plnohodnotné spouštěcí prostředí (Java/Python/...), stejně jako potřebné soubory/balíčky, předinstalované a připravené k běh. Kontejnery lze nasadit a spustit na různých serverech bez dalších kroků.

Kromě toho kontejnery fungují ve vlastním prostředí sandboxu. Mají vlastní virtuální síťový adaptér, vlastní souborový systém s omezeným přístupem, vlastní hierarchii procesů, vlastní omezení na CPU a paměť atd. To vše je implementováno díky speciálnímu subsystému linuxového jádra – jmenným prostorům.

Kubernetes

Jak bylo uvedeno dříve, Kubernetes je kontejnerový orchestrátor. Funguje to takto: dáte mu fond počítačů a pak řeknete: „Hej, Kubernetesi, spustíme deset instancí mého kontejneru se 2 procesory a 3 GB paměti a necháme je běžet!“ O zbytek se postará Kubernetes. Najde volnou kapacitu, spustí kontejnery a v případě potřeby je restartuje, zavede aktualizace při změně verzí atd. V podstatě vám Kubernetes umožňuje abstrahovat hardwarovou komponentu a dělá širokou škálu systémů vhodných pro nasazování a spouštění aplikací.

Limity CPU a agresivní omezování v Kubernetes
Kubernetes z pohledu laika

Co jsou požadavky a limity v Kubernetes

Dobře, zakryli jsme kontejnery a Kubernetes. Víme také, že na stejném stroji může být umístěno více kontejnerů.

Analogii lze nakreslit se společným bytem. Prostorné prostory (stroje/jednotky) jsou odebrány a pronajímány několika nájemcům (kontejnery). Kubernetes působí jako realitní makléř. Nabízí se otázka, jak ochránit nájemníky před vzájemnými konflikty? Co když se jeden z nich, řekněme, rozhodne půjčit si koupelnu na půl dne?

Zde vstupují do hry požadavky a limity. procesor Žádost potřebné pouze pro účely plánování. Je to něco jako „seznam přání“ kontejneru a používá se k výběru nejvhodnějšího uzlu. Zároveň CPU Omezit lze přirovnat k nájemní smlouvě - jakmile vybereme jednotku pro kontejner, nemůže překračovat stanovené limity. A tady nastává problém...

Jak jsou požadavky a limity implementovány v Kubernetes

Kubernetes používá škrtící mechanismus (přeskakování hodinových cyklů) zabudovaný v jádře k implementaci limitů CPU. Pokud aplikace překročí limit, je povoleno omezení (tj. přijímá méně cyklů CPU). Požadavky a limity pro paměť jsou organizovány odlišně, takže je lze snáze odhalit. Chcete-li to provést, stačí zkontrolovat stav posledního restartu modulu: zda je „OOMKilled“. Omezování CPU není tak jednoduché, protože K8s zpřístupňuje metriky pouze podle použití, nikoli podle cgroups.

Požadavek CPU

Limity CPU a agresivní omezování v Kubernetes
Jak je implementován požadavek CPU

Pro jednoduchost se podívejme na proces pomocí stroje se 4jádrovým CPU jako příklad.

K8s používá mechanismus kontrolní skupiny (cgroups) k řízení alokace zdrojů (paměti a procesoru). K dispozici je pro něj hierarchický model: dítě zdědí limity nadřazené skupiny. Podrobnosti o distribuci jsou uloženy ve virtuálním systému souborů (/sys/fs/cgroup). V případě procesoru je to tak /sys/fs/cgroup/cpu,cpuacct/*.

K8s používá soubor cpu.share k přidělení prostředků procesoru. V našem případě kořenová cgroup získá 4096 sdílení zdrojů CPU – 100 % dostupného výkonu procesoru (1 jádro = 1024; toto je pevná hodnota). Kořenová skupina rozděluje zdroje proporcionálně v závislosti na podílech potomků registrovaných v cpu.sharea oni zase totéž dělají se svými potomky atd. Na typickém uzlu Kubernetes má kořenová cgroup tři potomky: system.slice, user.slice и kubepods. První dvě podskupiny se používají k distribuci zdrojů mezi kritická systémová zatížení a uživatelské programy mimo K8. Poslední - kubepods — vytvořený Kubernetes k distribuci zdrojů mezi moduly.

Výše uvedený diagram ukazuje, že první a druhá podskupina obdržely obě 1024 akcií s přidělenou podskupinou kuberpod 4096 akcií Jak je to možné: kořenová skupina má koneckonců přístup pouze k 4096 podílů a součet podílů jejích potomků tento počet výrazně převyšuje (6144)? Jde o to, že hodnota dává logický smysl, takže plánovač Linuxu (CFS) ji používá k proporcionální alokaci prostředků CPU. V našem případě dostávají první dvě skupiny 680 reálné akcie (16,6 % z 4096) a kubepod obdrží zbývající 2736 akcií V případě výpadku první dvě skupiny nevyužijí přidělené prostředky.

Naštěstí má plánovač mechanismus, který zabrání plýtvání nevyužitými prostředky CPU. Přenáší „nečinnou“ kapacitu do globálního fondu, ze kterého je distribuována do skupin, které potřebují další výkon procesoru (přenos probíhá v dávkách, aby nedocházelo ke ztrátám při zaokrouhlování). Podobná metoda je aplikována na všechny potomky potomků.

Tento mechanismus zajišťuje spravedlivé rozdělení výkonu procesoru a zajišťuje, že žádný proces „nekrade“ zdroje ostatním.

Limit CPU

Navzdory skutečnosti, že konfigurace limitů a požadavků v K8 vypadají podobně, jejich implementace je radikálně odlišná: toto nejvíce zavádějící a nejméně zdokumentovaná část.

K8s se zapojí mechanismus kvót CFS implementovat limity. Jejich nastavení jsou specifikována v souborech cfs_period_us и cfs_quota_us v adresáři cgroup (tam se také nachází soubor cpu.share).

Na rozdíl od cpu.share, kvóta je založena na časový úseka nikoli na dostupném výkonu procesoru. cfs_period_us udává dobu trvání periody (epochy) - je vždy 100000 μs (100 ms). V K8s je možnost tuto hodnotu změnit, ale zatím je dostupná pouze v alpha. Plánovač používá epochu k restartování použitých kvót. Druhý soubor cfs_quota_us, určuje dostupný čas (kvótu) v každé epoše. Všimněte si, že se také uvádí v mikrosekundách. Kvóta může překročit délku epochy; jinými slovy, může být větší než 100 ms.

Podívejme se na dva scénáře na 16jádrových strojích (nejběžnější typ počítače, který máme v Omio):

Limity CPU a agresivní omezování v Kubernetes
Scénář 1: 2 vlákna a limit 200 ms. Žádné škrcení

Limity CPU a agresivní omezování v Kubernetes
Scénář 2: 10 vláken a limit 200 ms. Omezení začíná po 20 ms, přístup ke zdrojům procesoru je obnoven po dalších 80 ms

Řekněme, že jste nastavili limit CPU na 2 jádra; Kubernetes převede tuto hodnotu na 200 ms. To znamená, že kontejner může využívat maximálně 200 ms času CPU bez omezení.

A tady začíná zábava. Jak je uvedeno výše, dostupná kvóta je 200 ms. Pokud pracujete paralelně deset vlákna na 12jádrovém počítači (viz obrázek pro scénář 2), zatímco všechny ostatní moduly jsou nečinné, kvóta bude vyčerpána za pouhých 20 ms (protože 10 * 20 ms = 200 ms) a všechna vlákna tohoto modulu přestanou fungovat » (plyn) na dalších 80 ms. Již zmíněný chyba plánovače, kvůli kterému dochází k nadměrnému škrcení a kontejner ani nemůže naplnit stávající kvótu.

Jak vyhodnotit škrcení v luscích?

Stačí se přihlásit do modulu a spustit cat /sys/fs/cgroup/cpu/cpu.stat.

  • nr_periods — celkový počet období plánovače;
  • nr_throttled — počet škrtených period ve skladbě nr_periods;
  • throttled_time — kumulativní zkrácený čas v nanosekundách.

Limity CPU a agresivní omezování v Kubernetes

Co se to vlastně děje?

Výsledkem je vysoké škrcení ve všech aplikacích. Někdy je uvnitř jeden a půlkrát silnější, než se počítalo!

To vede k různým chybám – selhání kontroly připravenosti, zamrznutí kontejneru, přerušení síťového připojení, časové limity v rámci servisních hovorů. To má v konečném důsledku za následek zvýšenou latenci a vyšší chybovost.

Rozhodnutí a důsledky

Všechno je zde jednoduché. Opustili jsme limity CPU a začali aktualizovat jádro OS v clusterech na nejnovější verzi, ve které byla chyba opravena. Počet chyb (HTTP 5xx) v našich službách okamžitě výrazně klesl:

Chyby HTTP 5xx

Limity CPU a agresivní omezování v Kubernetes
Chyby HTTP 5xx pro jednu kritickou službu

Doba odezvy p95

Limity CPU a agresivní omezování v Kubernetes
Latence požadavku na kritickou službu, 95. percentil

Provozní náklady

Limity CPU a agresivní omezování v Kubernetes
Počet hodin strávených instancemi

Co je to úlovek?

Jak je uvedeno na začátku článku:

Analogii lze nakreslit s obecním bytem... Kubernetes působí jako realitní makléř. Jak ale ochránit nájemníky před vzájemnými konflikty? Co když se jeden z nich, řekněme, rozhodne půjčit si koupelnu na půl dne?

Tady je háček. Jeden neopatrný kontejner může sníst všechny dostupné zdroje CPU na počítači. Pokud máte inteligentní zásobník aplikací (například JVM, Go, Node VM jsou správně nakonfigurovány), pak to není problém: v takových podmínkách můžete pracovat po dlouhou dobu. Ale pokud jsou aplikace špatně optimalizované nebo nejsou optimalizovány vůbec (FROM java:latest), situace se může vymknout kontrole. Ve společnosti Omio máme automatizované základní soubory Dockerfiles s adekvátním výchozím nastavením pro hlavní jazykový zásobník, takže tento problém neexistoval.

Doporučujeme sledovat metriky POUŽITÍ (využití, saturace a chyby), zpoždění API a chybovost. Ujistěte se, že výsledky splňují očekávání.

reference

Toto je náš příběh. Následující materiály velmi pomohly pochopit, co se děje:

Hlášení chyb Kubernetes:

Setkali jste se ve své praxi s podobnými problémy nebo máte zkušenosti s omezováním v kontejnerových produkčních prostředích? Podělte se o svůj příběh v komentářích!

PS od překladatele

Přečtěte si také na našem blogu:

Zdroj: www.habr.com

Přidat komentář