CPU ograničenja i agresivno prigušivanje u Kubernetesu

Bilješka. transl.: Ova uzbudljiva istorija Omia – evropskog agregatora za putovanja – vodi čitaoce od osnovne teorije do fascinantnih praktičnih zamršenosti Kubernetes konfiguracije. Poznavanje takvih slučajeva pomaže ne samo da proširite svoje vidike, već i spriječite netrivijalne probleme.

CPU ograničenja i agresivno prigušivanje u Kubernetesu

Da li vam se ikada dogodilo da se neka aplikacija zaglavi, prestane da odgovara na zdravstvene preglede i ne možete da shvatite zašto? Jedno moguće objašnjenje se odnosi na ograničenja kvote CPU resursa. O tome ćemo govoriti u ovom članku.

TL; DR:
Preporučujemo da onemogućite CPU ograničenja u Kubernetesu (ili onemogućite CFS kvote u Kubeletu) ako koristite verziju Linux kernela sa greškom u CFS kvoti. U jezgru je dostupan ozbiljno i dobro poznat greška koja dovodi do pretjeranog prigušivanja i kašnjenja
.

U Omiu cjelokupnom infrastrukturom upravlja Kubernetes. Sva naša radna opterećenja bez stanja i stanja rade isključivo na Kubernetesu (koristimo Google Kubernetes Engine). U posljednjih šest mjeseci počeli smo da primjećujemo nasumična usporavanja. Aplikacije se zamrzavaju ili prestaju da odgovaraju na zdravstvene provjere, gube vezu s mrežom itd. Ovakvo ponašanje nas je dugo zbunjivalo i konačno smo odlučili ozbiljno shvatiti problem.

Sažetak članka:

  • Nekoliko riječi o kontejnerima i Kubernetesu;
  • Kako se implementiraju CPU zahtjevi i ograničenja;
  • Kako CPU limit radi u okruženjima sa više jezgara;
  • Kako pratiti CPU throttling;
  • Rješenje problema i nijanse.

Nekoliko riječi o kontejnerima i Kubernetesu

Kubernetes je u suštini savremeni standard u svetu infrastrukture. Njegov glavni zadatak je orkestracija kontejnera.

Kontejneri

U prošlosti smo morali da kreiramo artefakte kao što su Java JAR-ovi/WAR-ovi, Python jaja ili izvršni fajlovi za pokretanje na serverima. Međutim, da bi funkcionisali, trebalo je obaviti dodatni posao: instalirati runtime okruženje (Java/Python), postaviti potrebne fajlove na prava mesta, osigurati kompatibilnost sa određenom verzijom operativnog sistema itd. Drugim riječima, pažljiva pažnja se morala posvetiti upravljanju konfiguracijom (što je često bio izvor nesuglasica između programera i sistemskih administratora).

Kontejneri su sve promijenili. Sada je artefakt slika kontejnera. Može se predstaviti kao neka vrsta proširene izvršne datoteke koja sadrži ne samo program, već i punopravno okruženje za izvršavanje (Java/Python/...), kao i potrebne datoteke/pakete, unaprijed instalirane i spremne za trči. Kontejneri se mogu postaviti i pokrenuti na različitim serverima bez ikakvih dodatnih koraka.

Osim toga, kontejneri rade u vlastitom okruženju sandbox-a. Imaju svoj virtuelni mrežni adapter, vlastiti sistem datoteka s ograničenim pristupom, vlastitu hijerarhiju procesa, vlastita ograničenja na CPU i memoriju, itd. Sve se to implementira zahvaljujući posebnom podsistemu Linux kernela - imenskim prostorima.

Kubernet

Kao što je ranije rečeno, Kubernetes je orkestrator kontejnera. Funkcioniše ovako: date mu skup mašina, a zatim kažete: "Hej, Kubernetes, hajde da pokrenemo deset instanci mog kontejnera sa 2 procesora i 3 GB memorije svaki, i nastavi da rade!" Kubernetes će se pobrinuti za ostalo. Pronaći će slobodan kapacitet, pokrenuti kontejnere i ponovo ih pokrenuti ako je potrebno, pokrenuti ažuriranje prilikom promjene verzija itd. U suštini, Kubernetes vam omogućava da apstrahujete hardversku komponentu i pravi širok spektar sistema pogodnih za postavljanje i pokretanje aplikacija.

CPU ograničenja i agresivno prigušivanje u Kubernetesu
Kubernetes iz ugla laika

Šta su zahtjevi i ograničenja u Kubernetesu

U redu, pokrili smo kontejnere i Kubernetes. Također znamo da više kontejnera može biti smješteno na istoj mašini.

Može se povući analogija sa zajedničkim stanom. Prostrani prostor (mašine/agregati) se uzima i iznajmljuje za više zakupaca (kontejnera). Kubernetes djeluje kao agent za nekretnine. Postavlja se pitanje kako spriječiti stanare od međusobnih sukoba? Šta ako neko od njih, recimo, odluči da pozajmi kupatilo na pola dana?

Ovdje stupaju u igru ​​zahtjevi i ograničenja. CPU zahtjev potrebno isključivo u svrhu planiranja. Ovo je nešto poput „liste želja“ kontejnera i koristi se za odabir najpogodnijeg čvora. U isto vrijeme i CPU granica može se uporediti sa ugovorom o najmu - čim odaberemo jedinicu za kontejner, ne mogu preći utvrđene granice. I tu nastaje problem...

Kako se zahtjevi i ograničenja implementiraju u Kubernetes

Kubernetes koristi mehanizam za prigušivanje (preskakanje ciklusa takta) ugrađen u kernel za implementaciju CPU ograničenja. Ako aplikacija prekorači ograničenje, prigušivanje je omogućeno (tj. prima manje CPU ciklusa). Zahtjevi i ograničenja za memoriju različito su organizirani, pa ih je lakše otkriti. Da biste to učinili, samo provjerite status zadnjeg ponovnog pokretanja modula: da li je “OOMKilled”. Prigušivanje CPU-a nije tako jednostavno, pošto K8s čini metriku dostupnim samo po upotrebi, a ne po cgroupama.

CPU zahtjev

CPU ograničenja i agresivno prigušivanje u Kubernetesu
Kako se implementira CPU zahtjev

Radi jednostavnosti, pogledajmo proces koristeći mašinu sa 4-jezgrenim CPU-om kao primjer.

K8s koristi mehanizam kontrolne grupe (cgroups) za kontrolu alokacije resursa (memorije i procesora). Za to je dostupan hijerarhijski model: dijete nasljeđuje granice roditeljske grupe. Detalji distribucije su pohranjeni u virtuelnom sistemu datoteka (/sys/fs/cgroup). U slučaju procesora to jeste /sys/fs/cgroup/cpu,cpuacct/*.

K8s koristi fajl cpu.share za alociranje procesorskih resursa. U našem slučaju, root cgroup dobija 4096 dionica CPU resursa - 100% raspoložive snage procesora (1 jezgro = 1024; ovo je fiksna vrijednost). Korijenska grupa raspoređuje resurse proporcionalno ovisno o udjelu potomaka koji su registrirani cpu.share, a oni zauzvrat čine isto sa svojim potomcima itd. Na tipičnom Kubernetes čvoru, korijenska cgroup ima tri djece: system.slice, user.slice и kubepods. Prve dvije podgrupe se koriste za distribuciju resursa između kritičnih opterećenja sistema i korisničkih programa izvan K8s. Posljednji - kubepods — kreirao Kubernetes za distribuciju resursa između podova.

Gornji dijagram pokazuje da su prva i druga podgrupa primile svaku 1024 dionice, s dodijeljenom kuberpod podgrupom 4096 dionice Kako je to moguće: na kraju krajeva, root grupa ima pristup samo 4096 dionica, a zbir udjela njenih potomaka znatno premašuje ovaj broj (6144)? Poenta je u tome da vrijednost ima logičan smisao, pa je Linux planer (CFS) koristi za proporcionalnu dodjelu CPU resursa. U našem slučaju prve dvije grupe primaju 680 realnih dionica (16,6% od 4096), a kubepod prima ostatak 2736 dionice U slučaju zastoja, prve dvije grupe neće koristiti dodijeljene resurse.

Srećom, planer ima mehanizam za izbjegavanje trošenja neiskorištenih CPU resursa. On prenosi "neaktivni" kapacitet u globalni skup, iz kojeg se distribuira grupama kojima je potrebna dodatna snaga procesora (prijenos se događa u serijama kako bi se izbjegli gubici zaokruživanja). Slična metoda se primjenjuje na sve potomke potomaka.

Ovaj mehanizam osigurava pravednu raspodjelu procesorske snage i osigurava da nijedan proces ne “krade” resurse od drugih.

CPU Limit

Unatoč činjenici da konfiguracije ograničenja i zahtjeva u K8s izgledaju slično, njihova implementacija je radikalno drugačija: najviše obmanjujuće i najmanje dokumentovani dio.

K8s se uključuje CFS mehanizam kvota implementirati ograničenja. Njihova podešavanja su navedena u fajlovima cfs_period_us и cfs_quota_us u direktoriju cgroup (datoteka se također nalazi tamo cpu.share).

Suprotno cpu.share, kvota se zasniva na vremenski period, a ne na dostupnu snagu procesora. cfs_period_us specificira trajanje perioda (epohe) - uvijek je 100000 μs (100 ms). Postoji opcija za promjenu ove vrijednosti u K8s, ali je za sada dostupna samo u alfa verziji. Planer koristi epohu za ponovno pokretanje korištenih kvota. Drugi fajl cfs_quota_us, specificira dostupno vrijeme (kvotu) u svakoj epohi. Imajte na umu da je također navedeno u mikrosekundama. Kvota može premašiti dužinu epohe; drugim riječima, može biti veće od 100 ms.

Pogledajmo dva scenarija na mašinama sa 16 jezgara (najčešći tip računara koji imamo u Omiu):

CPU ograničenja i agresivno prigušivanje u Kubernetesu
Scenario 1: 2 niti i ograničenje od 200 ms. Nema prigušivanja

CPU ograničenja i agresivno prigušivanje u Kubernetesu
Scenario 2: 10 niti i ograničenje od 200 ms. Prigušivanje počinje nakon 20 ms, pristup resursima procesora se nastavlja nakon još 80 ms

Recimo da ste postavili CPU limit na 2 jezgra; Kubernetes će prevesti ovu vrijednost na 200 ms. To znači da kontejner može koristiti najviše 200ms CPU vremena bez prigušenja.

I tu počinje zabava. Kao što je gore spomenuto, dostupna kvota je 200 ms. Ako radite paralelno deset niti na 12-jezgrenoj mašini (pogledajte ilustraciju za scenario 2), dok su svi ostali podovi neaktivni, kvota će biti iscrpljena za samo 20 ms (pošto 10 * 20 ms = 200 ms), a sve niti ovog pod-a će visjeti » (gas) za narednih 80 ms. Već pomenuti planer bug, zbog čega dolazi do prekomjernog prigušivanja i kontejner ne može ispuniti ni postojeću kvotu.

Kako procijeniti prigušivanje u podovima?

Samo se prijavite na pod i izvršite cat /sys/fs/cgroup/cpu/cpu.stat.

  • nr_periods — ukupan broj perioda planiranja;
  • nr_throttled — broj prigušenih perioda u kompoziciji nr_periods;
  • throttled_time — kumulativno prigušeno vrijeme u nanosekundama.

CPU ograničenja i agresivno prigušivanje u Kubernetesu

Šta se zapravo dešava?

Kao rezultat toga, dobijamo visoko prigušivanje u svim aplikacijama. Ponekad je unutra jedan i po put jači od proračunatog!

To dovodi do raznih grešaka - neuspjeh u provjeri spremnosti, zamrzavanje kontejnera, prekid mrežne veze, vremensko ograničenje unutar poziva servisa. Ovo na kraju rezultira povećanjem latencije i većim stopama grešaka.

Odluka i posljedice

Ovdje je sve jednostavno. Napustili smo CPU ograničenja i počeli ažurirati kernel OS-a u klasterima na najnoviju verziju, u kojoj je greška ispravljena. Broj grešaka (HTTP 5xx) u našim servisima je odmah značajno opao:

HTTP 5xx greške

CPU ograničenja i agresivno prigušivanje u Kubernetesu
HTTP 5xx greške za jednu kritičnu uslugu

Vrijeme odziva p95

CPU ograničenja i agresivno prigušivanje u Kubernetesu
Kašnjenje zahtjeva kritične usluge, 95. percentil

Operativni troškovi

CPU ograničenja i agresivno prigušivanje u Kubernetesu
Broj utrošenih sati

Šta je ulov?

Kao što je rečeno na početku članka:

Može se povući analogija sa zajedničkim stanom... Kubernetes se ponaša kao prodavac nekretnina. Ali kako zaštititi stanare od međusobnih sukoba? Šta ako neko od njih, recimo, odluči da pozajmi kupatilo na pola dana?

Evo u čemu je kvaka. Jedan nemaran kontejner može pojesti sve raspoložive CPU resurse na mašini. Ako imate pametni stog aplikacija (na primjer, JVM, Go, Node VM su pravilno konfigurirani), onda to nije problem: možete raditi u takvim uvjetima dugo vremena. Ali ako su aplikacije loše optimizirane ili uopće nisu optimizirane (FROM java:latest), situacija može izmaći kontroli. U Omiu imamo automatizirane osnovne Dockerfiles sa adekvatnim zadanim postavkama za glavni jezični stog, tako da ovaj problem nije postojao.

Preporučujemo praćenje metrike UPOTREBA (upotreba, zasićenje i greške), kašnjenja API-ja i stope grešaka. Osigurajte da rezultati ispunjavaju očekivanja.

reference

Ovo je naša priča. Sljedeći materijali su uvelike pomogli da se shvati šta se dešava:

Kubernetes izvještaji o greškama:

Da li ste se susreli sa sličnim problemima u svojoj praksi ili imate iskustva u vezi sa prigušivanjem u kontejnerskim proizvodnim okruženjima? Podijelite svoju priču u komentarima!

PS od prevodioca

Pročitajte i na našem blogu:

izvor: www.habr.com

Dodajte komentar