Kubernetes'is kaotuste jÀlgimine.

Kubernetes'is kaotuste jÀlgimine.

MĂ”ned aastad tagasi arutleti Kubernetes'e ĂŒle ametlikus GitHubi blogis. Sellest ajast on see saanud standardtehnoloogiaks teenuste kĂ€itamiseks. NĂŒĂŒd haldab Kubernetes mĂ€rkimisvÀÀrset osa sise- ja avalikest teenustest. Kuna meie klastrid on kasvanud ja jĂ”udlusnĂ”uded rangemaks muutunud, oleme hakanud tĂ€hele panema, et mĂ”nedes Kubernetes'e teenustes ilmnevad sporaadiilised latentsused, mida ei saa seletada rakenduse enda koormusega. Sisuliselt esinevad rakendustes juhuslikud vĂ”rgu latentsused kuni 100 ms ja rohkem, mis toovad kaasa aegumisi vĂ”i korduskatseid. Oodati, et teenused suudavad pĂ€ringutele vastata palju kiiremini kui 100 ms. Kuid see pole vĂ”imalik, kui ĂŒhendus ise vĂ”tab nii kaua aega. Samuti oleme mĂ€rganud MySQL-i vĂ€ga kiireid pĂ€ringuid, millele oleks pidanud kuluma millisekundid, ja MySQL tĂ”esti suutis vastata millisekunditega, kuid kĂŒsiva rakenduse vaatenurgast vĂ”ttis vastamine 100 ms vĂ”i rohkem.

Selgelt oli nĂ€ha, et probleem tekib ainult Kubernetes'e sĂ”lme ĂŒhendamisel, isegi kui pĂ€ring tuli Kubernetes'est vĂ€lja. Probleemi on kĂ”ige lihtsam korrata testis

, mis kÀivitub igalt sisemiselt, testib Kubernetes'e teenust teatud sadamal ja sporaadiiliselt registreerib suurt latentsust. Selles artiklis vaatleme, kuidas me suutsime selle probleemi pÔhjuse jÀlile jÔuda. VegetaEemaldame ebaolulise keerukuse rikke ahelas

Korrates sama nĂ€idet, tahtsime probleemi fookust kitsendada ja eemaldada liigsed keerukuse kihid. Alguses oli Kubernetes'e pod'ide ja Vegeta vahelises voos liiga palju elemente. SĂŒgava vĂ”rgu probleemi mÀÀratlemiseks tuleb mĂ”ned neist vĂ€lja jĂ€tta.

Klient (Vegeta) loob TCP-ĂŒhenduse iga sĂ”lmega klastris. Kubernetes töötab ĂŒlekatte vĂ”rgu (olemasoleva andmekeskuse vĂ”rgu kohal), mis kasutab

Kubernetes'is kaotuste jÀlgimine.

IPIP , see tĂ€hendab, et kapseldab ĂŒlekatte vĂ”rgu IP-pakette andmekeskuse IP-pakettidesse. Esimese sĂ”lmega ĂŒhendamisel toimub aadresside muundurNetwork Address Translation (NAT), mille abil muudetakse Kubernetes'e sĂ”lme IP-aadress ja port ĂŒlekatte vĂ”rgu IP-aadressiks ja port asjakohasele pod'ile (konkreetset rakendust arvestades). VastuvĂ”etud pakettide puhul toimub vastupidine protsess. See on keeruline sĂŒsteem, millel on palju olekuid ja elemente, mis pidevalt uuenevad ja muutuvad teenuste juurutamisel ja liigutamisel. Vegeta testis ladestub latentsus TCP kĂ€epigistuse ajal (SYN ja SYN-ACK vahel). Selle liigse keerukuse eemaldamiseks on vĂ”imalik kasutada

Tööriist tcpdump lihtsate SYN-pingude jaoks. Kontrollime, kas vastava paketi latentsus on olemas, ja seejĂ€rel katki ĂŒhenduse. Saame andmeid filtreerida, lubades rohkem kui 100 ms pakette, ning saada lihtsam reprodutseerimise versioon kui tĂ€ispikk vĂ”rgu taseme 7 test Vegetas. Siin on "pings" Kubernetes'e sĂ”lmele, kasutades TCP SYN/SYN-ACK "teenuse sĂ”lme" sadamal (30927) 10 ms intervalliga, filtreeritud aeglaste vastuste jĂ€rgi: hping3 len=46 ip=172.16.47.27 ttl=59 DF id=0 sport=30927 flags=SA seq=1485 win=29200 rtt=127.1 ms

len=46 ip=172.16.47.27 ttl=59 DF id=0 sport=30927 flags=SA seq=1486 win=29200 rtt=117.0 ms

len=46 ip=172.16.47.27 ttl=59 DF id=0 sport=30927 flags=SA seq=1487 win=29200 rtt=106.2 ms

len=46 ip=172.16.47.27 ttl=59 DF id=0 sport=30927 flags=SA seq=1488 win=29200 rtt=104.1 ms

len=46 ip=172.16.47.27 ttl=59 DF id=0 sport=30927 flags=SA seq=5024 win=29200 rtt=109.2 ms

len=46 ip=172.16.47.27 ttl=59 DF id=0 sport=30927 flags=SA seq=5231 win=29200 rtt=109.2 ms

Esimese vaatlusena vĂ”ib kohe nĂ€ha, et jĂ€rjestuse ja ajastuse pĂ”hjal on selge, et need pole ĂŒhekordsed ummistused. Latentsus koguneb tihti ja lĂ”puks töödeldakse see.

Edasi tahame vĂ€lja selgitada, millised komponendid vĂ”ivad ummistuse tekkimisse sekkuda. Kas need vĂ”iksid olla mĂ”ned sajad iptables reegleid NAT-is? VĂ”i mĂ”ned probleemid IPIP tunneldamisel vĂ”rgus? Üks vĂ”imalus seda kontrollida on iga sĂŒsteemi sammu kontrollimine, vĂ€ljaarvamine. Mis juhtub, kui eemaldame NAT-i ja tulemĂŒĂŒriloogika, jĂ€ttes alles ainult IPIP osa:

Õnneks vĂ”imaldab Linux hĂ”lpsalt juurdepÀÀsu otse IP ĂŒlekande kihile, kui masin kuulub samasse vĂ”rku:

theojulienne@kube-node-client ~ $ sudo hping3 10.125.20.64 -S -i u10000 | egrep --line-buffered 'rtt=[0-9]{3}.'

Kubernetes'is kaotuste jÀlgimine.

len=40 ip=10.125.20.64 ttl=64 DF id=0 sport=0 flags=RA seq=7346 win=0 rtt=127.3 ms

len=40 ip=10.125.20.64 ttl=64 DF id=0 sport=0 flags=RA seq=7347 win=0 rtt=117.3 ms

len=40 ip=10.125.20.64 ttl=64 DF id=0 sport=0 flags=RA seq=7348 win=0 rtt=107.2 ms

Tulemustest lÀhtudes on probleem endiselt olemas! See vÀlistab iptables ja NAT. Nii et probleem on TCP-s? Vaatame, kuidas tavaline ICMP-ping jÀrgneb:

theojulienne@kube-node-client ~ $ sudo hping3 10.125.20.64 --icmp -i u10000 | egrep --line-buffered 'rtt=[0-9]{3}.'

len=28 ip=10.125.20.64 ttl=64 id=42594 icmp_seq=104 rtt=110.0 ms

len=28 ip=10.125.20.64 ttl=64 id=49448 icmp_seq=4022 rtt=141.3 ms

len=28 ip=10.125.20.64 ttl=64 id=49449 icmp_seq=4023 rtt=131.3 ms

pikk=28 ip=10.125.20.64 ttl=64 id=49448 icmp_jada=4022 rtt=141.3 ms

pikk=28 ip=10.125.20.64 ttl=64 id=49449 icmp_jada=4023 rtt=131.3 ms

len=28 ip=10.125.20.64 ttl=64 id=49450 icmp_seq=4024 rtt=121.2 ms

len=28 ip=10.125.20.64 ttl=64 id=49451 icmp_seq=4025 rtt=111.2 ms

len=28 ip=10.125.20.64 ttl=64 id=49452 icmp_seq=4026 rtt=101.1 ms

len=28 ip=10.125.20.64 ttl=64 id=50023 icmp_seq=4343 rtt=126.8 ms

len=28 ip=10.125.20.64 ttl=64 id=50024 icmp_seq=4344 rtt=116.8 ms

len=28 ip=10.125.20.64 ttl=64 id=50025 icmp_seq=4345 rtt=106.8 ms

len=28 ip=10.125.20.64 ttl=64 id=59727 icmp_seq=9836 rtt=106.1 ms

Tulemused nÀitavad, et probleem ei ole kadunud. Kas see on IPIP tunnel? Lihtsustame testi veel:

Kubernetes'is kaotuste jÀlgimine.

Kas kÔik paketid saadetakse nende kahe hosti vahel?

theojulienne@kube-node-client ~ $ sudo hping3 172.16.47.27 --icmp -i u10000 | egrep --line-buffered 'rtt=[0-9]{3}.'

len=46 ip=172.16.47.27 ttl=61 id=41127 icmp_seq=12564 rtt=140.9 ms

len=46 ip=172.16.47.27 ttl=61 id=41128 icmp_seq=12565 rtt=130.9 ms

len=46 ip=172.16.47.27 ttl=61 id=41129 icmp_seq=12566 rtt=120.8 ms

len=46 ip=172.16.47.27 ttl=61 id=41130 icmp_seq=12567 rtt=110.8 ms

len=46 ip=172.16.47.27 ttl=61 id=41131 icmp_seq=12568 rtt=100.7 ms

len=46 ip=172.16.47.27 ttl=61 id=9062 icmp_seq=31443 rtt=134.2 ms

len=46 ip=172.16.47.27 ttl=61 id=9063 icmp_seq=31444 rtt=124.2 ms

len=46 ip=172.16.47.27 ttl=61 id=9064 icmp_seq=31445 rtt=114.2 ms

len=46 ip=172.16.47.27 ttl=61 id=9065 icmp_seq=31446 rtt=104.2 ms

Lihtsustasime olukorda kahe Kubernetes sĂ”lmest, kes saadavad ĂŒksteisele mĂ”nda paketti, isegi ICMP pings. Nad nĂ€evad ikkagi viivitust, kui sihtkoht on 'halb' (mĂ”ned on halvemad kui teised).

NĂŒĂŒd viimane kĂŒsimus: miks viivitus tekib ainult kube-node serverites? Ja see juhtub, kui kube-node on saatja vĂ”i vastuvĂ”tja? Õnneks on seda samuti ĂŒsna lihtne vĂ€lja selgitada, saates paketi Kubernetesest vĂ€ljaspoolt hostist, kuid sama 'tuntud halvale' vastuvĂ”tjale. Nagu nĂ€eme, probleem ei ole kadunud:

theojulienne@shell ~ $ sudo hping3 172.16.47.27 -p 9876 -S -i u10000 | egrep --line-buffered 'rtt=[0-9]{3}.'

len=46 ip=172.16.47.27 ttl=61 DF id=0 sport=9876 flags=RA seq=312 win=0 rtt=108.5 ms

len=46 ip=172.16.47.27 ttl=61 DF id=0 sport=9876 flags=RA seq=5903 win=0 rtt=119.4 ms

len=46 ip=172.16.47.27 ttl=61 DF id=0 sport=9876 flags=RA seq=6227 win=0 rtt=139.9 ms

len=46 ip=172.16.47.27 ttl=61 DF id=0 sport=9876 flags=RA seq=7929 win=0 rtt=131.2 ms

SeejÀrel teeme sama pÀringud eelnevalt kui kube-node vÀlismaise hostini (mis vÀlistab algse hosti, kuna ping sisaldab RX ja TX komponente):

theojulienne@kube-node-client ~ $ sudo hping3 172.16.33.44 -p 9876 -S -i u10000 | egrep --line-buffered 'rtt=[0-9]{3}.'
^C
--- 172.16.33.44 hping statistika ---
22352 paketti edastatud, 22350 paketti saadud, 1% pakettide kadu
ringreis min/avg/max = 0.2/7.6/1010.6 ms

Uurides viivitusega paketide jÀÀke, saime lisainfot. TĂ€psemalt, et saatja (all) nĂ€eb seda ajaviivitust, kuid vastuvĂ”tja (ĂŒles) ei nĂ€e — vt Delta (sekundites) veerg:

Kubernetes'is kaotuste jÀlgimine.

Lisaks, kui vaadata TCP ja ICMP pakettide jÀrjekorra erinevust (jÀrjekorranumbrite kaupa) vastuvÔtja poolel, siis ICMP paketid saabuvad alati samas jÀrjekorras, nagu nad saadeti, kuid erineva ajaga. Samas TCP paketid mÔnikord vahelduvad, ja osa neist jÀÀb kinni. Eriti, kui uurida SYN paketid, siis saatja poolel nad tulevad jÀrjekorras, kuid vastuvÔtja poolel ei tule.

On Ă”huke erinevus selles, kuidas vĂ”rgukaardid kaasaegsetel serveritel (nagu meie andmekeskuses) kĂ€sitlevad pakette, mis sisaldavad TCP vĂ”i ICMP. Kui pakett saabub, siis vĂ”rguadapter 'hĂ€kkib selle ĂŒhenduse jĂ€rgi', st ĂŒritab jagada ĂŒhendusi jĂ€rjekordadesse ja saata iga jĂ€rjekord erinevale protsessorituumale. TCP puhul hĂ”lmab see hĂ€kkimine nii algset kui ka lĂ”pp-IP aadressi ja porti. TeisisĂ”nu, iga ĂŒhendus hĂ€kkitakse (vĂ”imalikult) erinevalt. ICMP puhul hĂ€kkitakse ainult IP-aadresse, kuna portide pole.

Veel ĂŒks uus tĂ€helepanek: selle perioodi jooksul nĂ€eme ICMP viivitusi kĂ”ikide kahe hosti vaheliste kommunikatsioonide puhul, kuid TCP ei nĂ€e. See ĂŒtleb meile, et pĂ”hjus on tĂ”enĂ€oliselt seotud RX jĂ€rjekordade hĂ€kkimisega: peaaegu kindlalt on ummik tekkimas RX paketide töötlemisel, mitte vastuste saatmisel.

See vĂ€listab pakettide saatmise vĂ”imalike pĂ”hjuste hulgas. NĂŒĂŒd teame, et paketitöötluse probleem on mĂ”nel kube-node serveril vastuvĂ”tmise pool.

Uurime paketitöötlust Linuxi tuumas

Kuna saame aru, miks probleem esineb mĂ”ne kube-node serveri vastuvĂ”tjal, vaatame, kuidas Linuxi tuum sĂŒsteemide pakette töötleb.

Tulles tagasi kĂ”ige lihtsama traditsioonilise rakenduse juurde, vĂ”rgukaart saabub pakett ja saadab katkestuse Linuxi tuumale, et öelda, et on pakett, mida tuleb töödelda. Tuum peatab muu töö, vahetab konteksti katkestuse kĂ€sitlejale, töötleb paketti ja naaseb seejĂ€rel oma praeguste ĂŒlesannete juurde.

Kubernetes'is kaotuste jÀlgimine.

See konteksti vahetus toimub aeglaselt: vÔib-olla viivitus ei olnud mÀrgatav 10-megabitistel vÔrgukaartidel 90ndate aastatel, kuid kaasaegsetel 10G kaartidel, mille maksimaalne lÀbilaskevÔime on 15 miljonit paketti sekundis, vÔib iga vÀikese kaheksatuumalise serveri tuum katkestada miljoneid kordi sekundis.

Et mitte pidevalt katkestus töötlemisega tegeleda, lisati Linuxi mitmeid aastaid tagasi NAPI: vĂ”rguprogrammeerimise API, mida kĂ”ik kaasaegsed draiverid kasutavad kĂ”rgel kiirusel töö efektiivsuse tĂ”stmiseks. Madalatel kiirusel tuum ikka veel aktsepteerib katkestusi vĂ”rkaartilt vana meetodi jĂ€rgi. Kui saabub piisav hulk pakette, mis ĂŒletab kĂŒnnist, lĂŒlitab tuum katkestused vĂ€lja ja hakkab selle asemel vĂ”rguadapterit kĂŒsitlema, vĂ”ttes pakette portfellides. Töötlemine toimub softirq kontekstis, st programmeerimise katkestuste kontekstis pĂ€rast sĂŒsteemikĂ”nesid ja riistvara katkestusi, mil tuum (erinevalt kasutajaruumi) on juba kĂ€ivitatud.

Kubernetes'is kaotuste jÀlgimine.

See on palju kiirem, kuid tekitab teise probleemi. Kui pakette on liiga palju, siis kulub kogu aeg vĂ”rkaartilt pakettide töötlemiseks ja kasutajaruumi protsessid ei suuda neid jĂ€rjekordi tĂ”husalt tĂŒhjendada (nt TCP-ĂŒhendustest lugemine). LĂ”puks tĂ€ituvad jĂ€rjekorrad ja hakkame pakette viskama. Üritades leida tasakaalu, mÀÀrab tuum maksimaalse pakettide töötlemise eelarve softirq kontekstis. Kui see eelarve ĂŒletatakse, Ă€rkab eraldi niit ksoftirqd (te nĂ€ete ĂŒhte neist ps iga tuuma jaoks), mis töötleb neid softirq vĂ€ljaspool tavapĂ€rast sĂŒstemikĂ”ne/katkestuse teed. See niit on planeeritud standardse protsesside planeerijaga, mis ĂŒritab Ă”iglaselt ressursse jaotada.

Kubernetes'is kaotuste jÀlgimine.

Uurides, kuidas tuum pakette töötleb, vĂ”ib mĂ€rgata, et siin on teatud tĂ”enĂ€osus ummikute tekkeks. Kui softirq kĂ”nesid tuleb harva, peavad paketid ootama töötlemist RX jĂ€rjekorras vĂ”rkaartil. VĂ”ib-olla juhtub see seetĂ”ttu, et mĂ”ni ĂŒlesanne blokeerib CPU tuuma vĂ”i midagi muud takistab tuuma softirq kĂ€ivitamast.

VÀhendame töötlemist tuuma vÔi meetodi

softirq viivitused on seni vaid oletus. Kuid see on mÔttekas ja me teame, et meil on midagi vÀga sarnast. Seega jÀrgmine samm on selle teooria kinnitamine. Ja kui see kinnitub, siis leida viivituste pÔhjus.

Naaseme meie aeglaste pakettide juurde:

len=46 ip=172.16.53.32 ttl=61 id=29573 icmp_seq=1953 rtt=99.3 ms

len=46 ip=172.16.53.32 ttl=61 id=29574 icmp_seq=1954 rtt=89.3 ms

len=46 ip=172.16.53.32 ttl=61 id=29575 icmp_seq=1955 rtt=79.2 ms

len=46 ip=172.16.53.32 ttl=61 id=29576 icmp_seq=1956 rtt=69.1 ms

len=46 ip=172.16.53.32 ttl=61 id=29577 icmp_seq=1957 rtt=59.1 ms

len=46 ip=172.16.53.32 ttl=61 id=29790 icmp_seq=2070 rtt=75.7 ms

len=46 ip=172.16.53.32 ttl=61 id=29791 icmp_seq=2071 rtt=65.6 ms

len=46 ip=172.16.53.32 ttl=61 id=29792 icmp_seq=2072 rtt=55.5 ms

Nagu eelnevalt arutatud, hashitakse need ICMP paketid ĂŒhte NIC RX jĂ€rjekorda ja töötleb ĂŒks CPU tuum. Kui soovime mĂ”ista, kuidas Linux töötab, on kasulik teada, kus (millisel CPU tuumal) ja kuidas (softirq, ksoftirqd) neid pakette töödeldakse, et jĂ€lgida protsessi.

NĂŒĂŒd on aeg kasutada tööriistu, mis vĂ”imaldavad reaalajas jĂ€lgida Linuxi tuuma tööd. Siin kasutasime bcc. See tööriistade komplekt vĂ”imaldab kirjutada vĂ€ikeseid C programme, mis haaravad juhuslikud funktsioonid tuumas ja puhverdavad sĂŒndmusi kasutajaruumi Python programmi, mis vĂ”ib neid töödelda ja tulemuse teile tagasi saata. Arbitraarsed funktsioonid tuumas on keeruline teema, kuid utiliit on projekteeritud maksimaalse turvalisuse tagamiseks ja mĂ”eldud jĂ€lgima selliseid tootmisprobleeme, mida ei ole lihtne korrata testimis- vĂ”i arenduskeskkonnas.

Kava on lihtne: me teame, et tuum töötleb neid ICMP pingeid, seega paneme hooki tuuma funktsioonile icmp_echo, mis aktsepteerib sisenenud ICMP-paketti „echo request” ja kĂ€ivitab ICMP vastuse „echo response” saatmise. Saame paketti identifitseerida icmp_seq arvu suurenemise jĂ€rgi, mis nĂ€itab hping3 kĂ”rgem.

Kood bcc skripti vĂ€ljanĂ€gemine vĂ”ib tunduda keeruline, kuid see ei ole nii hirmus, kui tundub. Funktsioon icmp_echo edastab struct sk_buff *skb: see on „echo request” paketiga. Saame seda jĂ€lgida, et vĂ€lja vĂ”tta jada echo.sequence (mis vĂ”rduvad icmp_seq hping3-st ĂŒle), ja saata see kasutajaruumi. Samuti on mugav haarata kĂ€imasoleva protsessi nimi/identifikaator. Allpool on tulemused, mida nĂ€eme otse pakettide töötlemise ajal tuuma poolt:

TGID    PID     PROTSESSI NIMI    ICMP_SEQ
0       0       swapper/11      770
0       0       swapper/11      771
0       0       swapper/11      772
0       0       swapper/11      773
0       0       swapper/11      774
20041   20086   prometheus      775
0       0       swapper/11      776
0       0       swapper/11      777
0       0       swapper/11      778
4512    4542   spokes-report-s  779

Siin on oluline mĂ€rkida, et kontekstis softirq kuvatakse sĂŒsteemikĂ”nesid teinud protsessid kui „protsessid”, kuigi tegelikult töötleb tuum turvaliselt pakette tuuma kontekstis.

Selle tööriistaga saame luua seose konkreetsete protsesside ja konkreetselt pakettide vahel, mis nĂ€itavad viivitust hping3. Teeme lihtsa grep selle saadud vÀÀrtuste korral icmp_seq. Pakette, mis vastavad ĂŒlaltoodud icmp_seq vÀÀrtustele, on jĂ€lgitud koos nende RTT-ga, mida me ĂŒlal vaatlesime (nurgas on Ă€ra toodud oodatud RTT vÀÀrtused pakettide puhul, mille oleme vĂ€listanud etteantud RTT vÀÀrtuste tĂ”ttu, mis on vĂ€iksemad kui 50 ms):

TGID    PID     PROTSESSI NIMI    ICMP_SEQ ** RTT
--
10137   10436   cadvisor        1951
10137   10436   cadvisor        1952
76      76      ksoftirqd/11    1953 ** 99ms
76      76      ksoftirqd/11    1954 ** 89ms
76      76      ksoftirqd/11    1955 ** 79ms
76      76      ksoftirqd/11    1956 ** 69ms
76      76      ksoftirqd/11    1957 ** 59ms
76      76      ksoftirqd/11    1958 ** (49ms)
76      76      ksoftirqd/11    1959 ** (39ms)
76      76      ksoftirqd/11    1960 ** (29ms)
76      76      ksoftirqd/11    1961 ** (19ms)
76      76      ksoftirqd/11    1962 ** (9ms)
--
10137   10436   cadvisor        2068
10137   10436   cadvisor        2069
76      76      ksoftirqd/11    2070 ** 75ms
76      76      ksoftirqd/11    2071 ** 65ms
76      76      ksoftirqd/11    2072 ** 55ms
76      76      ksoftirqd/11    2073 ** (45ms)
76      76      ksoftirqd/11    2074 ** (35ms)
76      76      ksoftirqd/11    2075 ** (25ms)
76      76      ksoftirqd/11    2076 ** (15ms)
76      76      ksoftirqd/11    2077 ** (5ms)

Tulemused rÀÀgivad meile mitmest asjast. Esiteks, kĂ”ik need paketid töödeldakse kontekstis ksoftirqd/11. See tĂ€hendab, et selle spetsiifilise masinate paari puhul hÀÀletatakse ICMP paketid vastuvĂ”tva poole 11. tuumale. Samuti nĂ€eme, et igas ummikus on pakette, mida töödeldakse sĂŒsteemikĂ”nede kontekstis cadvisor. Siis ksoftirqd vĂ”tab ĂŒlesande enda peale ja töötleb kogunenud jĂ€rjekorra: tĂ€pselt nii palju pakette, kui on kogunenud pĂ€rast cadvisor.

TĂ”siasi, et enne seda töötab alati cadvisor, viitab sellele, et ta on probleemi osaline. Ironiseeriv on see, et tema eesmĂ€rk cadvisor on "analĂŒĂŒsida konteinerite ressursikasutust ja jĂ”udluse iseloomu", mitte pĂ”hjustada seda jĂ”udlusprobleemi.

Nagu teiste konteineritööde aspektide puhul, on see kĂ”ik ĂŒlimalt arenenud tööriist, millelt vĂ”ib tĂ”siselt oodata jĂ”udlusprobleeme mĂ”nedes ettenĂ€gematutes oludes.

Mis teeb cadvisor, mis takistab pakettide jÀrjekorda?

NĂŒĂŒd on meil ĂŒsna hea arusaam, kuidas tĂ”rge toimub, milline protsess seda pĂ”hjustab ja millisel CPU-l. NĂ€eme, et jĂ€iga blokeerimise tĂ”ttu ei jĂ”ua Linuxi tuum Ă”igel ajal ĂŒlesandeid ajastada ksoftirqd. Ja me nĂ€eme, et paketid töödeldakse kontekstis cadvisor. Loogiliselt vĂ”iks eeldada, et cadvisor kĂ€ivitab aeglase syscall'i, pĂ€rast mida töödeldakse kĂ”ik kogunenud paketid:

Kubernetes'is kaotuste jÀlgimine.

See on teooria, kuid kuidas seda kontrollida? Mida me saame teha, on jĂ€lgida CPU tuuma tööd kogu selle protsessi vĂ€ltel, leida punkt, kus pakettide arv ĂŒletab eelarvet ja aktiveerib ksoftirqd, ning siis vaadata veidi varem - mis töötas tuumal just enne seda hetke. See on nagu röntgenpilt CPU-st iga paari millisekundi tagant. See nĂ€eb vĂ€lja umbes nii:

Kubernetes'is kaotuste jÀlgimine.

Hea on see, et kogu seda saab teha olemasolevate tööriistadega. NĂ€iteks perf record kontrollib mÀÀratud ajavahemike jĂ€rel antud tuuma CPU-d ja suudab genereerida kĂ€ivitusgraafiku, mis hĂ”lmab nii kasutajaruumi kui ka Linuxi tuuma. Saame selle salvestuse vĂ”tta ja töödelda vĂ€ikese muutmisprogrammi kaudu FlameGraph Brendan Greggilt, mis salvestab hunniku jĂ€lgimise jĂ€rjekorra. Saame salvestada ĂŒhe realise jĂ€lgimise iga 1 ms ja siis eraldada ja salvestada nĂ€idise 100 millisekundi jooksul enne seda, kui jĂ€lgimist tabab ksoftirqd:

# record 999 times a second, or every 1ms with some offset so not to align exactly with timers
sudo perf record -C 11 -g -F 999
# take that recording and make a simpler stack trace.
sudo perf script 2>/dev/null | ./FlameGraph/stackcollapse-perf-ordered.pl | grep ksoftir -B 100

Siin on tulemused:

(sajad jÀljed, mis nÀevad vÀlja sarnased)

cadvisor;[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];entry_SYSCALL_64_after_swapgs;do_syscall_64;sys_read;vfs_read;seq_read;memcg_stat_show;mem_cgroup_nr_lru_pages;mem_cgroup_node_nr_lru_pages cadvisor;[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];entry_SYSCALL_64_after_swapgs;do_syscall_64;sys_read;vfs_read;seq_read;memcg_stat_show;mem_cgroup_nr_lru_pages;mem_cgroup_node_nr_lru_pages cadvisor;[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];entry_SYSCALL_64_after_swapgs;do_syscall_64;sys_read;vfs_read;seq_read;memcg_stat_show;mem_cgroup_iter cadvisor;[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];[cadvisor];entry_SYSCALL_64_after_swapgs;do_syscall_64;sys_read;vfs_read;seq_read;memcg_stat_show;mem_cgroup_nr_lru_pages;mem_cgroup_node_nr_lru_pages ksoftirqd/11;ret_from_fork;kthread;kthread;smpboot_thread_fn;smpboot_thread_fn;run_ksoftirqd;__do_softirq;net_rx_action;ixgbe_poll;ixgbe_clean_rx_irq;napi_gro_receive;netif_receive_skb_internal;inet_gro_receive;bond_handle_frame;__netif_receive_skb_core;ip_rcv_finish;ip_rcv;ip_forward_finish;ip_forward;ip_finish_output;nf_iterate;ip_output;ip_finish_output2;__dev_queue_xmit;dev_hard_start_xmit;ipip_tunnel_xmit;ip_tunnel_xmit;iptunnel_xmit;ip_local_out;dst_output;__ip_local_out;nf_hook_slow;nf_iterate;nf_conntrack_in;generic_packet;ipt_do_table;set_match_v4;ip_set_test;hash_net4_kadt;ixgbe_xmit_frame_ring;swiotlb_dma_mapping_error;hash_net4_test ksoftirqd/11;ret_from_fork;kthread;kthread;smpboot_thread_fn;smpboot_thread_fn;run_ksoftirqd;__do_softirq;net_rx_action;gro_cell_poll;napi_gro_receive;netif_receive_skb_internal;inet_gro_receive;__netif_receive_skb_core;ip_rcv_finish;ip_rcv;ip_forward_finish;ip_forward;ip_finish_output;nf_iterate;ip_output;ip_finish_output2;__dev_queue_xmit;dev_hard_start_xmit;dev_queue_xmit_nit;packet_rcv;tpacket_rcv;sch_direct_xmit;validate_xmit_skb_list;validate_xmit_skb;netif_skb_features;ixgbe_xmit_frame_ring;swiotlb_dma_mapping_error;__dev_queue_xmit;dev_hard_start_xmit;__bpf_prog_run;__bpf_prog_run

Siin on palju, aga peamine, et leiame mustri „cadvisor enne ksoftirqd“, mida oleme varem nĂ€inud ICMP jĂ€lgijas. Mida see tĂ€hendab?

Iga rida on kiiprocessori jĂ€lgimine kindlal ajahetkel. Iga allapoole suunatud kutsumine real eraldatakse semikooloniga. Ridade keskel nĂ€eme kutsutud sĂŒsteهkĂ”nnet: read(): .... ;do_syscall_64;sys_read; .... Seega kulutab cadvisor palju aega sĂŒsteemikutse read(), mis on seotud funktsioonidega mem_cgroup_* (kĂ”rge positsioon kutsumise virnas/lehe lĂ”pp).

Kutsumiste jĂ€lgimisel on ebamugav nĂ€ha, mida tĂ€pselt loetakse, seetĂ”ttu kĂ€ivitame strace ja vaatame, mida cadvisor teeb, ning leiame sĂŒsteemikutse, mis kestab ĂŒle 100 ms:

theojulienne@kube-node-bad ~ $ sudo strace -p 10137 -T -ff 2>&1 | egrep '<0.[1-9]'
[pid 10436] ) = 0
[pid 10432] ) = 0
[pid 10137] ) = 0
[pid 10384] ) = 0
[pid 10436] "cache 154234880nrss 507904nrss_h"..., 4096) = 658
[pid 10384] ) = 0
[pid 10436] ) = 0
[pid 10436] "cache 0nrss 0nrss_huge 0nmapped_"..., 4096) = 577
[pid 10427] "cache 0nrss 0nrss_huge 0nmapped_"..., 4096) = 577
[pid 10411] ) = 0
[pid 10382] ) = 0 (Timeout)
[pid 10436] "cache 154234880nrss 507904nrss_h"..., 4096) = 660
[pid 10417] ) = 0
[pid 10436] ) = 0
[pid 10417] ) = 0
[pid 10417] "cache 0nrss 0nrss_huge 0nmapped_"..., 4096) = 576

Nagu eeldatud, nĂ€eme siin aeglaseid kutseid read(). Lugemisoperatsioonide sisust ja kontekstist mem_cgroup , on selge, et need kutsejad read() on seotud faili memory.stat, mis nĂ€itab mĂ€lu kasutamist ja piiranguid cgroup (Docker'i ressursi isolatsiooni tehnoloogia) jaoks. Cadvisor tööriist kĂŒsib selle faili kohta ressursikasutuse andmeid konteinerite kohta. Vaatame, kas probleem on tuumas vĂ”i teeb cadvisor midagi ootamatut:

theojulienne@kube-node-bad ~ $ time cat /sys/fs/cgroup/memory/memory.stat >/dev/null

reaalne 0m0.153s
kasutaja 0m0.000s
sĂŒsteem 0m0.152s
theojulienne@kube-node-bad ~ $

NĂŒĂŒd saame vea uuesti korrata ja mĂ”istame, et Linuxi tuum seisab silmitsi patoloogiaga.

Mis pÔhjustab lugemise operatsiooni nii aeglaselt?

Selles etapis on palju lihtsam leida teiste kasutajate sĂ”numeid sarnaste probleemide kohta. Selgub, et cadvisor jĂ€lgija oletab, et probleem on liialdases CPU kasutamises, lihtsalt keegi ei pannud tĂ€hele, et viivitus kajastub ka vĂ”rgu stegis. TĂ”epoolest tĂ€heldati, et cadvisor tarbib rohkem protsessorivĂ”imet, kui oodata, kuid sellele ei pööratud suurt tĂ€helepanu, kuna meie serveritel on palju protsessorivĂ”imet, seega ei uuritud probleemi sĂŒvitsi.

Probleem seisneb selles, et kontrollgrupid (cgroups) arvestavad mÀlu kasutamist konteinerite nimiruumi sees. Kui kÔik protsessid selles cgroup'is lÔpetatakse, vabastab Docker mÀlu kontrollgrupi. Siiski ei ole 'mÀlu' lihtsalt protsessi mÀlu. Kuigi protsesside mÀlu ei ole enam kasutusel, osutub, et tuum mÀÀrab endiselt vahemÀlu sisu, nagu dentries ja inodes (kaustade ja failide metaandmed), mis salvestatakse mÀlu cgroup'i. Probleemi kirjelduse kohaselt:

cgroups-zombid: kontrollgrupid, kus pole protsesse ja need on kĂ”rvaldatud, kuid mille jaoks on endiselt eraldatud mĂ€lu (minu puhul dentry vahemĂ€lust, kuid see vĂ”ib olla eraldatud lehekĂŒlgede vĂ”i tmpfs vahemĂ€lust).

Tuum kontrollib kÔiki lehti vahemÀlus cgroup'i vabastamisel, see vÔib olla vÀga aeglane, seetÔttu on valitud laisk protsess: oodata, kuni need lehed taaskÀideldakse, ja alles siis, kui mÀlu on tÔeliselt vajalik, lÔpuks puhastada cgroup. Seni arvestatakse cgroup endiselt statistika kogumisel.

Toimivuse seisukohalt on nad ohvriks toonud mÀlu toimivuse nimel: esialgse puhastamise kiirus, kuna jÀÀb alles natuke vahemÀlu. See on okei. Kui tuum kasutab viimast osa vahemÀlust, puhastatakse cgroup lÔpuks, seega ei saa seda nimetada "lekkeks". Kahjuks viib konkreetne rakendusotsingumehhanism memory.stat selles tuuma versioonis (4.9), koos meie serverite tohutu mÀlumahuga, selleni, et viimaste vahemÀlustatud andmete taastamine ja cgroup-zombide puhastamine vÔtab palju rohkem aega.

Selgub, et mĂ”nes meie sĂ”lmes oli nii palju cgroup-zombisid, et lugemisaeg ja viivitus ĂŒletasid ĂŒhe sekundi.

Probleemi vĂ€ltimise viis cadvisoriga on kohe vabastada dentries/inodes kogu sĂŒsteemis vahemĂ€lu, mis kĂ”rvaldab viivituse lugemisel ja ka vĂ”rgu viivituse hostis, kuna vahemĂ€lu kustutamine hĂ”lmab ka cgroup-zombide vahemĂ€lustatud lehekĂŒlgi, mis saavad samuti vabastatud. See ei ole lahendus, kuid kinnitab probleemi pĂ”hjuse.

Selgus, et uutes tuuma versioonides (4.19+) on kĂ”ne tootlikkus paranenud memory.stat, seega tuumale ĂŒleminek kĂ”rvaldas probleemi. Samal ajal oli meil tööriistad Kubernetes'e klastrite probleemsete sĂ”lmede tuvastamiseks, nende elegantne tĂŒhjendamine ja taaskĂ€ivitamine. LĂ€vitasime kĂ”ik klastrid, leidsime sĂ”lmed koos piisavalt kĂ”rge viivitusega ja taaskĂ€ivitasime need. See andis meile aega operatsioonisĂŒsteemi uuendamiseks ĂŒlejÀÀnud serverites.

KokkuvÔtteks

Kuna see viga peatas NIC RX jĂ€rjekordade töötlemise sadade millisekundite jooksul, pĂ”hjustas see samal ajal suurt viivitust lĂŒhikestes ĂŒhendustes ning keskel, nĂ€iteks MySQL pĂ€ringute ja vastuspakettide vahel.

MĂ”istmine ja toetamine kĂ”ige fundamentaalsemate sĂŒsteemide nagu Kubernetes tootlikkust on ĂŒlioluline, et tagada kĂ”ikide nende pĂ”hjal töötavate teenuste usaldusvÀÀrsus ja kiirus. Kubernetes'e tootlikkuse parandustest saavad kasu kĂ”ik jooksutatud sĂŒsteemid.

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster