Debugging a latenza di a rete in Kubernetes

Debugging a latenza di a rete in Kubernetes

Un paru d'anni fà Kubernetes digià discutitu nantu à u blog ufficiale di GitHub. Da tandu, hè diventata a tecnulugia standard per implementà i servizii. Kubernetes gestisce avà una parte significativa di i servizii interni è publichi. Cume i nostri cluster crescevanu è i requisiti di prestazione sò diventati più stretti, avemu cuminciatu à nutà chì certi servizii nantu à Kubernetes anu spirimintatu sporadicamente una latenza chì ùn puderia micca esse spiegata da a carica di l'applicazione stessa.

Essenzialmente, l'applicazioni sperimentanu una latenza aleatoria di a rete di finu à 100 ms o più, chì si traduce in timeouts o tentativi. I servizii eranu previsti per pudè risponde à e dumande assai più veloce di 100 ms. Ma questu hè impussibile se a cunnessione stessu pigghia tantu tempu. Separatamente, avemu osservatu e dumande di MySQL assai veloci chì duveranu piglià millisecondi, è MySQL hà cumpletu in millisecondi, ma da a perspettiva di l'applicazione richiesta, a risposta hà pigliatu 100 ms o più.

Immediatamente hè diventatu chjaru chì u prublema hè accadutu solu quandu si cunnetta à un node Kubernetes, ancu s'è a chjamata hè vinuta da fora di Kubernetes. A manera più faciule di ripruduce u prublema hè in una prova Vegeta, chì corre da qualsiasi host internu, teste u serviziu Kubernetes in un portu specificu, è sporadicamente registra una alta latenza. In issu articulu, avemu Mulateri Di L'guardà cumu avemu statu capaci di pista falà a causa di stu prublema.

Eliminazione di cumplessità inutile in a catena chì porta à u fallimentu

Riproducendu u listessu esempiu, avemu vulsutu ristrettu u focu di u prublema è caccià strati innecessarii di cumplessità. Inizialmente, ci era troppu elementi in u flussu trà Vegeta è i podi Kubernetes. Per identificà un prublema di rete più profonda, avete bisognu di escludiri alcuni di elli.

Debugging a latenza di a rete in Kubernetes

U cliente (Vegeta) crea una cunnessione TCP cù qualsiasi node in u cluster. Kubernetes opera cum'è una reta overlay (in cima à a reta di u centru di dati esistenti) chì usa IPIP, vale à dì, incapsula i pacchetti IP di a reta overlay in i pacchetti IP di u centru di dati. Quandu si cunnetta à u primu node, a traduzzione di l'indirizzu di a rete hè realizata Traduzzione d'indirizzi di rete (NAT) stateful per traduce l'indirizzu IP è u portu di u node Kubernetes à l'indirizzu IP è u portu in a reta overlay (in particulare, u pod cù l'applicazione). Per i pacchetti in entrata, a sequenza inversa di l'azzioni hè realizata. Hè un sistema cumplessu cù assai statu è parechji elementi chì sò constantemente aghjurnati è cambiati cum'è servizii sò implementati è spustati.

Utilità tcpdump in a prova di Vegeta ci hè un ritardu durante a stretta di mano TCP (trà SYN è SYN-ACK). Per sguassà sta cumplessità inutile, pudete aduprà hping3 per semplici "pings" cù pacchetti SYN. Avemu verificatu s'ellu ci hè un ritardu in u pacchettu di risposta, è poi resettate a cunnessione. Pudemu filtrà e dati per include solu pacchetti più grande di 100 ms è uttene un modu più faciule per riproduce u prublema cà a prova di a reta di a reta 7 di Vegeta. Eccu i "pings" di u nodu Kubernetes chì utilizanu TCP SYN/SYN-ACK nantu à u "portu di u nodu" di u serviziu (30927) à intervalli di 10 ms, filtrati da e risposte più lente:

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

Pudete subitu fà a prima osservazione. A ghjudicà da i numeri di sequenza è i timings, hè chjaru chì ùn sò micca congestioni una volta. U ritardu spessu s'accumula è hè eventualmente processatu.

Dopu, vulemu sapè quale cumpunenti pò esse implicati in l'occurrence di congestioni. Forse queste sò alcune di e centinaie di reguli iptables in NAT? O ci sò prublemi cù u tunneling IPIP in a reta? Una manera di verificà questu hè di pruvà ogni passu di u sistema eliminendu. Chì succede si sguassate a logica NAT è firewall, lascendu solu a parte IPIP:

Debugging a latenza di a rete in Kubernetes

Fortunatamente, Linux facilita l'accessu à a capa di sovrapposizione IP direttamente se a macchina hè nantu à a stessa reta:

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

A ghjudicà da i risultati, u prublema ferma sempre ! Questu esclude iptables è NAT. Allora u prublema hè TCP? Videmu cumu si passa un ping ICMP regulare:

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

I risultati mostranu chì u prublema ùn hè micca andatu. Forse questu hè un tunnel IPIP? Semplicemu ancu a prova:

Debugging a latenza di a rete in Kubernetes

Sò tutti i pacchetti mandati trà sti dui ospiti?

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

Avemu simplificatu a situazione à dui nodi Kubernetes chì si mandanu ogni pacchettu, ancu un ping ICMP. Anu sempre vede a latenza se l'ospitu di destinazione hè "cattivu" (alcuni peggiu cà l'altri).

Avà l'ultima quistione: perchè u ritardu hè solu in i servitori kube-node? È succede quandu kube-node hè u mittente o u receptore? Per furtuna, questu hè ancu abbastanza faciule da capisce mandendu un pacchettu da un òspite fora di Kubernetes, ma cù u stessu destinatariu "cunnisciutu cattivu". Comu pudete vede, u prublema ùn hè micca sparitu:

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

Eseguiremu allora e stesse richieste da a fonte precedente kube-node à l'ospite esternu (chì esclude l'ospite di fonte postu chì u ping include sia un cumpunente RX sia TX):

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

Esaminendu e catture di pacchetti di latenza, avemu ottenutu alcune informazioni supplementari. In particulare, chì u mittente (in fondu) vede stu timeout, ma u destinatariu (in cima) ùn vede micca a colonna Delta (in seconde):

Debugging a latenza di a rete in Kubernetes

Inoltre, se fighjate a diffarenza in l'ordine di i pacchetti TCP è ICMP (per numeri di sequenza) da u latu di u destinatariu, i pacchetti ICMP sempre ghjunghjenu in a listessa sequenza in quale sò stati mandati, ma cù un timing differente. À u listessu tempu, i pacchetti TCP si intreccianu qualchì volta, è alcuni di elli si bloccanu. In particulare, se esaminate i porti di i pacchetti SYN, sò in ordine da u latu di u mittente, ma micca da u latu di u receptore.

Ci hè una sfarenza sottile in cumu carte di rete servitori muderni (cum'è quelli in u nostru centru di dati) processanu pacchetti chì cuntenenu TCP o ICMP. Quandu un pacchettu arriva, l'adattatore di rete "hashes it per connection", vale à dì, prova di rompe e cunnessione in fila è mandà ogni fila à un core di processore separatu. Per TCP, questu hash include l'indirizzu IP è u portu d'origine è di destinazione. In altre parolle, ogni cunnessione hè hashed (potenzialmente) in modu diversu. Per ICMP, solu l'indirizzi IP sò hashed, postu chì ùn ci sò micca porti.

Un'altra nova osservazione: durante stu periodu vedemu ritardi ICMP in tutte e cumunicazioni trà dui ospiti, ma TCP ùn hè micca. Questu ci dice chì a causa hè prubabilmente ligata à l'hashing di fila RX: a congestione hè quasi certamente in u processu di pacchetti RX, micca in l'inviu di risposte.

Questu elimina l'inviu di pacchetti da a lista di e pussibuli cause. Avà sapemu chì u prublema di trasfurmazioni di pacchetti hè nantu à u latu di riceve in certi servitori kube-node.

Capisce u processu di pacchetti in u kernel Linux

Per capisce perchè u prublema si trova à u ricevitore in certi servitori kube-node, fighjemu cumu u kernel Linux processa i pacchetti.

Riturnendu à l'implementazione tradiziunale più simplice, a carta di rete riceve u pacchettu è manda interrompe u kernel Linux chì ci hè un pacchettu chì deve esse processatu. U kernel ferma l'altru travagliu, cambia u cuntestu à u gestore di interruzzione, processa u pacchettu, è poi torna à i travaglii attuali.

Debugging a latenza di a rete in Kubernetes

Questa cunversione di u cuntestu hè lenta: a latenza puderia micca esse notevule nantu à e carte di rete 10Mbps in l'anni '90, ma nantu à e carte 10G muderne cù un rendimentu massimu di 15 milioni di pacchetti per seconda, ogni core di un picculu servitore di ottu core pò esse interrotta milioni. di volte per seconda.

Per ùn trattà constantemente interruzioni, parechji anni fà Linux hà aghjustatu NAPI: API di rete chì tutti i cunduttori muderni utilizanu per migliurà u rendiment à alta velocità. À bassa velocità, u kernel riceve sempre interruzioni da a carta di rete in u vechju modu. Una volta chì arrivanu abbastanza pacchetti chì superanu u sogliu, u kernel disattiva l'interruzioni è invece principia à polling l'adattatore di rete è à coglie i pacchetti in pezzi. U processu hè realizatu in softirq, vale à dì in cuntestu di interruzioni di u software dopu à e chjama di u sistema è l'interruzioni di hardware, quandu u kernel (in uppusizione à u spaziu di l'utilizatori) hè digià in esecuzione.

Debugging a latenza di a rete in Kubernetes

Questu hè assai più veloce, ma causa un prublema diversu. Se ci sò troppu pacchetti, allora tuttu u tempu hè passatu à trasfurmà i pacchetti da a carta di rete, è i prucessi di u spaziu di l'utilizatori ùn anu micca u tempu di sviutata sti file (leghjendu da e cunnessione TCP, etc.). Eventualmente e file si riempianu è cuminciamu à abbandunà i pacchetti. In un tentativu di truvà un equilibriu, u kernel stabilisce un budgetu per u numeru massimu di pacchetti processati in u cuntestu softirq. Una volta stu budgetu hè superatu, un filu separatu hè svegliatu ksoftirqd (ne vedete unu in ps per core) chì gestisce questi softirq fora di u percorsu normale di syscall/interrupt. Stu filu hè pianificatu utilizendu u pianificatore di prucessu standard, chì prova di assignà risorse in modu ghjustu.

Debugging a latenza di a rete in Kubernetes

Dopu avè studiatu cumu u kernel processa i pacchetti, pudete vede chì ci hè una certa probabilità di congestione. Se e chiamate softirq sò ricevute menu freti, i pacchetti duveranu aspittà per qualchì tempu per esse processati in a fila RX nantu à a carta di rete. Questu pò esse dovutu à qualchì compitu chì blocca u core di u processatore, o qualcosa altru impedisce à u core di eseguisce softirq.

Ristrettu u prucessu finu à u core o u metudu

I ritardi di Softirq sò solu una supposizione per ora. Ma hè sensu, è sapemu chì vedemu qualcosa assai simili. Allora u prossimu passu hè di cunfirmà sta teoria. È s'ellu hè cunfirmatu, allora truvà u mutivu di i ritardi.

Riturnemu à i nostri pacchetti lenti:

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

Cum'è discutitu prima, questi pacchetti ICMP sò sbulicati in una sola fila RX NIC è processati da un unicu core CPU. Se vulemu capisce cumu funziona Linux, hè utile sapè induve (in quale core CPU) è cumu (softirq, ksoftirqd) sti pacchetti sò processati per seguità u prucessu.

Avà hè u tempu d'utilizà strumenti chì permettenu di monitorà u kernel Linux in tempu reale. Quì avemu usatu MASSARO. Stu settore di arnesi permette di scrive picculi prugrammi C chì aghjunghjenu funzioni arbitrarie in u kernel è buffer l'avvenimenti in un prugramma Python di u spaziu di l'utilizatori chì ponu processà è rinvià u risultatu à voi. I ganci per e funzioni arbitrarie in u kernel sò una materia cumplessa, ma l'utilità hè pensata per a massima sicurezza è hè pensata per seguità esattamente u tipu di prublemi di produzzione chì ùn sò micca facilmente riproduci in un ambiente di prova o di sviluppu.

U pianu quì hè simplice: sapemu chì u kernel processa questi ping ICMP, cusì metteremu un ganciu nantu à a funzione kernel. icmp_echo, chì accetta un pacchettu di dumanda di eco ICMP in entrata è inizia l'inviu di una risposta di eco ICMP. Pudemu identificà un pacchettu aumentendu u numeru icmp_seq, chì mostra hping3 più altu.

codice script bcc Sembra complicatu, ma ùn hè micca cusì spaventoso cum'è pare. Funzione icmp_echo trasmette struct sk_buff *skb: Questu hè un pacchettu cù una "echo request". Pudemu seguità, tira fora a sequenza echo.sequence (chì si compara cù icmp_seq da hping3 выше), è mandà lu à u spaziu utilizatori. Hè ancu cunvenutu per catturà u nome / id di prucessu attuale. Quì sottu sò i risultati chì vedemu direttamente mentre u kernel processa i pacchetti:

TGID PID PROCESS NAME 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 775 swapper/0 0 11 776 swapper/0 0 11 777 swapper/0 0 11 778 4512 4542 779 swapper/XNUMX XNUMX XNUMX XNUMX raggi-rapportu-s XNUMX

Si deve esse nutatu quì chì in u cuntestu softirq I prucessi chì anu fattu e chjama di u sistema appariscenu cum'è "processi" quandu in fattu hè u kernel chì processa in modu sicuru i pacchetti in u cuntestu di u kernel.

Cù stu strumentu pudemu associà prucessi specifichi cù pacchetti specifichi chì mostranu un ritardu di hping3. Facemu sèmplice grep nantu à sta cattura per certi valori icmp_seq. I pacchetti chì currispondenu à i valori icmp_seq sopra sò stati nutati cù u so RTT chì avemu osservatu sopra (in parentesi sò i valori RTT previsti per i pacchetti chì avemu filtratu per via di i valori RTT menu di 50 ms):

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

I risultati ci dicenu parechje cose. Prima, tutti sti pacchetti sò trattati da u cuntestu ksoftirqd/11. Questu significa chì per questu paru particulari di machini, i pacchetti ICMP sò stati hashed à core 11 à l'estremità di riceve. Avemu vistu ancu chì ogni volta chì ci hè una cunfittura, ci sò pacchetti chì sò processati in u cuntestu di a chjama di u sistema. cadvisor. Dopu ksoftirqd ripiglià u compitu è ​​processa a fila accumulata: esattamente u numeru di pacchetti chì anu accumulatu dopu cadvisor.

U fattu chì immediatamenti prima sempre travaglia cadvisor, implica a so implicazione in u prublema. Irnicamenti, u scopu cadvisor - "analizà l'usu di e risorse è e caratteristiche di rendiment di i cuntenituri in esecuzione" piuttostu cà di causà stu prublema di prestazione.

Cum'è cù l'altri aspetti di i cuntenituri, sò tutti strumenti assai avanzati è ponu esse espertu di prublemi di rendiment in certi circustanze impreviste.

Chì face cadvisor chì rallenta a fila di pacchetti?

Avemu avà una bella cunniscenza di cumu si faci u crash, chì prucessu hè causatu, è nantu à quale CPU. Avemu vistu chì per via di u bloccu duru, u kernel Linux ùn hà micca tempu di pianificà ksoftirqd. È vedemu chì i pacchetti sò processati in u cuntestu cadvisor. Hè logicu per suppone chì cadvisor lancia un syscall lento, dopu chì tutti i pacchetti accumulati in quellu tempu sò processati:

Debugging a latenza di a rete in Kubernetes

Questa hè una teoria, ma cumu pruvà? Ciò chì pudemu fà hè traccia u core di CPU in tuttu stu prucessu, truvate u puntu induve u numeru di pacchetti supera u budgetu è ksoftirqd hè chjamatu, è dopu guardà un pocu più in daretu per vede ciò chì esattamente era in esecuzione nantu à u core CPU appena prima di quellu puntu. . Hè cum'è una radiografia di u CPU ogni pochi millisecondi. Serà qualcosa cusì:

Debugging a latenza di a rete in Kubernetes

Convenientemente, tuttu questu pò esse fattu cù l'arnesi esistenti. Per esempiu, record di perf verifica un core di CPU determinatu à una frequenza specifica è pò generà un calendariu di chjama à u sistema in esecuzione, cumpresu u spaziu di l'utilizatori è u kernel Linux. Pudete piglià stu registru è prucessu cù una piccula furchetta di u prugramma FlameGraph da Brendan Gregg, chì cunserva l'ordine di a traccia di stack. Pudemu salvà tracce di stack in una sola linea ogni 1 ms, è poi mette in risaltu è salve un sample 100 millisecondi prima chì a traccia ghjunghje 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

Eccu i risultati:

(сотни следов, которые выглядят похожими)

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

Ci hè parechje cose quì, ma u principale hè chì truvamu u mudellu "cadvisor before ksoftirqd" chì avemu vistu prima in u tracciatore ICMP. Cosa significa?

Ogni linea hè una traccia di CPU in un certu puntu in u tempu. Ogni chjama à a pila nantu à una linea hè siparata da un puntu e virgola. À mezu à e linii vedemu u syscall chjamatu: read(): .... ;do_syscall_64;sys_read; .... Allora cadvisor passa assai tempu nantu à a chjama di u sistema read()in relazione cù e funzioni mem_cgroup_* (top of call stack/end of line).

Hè sconveniente per vede in una traccia di chjama ciò chì esattamente hè lettu, allora corremu strace è vedemu ciò chì face cadvisor è truvate e chjama di u sistema più longu di 100 ms:

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>

Cum'è puderebbe aspittà, vedemu chjamate lente quì read(). Da u cuntenutu di l'operazioni di lettura è u cuntestu mem_cgroup hè chjaru chì sti sfide read() riferite à u schedariu memory.stat, chì mostra l'usu di memoria è i limiti di cgroup (a tecnulugia di isolamentu di risorse di Docker). L'uttellu di cadvisor interruga stu schedariu per ottene infurmazione di l'usu di risorse per i cuntenituri. Cuntrollamu s'ellu hè u kernel o cadvisor chì face qualcosa inesperu:

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

Avà pudemu ripruduce u bug è capisce chì u kernel Linux face una patologia.

Perchè l'operazione di lettura hè cusì lenta?

À questu stadiu, hè assai più faciule per truvà missaghji da altri utilizatori nantu à prublemi simili. Cum'è hè risultatu, in u cadvisor tracker stu bug hè statu signalatu cum'è prublema di usu eccessivu di CPU, hè solu chì nimu hà nutatu chì a latenza hè ancu riflessa aleatoriamente in a pila di rete. Hè statu veramente nutatu chì u cadvisor cunsumava più tempu di CPU di l'espertu, ma questu ùn era micca datu assai impurtanza, postu chì i nostri servitori anu assai risorse di CPU, cusì u prublema ùn hè statu studiatu currettamente.

U prublema hè chì i cgroups piglianu in contu l'usu di memoria in u spaziu di nomi (container). Quandu tutti i prucessi in questu cgroup esce, Docker libera u cgroup di memoria. Tuttavia, "memoria" ùn hè micca solu memoria di prucessu. Ancu s'è a memoria di u prucessu stessu ùn hè più utilizata, pare chì u kernel assigna ancu cuntenuti in cache, cum'è dentries è inodes (metadata di repertoriu è file), chì sò in cache in u cgroup di memoria. Da a descrizzione di u prublema:

cgroups zombie: cgroups chì ùn anu micca prucessi è sò stati sguassati, ma anu sempre memoria attribuita (in u mo casu, da a cache di dentry, ma pò ancu esse attribuita da a cache di pagina o tmpfs).

A verificazione di u kernel di tutte e pagine in u cache quandu liberate un cgroup pò esse assai lenta, cusì u prucessu lazy hè sceltu: aspettate finu à chì queste pagine sò richieste di novu, è infine sguassate u cgroup quandu a memoria hè veramente necessariu. Finu à questu puntu, cgroup hè sempre cunsideratu quandu cullighjate statistiche.

Da u puntu di vista di u rendiment, anu sacrificatu a memoria per u rendiment: accelerà a pulizia iniziale lascendu una certa memoria cache. Questu hè bè. Quandu u kernel usa l'ultimu di a memoria in cache, u cgroup hè eventualmente sbulicatu, perchè ùn pò micca esse chjamatu "fuga". Sfurtunatamente, l'implementazione specifica di u mecanismu di ricerca memory.stat in questa versione di kernel (4.9), cumminata cù l'immensa quantità di memoria in i nostri servitori, significa chì ci vole assai più tempu per restaurà l'ultime dati in cache è sguassate i zombies cgroup.

Risulta chì alcuni di i nostri nodi avianu tanti zombies cgroup chì a lettura è a latenza superavanu un secondu.

A soluzione per u prublema di cadvisor hè di liberà immediatamente i cache di dentries / inodi in tuttu u sistema, chì elimina immediatamente a latenza di lettura è a latenza di a rete in l'ospitu, postu chì sguassate a cache accende e pagine di cgroup zombie in cache è li libera ancu. Questa ùn hè micca una suluzione, ma cunfirma a causa di u prublema.

Hè risultatu chì in e versioni di kernel più recenti (4.19+) u rendimentu di a chjama hè stata migliurata memory.stat, cusì cambiendu à stu kernel hà risoltu u prublema. À u listessu tempu, avemu avutu strumenti per detectà i nodi problematici in clusters Kubernetes, sguassate cù grazia è riavviate. Avemu pettinatu tutti i clusters, truvamu nodi cù una latenza abbastanza alta è li riavviamu. Questu ci hà datu u tempu per aghjurnà u SO nantu à i servitori rimanenti.

Per sintetizà

Perchè stu bug hà firmatu l'elaborazione di a fila di RX NIC per centinaie di millisecondi, hà causatu simultaneamente una alta latenza in cunnessione curta è una latenza di cunnessione media, cum'è trà e dumande MySQL è i pacchetti di risposta.

Capisce è mantene a prestazione di i sistemi più fundamentali, cum'è Kubernetes, hè critica per l'affidabilità è a rapidità di tutti i servizii basati nantu à elli. Ogni sistema chì eseguite beneficia di i miglioramenti di u rendiment di Kubernetes.

Source: www.habr.com

Add a comment