Kubernetesen sareko latentzia araztea

Kubernetesen sareko latentzia araztea

Duela urte pare bat Kubernetes dagoeneko eztabaidatua GitHub blog ofizialean. Orduz geroztik, zerbitzuak zabaltzeko teknologia estandarra bihurtu da. Kubernetes-ek barne eta zerbitzu publikoen zati garrantzitsu bat kudeatzen du orain. Gure klusterrak hazi ahala eta errendimendu-eskakizunak zorrotzagoak ziren heinean, Kubernetes-en zerbitzu batzuk noizean behin aplikazioaren kargak azaldu ezin zuen latentzia jasaten ari zirela ohartzen hasi ginen.

Funtsean, aplikazioek ausazko sareko latentzia izaten dute 100 ms edo gehiagoko latentzia, eta ondorioz denbora-muga edo berriro saiakerak izaten dira. Zerbitzuek eskaerei 100 ms baino askoz azkarrago erantzuteko gai izango zirela espero zen. Baina hori ezinezkoa da konexioak berak hainbeste denbora hartzen badu. Bereiz, milisegundo hartu behar zuten MySQL kontsulta oso azkarrak ikusi genituen, eta MySQLk milisegundotan osatu zuen, baina eskaera egiten duen aplikazioaren ikuspegitik, erantzunak 100 ms edo gehiago behar zituen.

Berehala argi geratu zen arazoa Kubernetes nodo batera konektatzean bakarrik gertatzen zela, nahiz eta deia Kubernetes kanpotik etorri. Arazoa erreproduzitzeko modurik errazena proba batean da Vegeta, edozein barne ostalaritik exekutatzen dena, Kubernetes zerbitzua portu zehatz batean probatzen du eta noizean behin latentzia handia erregistratzen du. Artikulu honetan, arazo honen kausa nola aurkitu ahal izan genuen aztertuko dugu.

Porrota eragiten duen katean beharrezkoa ez den konplexutasuna ezabatzea

Adibide bera erreproduzituz, arazoaren fokua murriztu eta beharrezkoak ez diren konplexutasun geruzak kendu nahi izan ditugu. Hasieran, elementu gehiegi zeuden Vegeta eta Kubernetes lekaren arteko fluxuan. Sare-arazo sakonagoa identifikatzeko, horietako batzuk baztertu behar dituzu.

Kubernetesen sareko latentzia araztea

Bezeroak (Vegeta) TCP konexio bat sortzen du klusterreko edozein nodorekin. Kubernetes-ek erabiltzen duen gainjartze-sare gisa funtzionatzen du (lehendik dagoen datu-zentroen sarearen gainean). IPIP, hau da, gainjarritako sareko IP paketeak datu-zentroko IP paketeen barruan kapsulatzen ditu. Lehen nodora konektatzean, sareko helbidearen itzulpena egiten da Sareko Helbideen Itzulpena (NAT) Stateful Kubernetes nodoaren IP helbidea eta ataka gainjarri sareko IP helbidera eta atakara itzultzeko (zehazki, aplikazioa duen poda). Sarrerako paketeetarako, ekintzen alderantzizko sekuentzia egiten da. Sistema konplexua da, egoera asko eta elementu asko dituena, zerbitzuak zabaldu eta mugitzen diren heinean etengabe eguneratzen eta aldatzen dena.

Erabilgarritasuna tcpdump Vegeta proban atzerapen bat dago TCP esku-ematean (SYN eta SYN-ACK artean). Alferrikako konplexutasun hori kentzeko, erabil dezakezu hping3 SYN paketeekin "ping" soiletarako. Erantzun paketean atzerapenik dagoen egiaztatzen dugu, eta gero konexioa berrezarri. Datuak iragazi ditzakegu 100 ms baino handiagoak diren paketeak soilik sartzeko eta Vegetaren sare-geruza osoa 7 proba baino arazoa erreproduzitzeko modu errazagoa lortzeko. Hona hemen Kubernetes nodoaren "ping-ak" TCP SYN/SYN-ACK erabiliz zerbitzuko "nodo ataka" (30927) 10 ms-ko tarteetan, erantzun motelenek iragazita:

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

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

Lehen behaketa berehala egin dezake. Sekuentzia-zenbakiak eta denborak ikusita, argi dago ez direla behin-behineko pilaketak. Atzerapena askotan metatzen da eta azkenean prozesatu egiten da.

Jarraian, pilaketak agertzean zein osagai izan daitezkeen jakin nahi dugu. Agian hauek dira NATeko iptables-en ehunka arauetako batzuk? Edo arazorik al dago sarean IPIP tunelarekin? Hau egiaztatzeko modu bat sistemaren urrats bakoitza probatzea da, ezabatuz. Zer gertatzen da NAT eta suebaki logika kentzen badituzu, IPIP zatia bakarrik utziz:

Kubernetesen sareko latentzia araztea

Zorionez, Linuxek IP gainjarri geruza zuzenean sartzea errazten du makina sare berean badago:

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

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

Emaitzak ikusita, arazoak jarraitzen du oraindik! Honek iptables eta NAT baztertzen ditu. Beraz, arazoa TCP al da? Ikus dezagun ICMP ping arrunta nola doan:

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

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

Emaitzek erakusten dute arazoa ez dela desagertu. Agian hau IPIP tunel bat da? Sinplifikatu dezagun proba gehiago:

Kubernetesen sareko latentzia araztea

Bi ostalari hauen artean bidaltzen al dira pakete guztiak?

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

Egoera sinplifikatu dugu Kuberneteseko bi nodotara, edozein pakete elkarri bidaliz, baita ICMP ping bat ere. Oraindik latentzia ikusten dute xede-ostalaria "txarra" bada (batzuk besteak baino okerragoak).

Orain azken galdera: zergatik gertatzen da atzerapena kube-node zerbitzarietan soilik? Eta gertatzen al da kube-node igorlea edo hartzailea denean? Zorionez, hori ere nahiko erraza da Kubernetes-etik kanpoko ostalari batetik pakete bat bidaliz, baina hartzaile "txar ezaguna" berarekin. Ikus dezakezunez, arazoa ez da desagertu:

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

Ondoren, aurreko kube-node iturburutik kanpoko ostalarira eskaera berdinak exekutatuko ditugu (iturburuko ostalaria baztertzen duena, ping-ak RX eta TX osagaiak barne hartzen dituelako):

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 statistic ---
22352 packets transmitted, 22350 packets received, 1% packet loss
round-trip min/avg/max = 0.2/7.6/1010.6 ms

Latentzia-paketeen harrapaketa aztertuta, informazio gehigarri bat lortu dugu. Zehazki, igorleak (behean) denbora-muga hori ikusten duela, baina hartzaileak (goian) ez duela - Delta zutabea ikusten du (segundotan):

Kubernetesen sareko latentzia araztea

Horrez gain, hartzailearen aldean TCP eta ICMP paketeen ordenaren (sekuentzia-zenbakien arabera) desberdintasuna ikusten baduzu, ICMP paketeak beti bidali ziren sekuentzia berean iristen dira, baina denbora ezberdinarekin. Aldi berean, TCP paketeak tartekatzen dira batzuetan, eta horietako batzuk trabatu egiten dira. Bereziki, SYN paketeen atakak aztertzen badituzu, ordenatuta daude igorlearen aldean, baina ez hartzailearen aldean.

Ezberdintasun sotil bat dago nola sare-txartelak zerbitzari modernoek (gure datu-zentrokoek bezala) TCP edo ICMP duten paketeak prozesatzen dituzte. Pakete bat iristen denean, sare-egokitzaileak "hashes egiten du konexio bakoitzeko", hau da, konexioak ilaretan apurtzen eta ilara bakoitza prozesadore-nukleo batera bidaltzen saiatzen da. TCPrako, hash honek iturburuko eta helmugako IP helbidea eta ataka barne hartzen ditu. Beste era batera esanda, konexio bakoitza hash (potentzialki) ezberdina da. ICMP-rako, IP helbideak soilik hashatzen dira, ez baitago atakarik.

Beste behaketa berri bat: aldi honetan ICMP atzerapenak ikusten ditugu bi ostalarien arteko komunikazio guztietan, baina TCPk ez. Honek esaten digu kausa RX ilararen hashingarekin erlazionatuta dagoela: pilaketak RX paketeen prozesamenduan dago ia ziur, ez erantzunak bidaltzean.

Honek kausa posibleen zerrendatik paketeak bidaltzea ezabatzen du. Orain badakigu paketeen prozesatzeko arazoa kube-node zerbitzari batzuetan jasotzeko aldean dagoela.

Linux nukleoan paketeen prozesamendua ulertzea

Kube-node zerbitzari batzuetako hargailuan arazoa zergatik gertatzen den ulertzeko, ikus dezagun nola prozesatzen dituen Linux nukleoak paketeak.

Inplementazio tradizional errazenera itzuliz, sare-txartelak paketea jasotzen du eta bidaltzen du eten Linux nukleoa prozesatu behar den pakete bat dagoela. Nukleoak beste lan batzuk geldiarazten ditu, testuingurua eten kudeatzailera aldatzen du, paketea prozesatzen du eta, ondoren, uneko zereginetara itzultzen da.

Kubernetesen sareko latentzia araztea

Testuinguru aldaketa hau motela da: baliteke latentzia ez izatea 10eko hamarkadan 90 Mbps sareko txarteletan nabaritzea, baina segundoko 10 milioi pakete gehienez 15 milioi pakete duten XNUMXG txartel modernoetan, zortzi nukleoko zerbitzari txiki baten nukleo bakoitza milioika eten daiteke. segundoko aldiz.

Etenaldiak etengabe kudeatzeko, duela urte asko Linux gehitu zuen NAPI: Gidari moderno guztiek abiadura handiko errendimendua hobetzeko erabiltzen duten sareko APIa. Abiadura baxuetan kernelak modu zaharrean sare-txarteletik etenaldiak jasotzen ditu oraindik. Atalasea gainditzen duten pakete nahikoa iristen direnean, nukleoak etenaldiak desgaitzen ditu eta horren ordez sare-egokitzaileari galdetzen eta paketeak zatika jasotzen hasten da. Prozesatzea softirq-en egiten da, hau da, in software-etenen testuingurua sistema-deien eta hardware-etenen ondoren, nukleoa (erabiltzaile-espazioa ez bezala) dagoeneko martxan dagoenean.

Kubernetesen sareko latentzia araztea

Hau askoz azkarragoa da, baina beste arazo bat eragiten du. Pakete gehiegi badira, denbora guztia sare-txarteletik paketeak prozesatzen igarotzen da, eta erabiltzaile-espazio-prozesuek ez dute ilara horiek benetan husteko denborarik (TCP konexioetatik irakurtzen, etab.). Azkenean ilarak betetzen dira eta paketeak jaisten hasten gara. Oreka aurkitu nahian, nukleoak aurrekontua ezartzen du softirq testuinguruan prozesatutako pakete kopuru maximorako. Aurrekontu hori gaindituta, aparteko hari bat pizten da ksoftirqd (Horietako bat ikusiko duzu ps nukleo bakoitzeko) softirq hauek kudeatzen dituen syscall/eten bide arruntetik kanpo. Hari hau prozesuen programatzaile estandarra erabiliz programatzen da, baliabideak modu egokian esleitzen saiatzen dena.

Kubernetesen sareko latentzia araztea

Nukleoak paketeak nola prozesatzen dituen aztertuta, pilaketak izateko probabilitate jakin bat dagoela ikus dezakezu. Softirq deiak maizago jasotzen badira, paketeak denbora pixka bat itxaron beharko dute sare-txarteleko RX ilaran prozesatzeko. Baliteke prozesadorearen nukleoa blokeatzen duen atazaren batek edo beste zerbaitek nukleoak softirq exekutatzeko eragozten duelako.

Prozesamendua muinera edo metodora murriztea

Softirq-en atzerapenak asmakizun bat besterik ez dira oraingoz. Baina zentzuzkoa da, eta badakigu oso antzeko zerbait ikusten ari garela. Beraz, hurrengo urratsa teoria hau berrestea da. Eta baieztatzen bada, aurkitu atzerapenen arrazoia.

Itzuli gaitezen gure pakete geldoetara:

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

Lehenago esan bezala, ICMP pakete hauek RX NIC ilara bakarrean sartzen dira eta CPU nukleo bakar batek prozesatzen ditu. Linux-ek nola funtzionatzen duen ulertu nahi badugu, pakete hauek non (zein CPU nukleotan) eta nola (softirq, ksoftirqd) prozesatzen diren jakitea komeni da prozesuaren jarraipena egiteko.

Orain denbora errealean Linux nukleoa kontrolatzeko aukera ematen duten tresnak erabiltzeko garaia da. Hemen erabili dugu bcc. Tresna multzo honek nukleoan funtzio arbitrarioak lotzen dituzten C programa txikiak idazteko aukera ematen du eta gertaerak prozesatu eta emaitza itzul diezazukeen erabiltzaile-espazioko Python programa batean gordetzeko aukera ematen du. Nukleoan funtzio arbitrarioak lotzea gai konplexua da, baina erabilgarritasuna segurtasun handiena lortzeko diseinatuta dago eta proba edo garapen ingurune batean erraz erreproduzitzen ez diren ekoizpen-arazoak zehatz-mehatz jarraitzeko diseinatuta dago.

Hemen plana erraza da: badakigu nukleoak ICMP ping hauek prozesatzen dituela, beraz, nukleoaren funtzioari amu bat jarriko diogu. icmp_oihartzuna, sarrerako ICMP oihartzun eskaera pakete bat onartzen duena eta ICMP oihartzun erantzuna bidaltzen hasten duena. Pakete bat identifika dezakegu icmp_seq zenbakia handituz, erakusten duena hping3 handiagorik.

Code bcc gidoia konplexua dirudi, baina ez da dirudien bezain beldurgarria. Funtzioa icmp_echo transmititzen du struct sk_buff *skb: "oihartzun eskaera" duen pakete bat da. Jarraitu dezakegu, sekuentzia atera echo.sequence (ekin alderatzen duena icmp_seq hping3-ren eskutik Π²Ρ‹ΡˆΠ΅), eta bidali erabiltzailearen espaziora. Erosoa da uneko prozesuaren izena/id harrapatzea ere. Hona hemen nukleoak paketeak prozesatzen dituen bitartean zuzenean ikusten ditugun emaitzak:

TGID PID PROZESUA IZENA 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 swapper/775 0 0 11 swapper/776 0 0 11 swapper 777 0 0 11 trukatu/778 4512 4542 779 erradio-txostena XNUMX

Kontuan izan behar da hemen testuinguruan softirq sistema-deiak egin zituzten prozesuak "prozesu" gisa agertuko dira, kernelaren testuinguruan paketeak segurtasunez prozesatzen dituen nukleoa denean.

Tresna honekin prozesu zehatzak atzerapena erakusten duten pakete zehatzekin lotu ditzakegu hping3. Egin dezagun erraza grep balio jakin batzuen harrapaketa honen gainean icmp_seq. Goiko icmp_seq balioekin bat datozen paketeak goian ikusi ditugun RTTrekin batera adierazi dira (parentesi artean 50 ms baino gutxiagoko RTT balioengatik iragazi ditugun paketeetarako espero diren RTT balioak daude):

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

Emaitzek hainbat gauza esaten dizkigute. Lehenik eta behin, pakete horiek guztiak testuinguruaren arabera prozesatzen dira ksoftirqd/11. Horrek esan nahi du makina pare zehatz honetarako, ICMP paketeak 11 nukleoan haztatu zirela hartzailearen amaieran. Era berean, ikusten dugu jam bat dagoen bakoitzean sistema-deiaren testuinguruan prozesatzen diren paketeak daudela. cadvisor. Ondoren ksoftirqd zeregina hartzen du eta metatutako ilara prozesatzen du: zehatz-mehatz pilatutako pakete kopurua ondoren cadvisor.

Izan ere, berehala aurretik beti funtzionatzen duela cadvisor, bere arazoan parte hartzea dakar. Ironikoki, helburua kadvisor - "Aztertu exekutatzen ari diren edukiontzien baliabideen erabilera eta errendimendu-ezaugarriak" errendimendu-arazo hau eragin beharrean.

Edukiontzien beste alderdi batzuekin gertatzen den bezala, tresna oso aurreratuak dira eta errendimendu arazoak izango dituztela espero daiteke ezusteko egoera batzuetan.

Zer egiten du cadvisor-ek pakete-ilara moteltzen duenik?

Orain nahiko ondo ulertzen dugu kraskadura nola gertatzen den, zer prozesu eragiten duen eta zein CPUtan. Blokeo gogorra dela eta, Linux nukleoak ez duela programatzeko denborarik ikusten dugu ksoftirqd. Eta paketeak testuinguruan prozesatzen direla ikusten dugu cadvisor. Logikoa da hori suposatzea cadvisor syscall motela abiarazten du, eta, ondoren, une horretan pilatutako pakete guztiak prozesatzen dira:

Kubernetesen sareko latentzia araztea

Hau teoria bat da, baina nola probatu? Egin dezakeguna da prozesu honetan zehar PUZaren nukleoa trazatzea, pakete kopurua aurrekontua gainditzen duen eta ksoftirqd deitzen den puntua aurkitzea eta, gero, atzera pixka bat atzera begiratu, puntu hori baino lehen PUZaren nukleoan zer exekutatzen ari zen ikusteko. . PUZa milisegundo batzuetan X izpiak egitea bezalakoa da. Honelako itxura izango du:

Kubernetesen sareko latentzia araztea

Eroso, hori guztia lehendik dauden tresnekin egin daiteke. Adibidez, perf errekorra PUZaren nukleo jakin bat maiztasun zehatz batean egiaztatzen du eta exekutatzen ari den sistemarako deien egutegia sor dezake, erabiltzaileen espazioa eta Linux nukleoa barne. Disko hau hartu eta prozesatu dezakezu programaren sardexka txiki bat erabiliz FlameGraph Brendan Gregg-en eskutik, pilaren arrastoaren ordena gordetzen duena. Lerro bakarreko pila-aztarnak gorde ditzakegu 1 ms behin, eta, ondoren, lagin bat nabarmendu eta gorde 100 milisegundo arrastoa iritsi baino lehen 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

Hona hemen emaitzak:

(сотни слСдов, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ выглядят ΠΏΠΎΡ…ΠΎΠΆΠΈΠΌΠΈ)

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 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

Hemen gauza asko daude, baina gauza nagusia da "cadvisor aurretik ksoftirqd" eredua aurkitzen dugula lehenago ICMP trazatzailean. Zer esan nahi du?

Lerro bakoitza PUZaren arrastoa da une jakin batean. Lerro bateko pilatik beherako dei bakoitza puntu eta komaz bereizten da. Lerroen erdian syscall izenekoa ikusten dugu: read(): .... ;do_syscall_64;sys_read; .... Beraz, cadvisor-ek denbora asko ematen du sistema-deian read()funtzioekin lotutakoak mem_cgroup_* (deien pilaren goiko/lerroaren amaiera).

Deserosoa da dei-arlo batean zer irakurtzen den zehazki ikustea, beraz, korrika egin dezagun strace eta ikus ditzagun cadvisor-ek zer egiten duen eta aurki ditzagun 100 ms baino luzeagoak diren sistema-deiak:

theojulienne@kube-node-bad ~ $ sudo strace -p 10137 -T -ff 2>&1 | egrep '<0.[1-9]'
[pid 10436] <... futex resumed> ) = 0 <0.156784>
[pid 10432] <... futex resumed> ) = 0 <0.258285>
[pid 10137] <... futex resumed> ) = 0 <0.678382>
[pid 10384] <... futex resumed> ) = 0 <0.762328>
[pid 10436] <... read resumed> "cache 154234880nrss 507904nrss_h"..., 4096) = 658 <0.179438>
[pid 10384] <... futex resumed> ) = 0 <0.104614>
[pid 10436] <... futex resumed> ) = 0 <0.175936>
[pid 10436] <... read resumed> "cache 0nrss 0nrss_huge 0nmapped_"..., 4096) = 577 <0.228091>
[pid 10427] <... read resumed> "cache 0nrss 0nrss_huge 0nmapped_"..., 4096) = 577 <0.207334>
[pid 10411] <... epoll_ctl resumed> ) = 0 <0.118113>
[pid 10382] <... pselect6 resumed> ) = 0 (Timeout) <0.117717>
[pid 10436] <... read resumed> "cache 154234880nrss 507904nrss_h"..., 4096) = 660 <0.159891>
[pid 10417] <... futex resumed> ) = 0 <0.917495>
[pid 10436] <... futex resumed> ) = 0 <0.208172>
[pid 10417] <... futex resumed> ) = 0 <0.190763>
[pid 10417] <... read resumed> "cache 0nrss 0nrss_huge 0nmapped_"..., 4096) = 576 <0.154442>

Espero zenuten bezala, dei motelak ikusten ditugu hemen read(). Irakurketa eragiketen edukietatik eta testuingurutik mem_cgroup argi dago erronka horiek read() fitxategira jo memory.stat, memoriaren erabilera eta cgroup mugak erakusten dituena (Dockerren baliabideak isolatzeko teknologia). Cadvisor tresnak fitxategi hau kontsultatzen du edukiontzietarako baliabideen erabilerari buruzko informazioa lortzeko. Egiaztatu dezagun nukleoa edo cadvisor den ustekabeko zerbait egiten ari den:

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

real 0m0.153s
user 0m0.000s
sys 0m0.152s
theojulienne@kube-node-bad ~ $

Orain akatsa erreproduzitu dezakegu eta Linux nukleoa patologia baten aurrean dagoela uler dezakegu.

Zergatik da hain motela irakurtzeko eragiketa?

Fase honetan, askoz errazagoa da antzeko arazoei buruzko beste erabiltzaileen mezuak aurkitzea. Gertatu zenez, cadvisor tracker-en akats hau honela jakinarazi zen CPU gehiegi erabiltzearen arazoa, besterik ez da inork ez zuela ohartu latentzia sareko pilan ere ausaz islatzen dela. Izan ere, ohartu zen cadvisor-ek espero baino CPU denbora gehiago kontsumitzen zuela, baina honi ez zitzaion garrantzi handirik eman, gure zerbitzariek CPU baliabide asko baitituzte, beraz, arazoa ez zen arretaz aztertu.

Arazoa da cgroups-ek izen-eremuaren (edukiontzi) barruan dagoen memoriaren erabilera kontuan hartzen duela. Cgroup honetako prozesu guztiak irteten direnean, Docker-ek memoria cgroup askatzen du. Hala ere, "memoria" ez da prozesu memoria soilik. Prozesu-memoria bera jada erabiltzen ez den arren, badirudi nukleoak oraindik ere cache-ko edukiak esleitzen dituela, hala nola dentries eta inodeak (direktorio eta fitxategi metadatuak), memoriaren cgroup-ean gordetzen direnak. Arazoaren deskribapenetik:

zonbi cgroups: prozesurik ez duten eta ezabatu diren cgroups, baina oraindik memoria esleituta daukatenak (nire kasuan, dentry cachetik, baina orrialdeko cachetik edo tmpfs-tik ere esleitu daiteke).

Cgroup bat askatzean nukleoaren cacheko orrialde guztien egiaztapena oso motela izan daiteke, beraz, prozesu alferra aukeratzen da: itxaron orrialde hauek berriro eskatu arte, eta, azkenik, garbitu cgroup memoria benetan behar denean. Une honetara arte, cgroup oraindik ere kontuan hartzen da estatistikak biltzeko orduan.

Errendimenduaren ikuspuntutik, memoria sakrifikatu zuten errendimendurako: hasierako garbiketa bizkortu, cacheko memoriaren bat atzean utzita. Hau ondo dago. Nukleoak cache-ko memoriaren azkena erabiltzen duenean, azkenean cgroup garbitzen da, beraz, ezin zaio "filtrazio" deitu. Zoritxarrez, bilaketa-mekanismoaren ezarpen zehatza memory.stat nukleoaren bertsio honetan (4.9), gure zerbitzarietako memoria kopuru handiarekin konbinatuta, askoz ere denbora gehiago behar da cacheko azken datuak berreskuratzeko eta cgroup zonbiak garbitzeko.

Ematen du gure nodo batzuek hainbeste cgroup zonbi zituztela irakurketa eta latentzia segundo bat gainditzen zutela.

Cadvisor arazoaren konponbidea sistema osoan dentries/inodoen cacheak berehala askatzea da, eta horrek berehala ezabatzen du irakurketa latentzia eta sarearen latentzia ostalarian, cachea garbitzeak cacheko zonbi cgroup orrialdeak pizten ditu eta haiek ere askatzen baititu. Hau ez da irtenbide bat, baina arazoaren kausa baieztatzen du.

Kernel bertsio berriagoetan (4.19+) deien errendimendua hobetu zen memory.stat, beraz, nukleo honetara aldatzeak arazoa konpondu du. Aldi berean, Kubernetes klusterretan nodo problematikoak detektatzeko tresnak genituen, dotorez hustu eta berrabiarazi. Kluster guztiak orraztu, nahikoa latentzia handiko nodoak aurkitu eta berrabiarazi genituen. Horrek denbora eman zigun gainerako zerbitzarietan OS eguneratzeko.

Laburbilduz

Akats honek RX NIC ilararen prozesamendua ehunka milisegundotan geldiarazi zuenez, aldi berean latentzia handia eragin zuen konexio laburretan eta konexio erdiko latentzia, esate baterako, MySQL eskaeren eta erantzun-paketeen artean.

Sistema oinarrizkoenen errendimendua ulertzea eta mantentzea, hala nola Kubernetes, funtsezkoa da horietan oinarritutako zerbitzu guztien fidagarritasuna eta abiadura lortzeko. Exekutatzen dituzun sistema bakoitzak Kubernetesen errendimenduaren hobekuntzak lortzen ditu.

Iturria: www.habr.com

Gehitu iruzkin berria