Tīkla latentuma atkļūdoŔana pakalpojumā Kubernetes

Tīkla latentuma atkļūdoŔana pakalpojumā Kubernetes

Pirms pāris gadiem Kubernetes jau apspriests oficiālajā GitHub emuārā. KopÅ” tā laika tā ir kļuvusi par standarta tehnoloÄ£iju pakalpojumu izvietoÅ”anai. Tagad Kubernetes pārvalda ievērojamu daļu iekŔējo un sabiedrisko pakalpojumu. Pieaugot mÅ«su klasteriem un kļūstot stingrākām veiktspējas prasÄ«bām, mēs sākām pamanÄ«t, ka daži Kubernetes pakalpojumi sporādiski piedzÄ«vo latentumu, ko nevarēja izskaidrot ar paÅ”as lietojumprogrammas slodzi.

BÅ«tÄ«bā lietojumprogrammas saskaras ar Ŕķietami nejauÅ”u tÄ«kla latentumu lÄ«dz 100 ms vai vairāk, kā rezultātā rodas noildze vai atkārtojumi. Paredzams, ka pakalpojumi spēs atbildēt uz pieprasÄ«jumiem daudz ātrāk nekā 100 ms. Bet tas nav iespējams, ja pats savienojums aizņem tik daudz laika. AtseviŔķi mēs novērojām ļoti ātrus MySQL vaicājumus, kam vajadzēja aizņemt milisekundes, un MySQL pabeidza milisekundēs, taču no pieprasÄ«juma iesniedzējas lietojumprogrammas viedokļa atbilde aizņēma 100 ms vai vairāk.

Uzreiz kļuva skaidrs, ka problēma radās tikai savienojoties ar Kubernetes mezglu, pat ja zvans nāca no ārpuses Kubernetes. VienkārŔākais veids, kā atkārtot problēmu, ir tests veÄ£etēt, kas darbojas no jebkura iekŔējā resursdatora, pārbauda Kubernetes pakalpojumu noteiktā portā un sporādiski reÄ£istrē lielu latentumu. Å ajā rakstā mēs apskatÄ«sim, kā mēs varējām izsekot Ŕīs problēmas cēloni.

NevajadzÄ«gas sarežģītÄ«bas novērÅ”ana ķēdē, kas noved pie neveiksmes

Atkārtojot to paÅ”u piemēru, mēs vēlējāmies saÅ”aurināt problēmas fokusu un noņemt nevajadzÄ«gus sarežģītÄ«bas slāņus. Sākotnēji plÅ«smā starp VeÄ£etu un Kubernetes pākstÄ«m bija pārāk daudz elementu. Lai noteiktu dziļāku tÄ«kla problēmu, dažas no tām ir jāizslēdz.

Tīkla latentuma atkļūdoŔana pakalpojumā Kubernetes

Klients (Vegeta) izveido TCP savienojumu ar jebkuru klastera mezglu. Kubernetes darbojas kā pārklājuma tÄ«kls (papildus esoÅ”ajam datu centra tÄ«klam), kas izmanto IPIP, tas ir, tas iekapsulē pārklājuma tÄ«kla IP paketes datu centra IP paketēs. Pieslēdzoties pirmajam mezglam, tiek veikta tÄ«kla adreses tulkoÅ”ana TÄ«kla adreses tulkoÅ”ana (NAT) statusful lai tulkotu Kubernetes mezgla IP adresi un portu uz IP adresi un portu pārklājuma tÄ«klā (konkrēti, podā ar lietojumprogrammu). IenākoÅ”ajām paketēm tiek veikta apgrieztā darbÄ«bu secÄ«ba. Tā ir sarežģīta sistēma ar daudziem stāvokļiem un daudziem elementiem, kas tiek pastāvÄ«gi atjaunināti un mainÄ«ti, izvietojot un pārvietojot pakalpojumus.

LietderÄ«ba tcpdump Vegeta testā ir aizkave TCP rokasspiediena laikā (starp SYN un SYN-ACK). Lai noņemtu Å”o nevajadzÄ«go sarežģītÄ«bu, varat izmantot hping3 vienkārÅ”iem ā€œpingiemā€ ar SYN paketēm. Mēs pārbaudām, vai atbildes paketē ir aizkave, un pēc tam atiestatām savienojumu. Mēs varam filtrēt datus, lai iekļautu tikai paketes, kas ir lielākas par 100 ms, un iegÅ«t vienkārŔāku veidu, kā reproducēt problēmu nekā pilna tÄ«kla 7. slāņa pārbaude pakalpojumā Vegeta. Å eit ir Kubernetes mezgla ā€œpingsā€, izmantojot TCP SYN/SYN-ACK pakalpojuma ā€œnode portāā€ (30927) ar 10 ms intervālu, filtrējot pēc lēnākās atbildes:

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

Var uzreiz veikt pirmo novērojumu. Spriežot pēc kārtas numuriem un laikiem, ir skaidrs, ka tie nav vienreizēji sastrēgumi. KavÄ“Å”anās bieži uzkrājas un galu galā tiek apstrādāta.

Tālāk mēs vēlamies noskaidrot, kuras sastāvdaļas var bÅ«t saistÄ«tas ar sastrēgumu raÅ”anos. VarbÅ«t Å”ie ir daži no simtiem NAT iptables noteikumu? Vai arÄ« ir problēmas ar IPIP tunelÄ“Å”anu tÄ«klā? Viens no veidiem, kā to pārbaudÄ«t, ir pārbaudÄ«t katru sistēmas posmu, to novērÅ”ot. Kas notiek, ja noņemat NAT un ugunsmÅ«ra loÄ£iku, atstājot tikai IPIP daļu:

Tīkla latentuma atkļūdoŔana pakalpojumā Kubernetes

Par laimi, Linux ļauj viegli piekļūt tieŔi IP pārklājuma slānim, ja iekārta atrodas tajā paŔā tīklā:

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

Spriežot pēc rezultātiem, problēma joprojām pastāv! Tas izslēdz iptables un NAT. Tātad problēma ir TCP? Apskatīsim, kā notiek parastais ICMP ping:

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

Rezultāti liecina, ka problēma nav pazudusi. VarbÅ«t tas ir IPIP tunelis? VienkārÅ”osim testu vēl vairāk:

Tīkla latentuma atkļūdoŔana pakalpojumā Kubernetes

Vai visas paketes tiek nosūtītas starp Ŕiem diviem saimniekiem?

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

Mēs esam vienkārÅ”ojuÅ”i situāciju lÄ«dz diviem Kubernetes mezgliem, kas viens otram nosÅ«ta jebkuru paketi, pat ICMP ping. Viņi joprojām redz latentumu, ja mērÄ·a resursdators ir "slikts" (daži sliktāki par citiem).

Tagad pēdējais jautājums: kāpēc aizkave notiek tikai kube-node serveros? Un vai tas notiek, ja kube-node ir sÅ«tÄ«tājs vai saņēmējs? Par laimi, to ir arÄ« diezgan viegli noskaidrot, nosÅ«tot paketi no resursdatora ārpus Kubernetes, bet ar to paÅ”u ā€œzināmo sliktuā€ adresātu. Kā redzat, problēma nav pazudusi:

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

Pēc tam mēs izpildÄ«sim tos paÅ”us pieprasÄ«jumus no iepriekŔējā avota kube-node ārējam resursdatoram (kas izslēdz avota resursdatoru, jo ping ietver gan RX, gan TX komponentu):

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

Pārbaudot latentuma pakeÅ”u uztverÅ”anu, mēs ieguvām papildu informāciju. Konkrēti, ja sÅ«tÄ«tājs (apakŔā) redz Å”o taimautu, bet adresāts (augŔējais) neredz - skatiet kolonnu Delta (sekundēs):

Tīkla latentuma atkļūdoŔana pakalpojumā Kubernetes

Turklāt, ja paskatās uz atŔķirÄ«bu TCP un ICMP pakeÅ”u secÄ«bā (pēc kārtas numuriem) saņēmēja pusē, ICMP paketes vienmēr nonāk tajā paŔā secÄ«bā, kādā tās tika nosÅ«tÄ«tas, bet ar atŔķirÄ«gu laiku. Tajā paŔā laikā TCP paketes dažkārt pāriet, un dažas no tām iestrēgst. Jo Ä«paÅ”i, ja pārbaudāt SYN pakeÅ”u portus, tie ir kārtÄ«bā sÅ«tÄ«tāja pusē, bet ne saņēmēja pusē.

Ir neliela atŔķirÄ«ba, kā tÄ«kla kartes mÅ«sdienu serveri (piemēram, tie, kas atrodas mÅ«su datu centrā) apstrādā paketes, kas satur TCP vai ICMP. Kad tiek saņemta pakete, tÄ«kla adapteris to "jauc katrā savienojumā", tas ir, mēģina sadalÄ«t savienojumus rindās un nosÅ«tÄ«t katru rindu uz atseviŔķu procesora kodolu. TCP gadÄ«jumā Å”is hash ietver gan avota, gan galamērÄ·a IP adresi un portu. Citiem vārdiem sakot, katrs savienojums tiek sajaukts (potenciāli) atŔķirÄ«gi. ICMP gadÄ«jumā tiek jauktas tikai IP adreses, jo nav portu.

Vēl viens jauns novērojums: Å”ajā periodā mēs redzam ICMP aizkavi visos sakaros starp diviem resursdatoriem, bet TCP ne. Tas norāda, ka iemesls, visticamāk, ir saistÄ«ts ar RX rindas jaukÅ”anu: pārslodze gandrÄ«z noteikti ir saistÄ«ta ar RX pakeÅ”u apstrādi, nevis atbilžu nosÅ«tÄ«Å”anu.

Tas izslēdz pakeÅ”u sÅ«tÄ«Å”anu no iespējamo iemeslu saraksta. Tagad mēs zinām, ka pakeÅ”u apstrādes problēma dažos Kube-node serveros ir uztverÅ”anas pusē.

Izpratne par pakeŔu apstrādi Linux kodolā

Lai saprastu, kāpēc problēma rodas dažu kube mezglu serveru uztvērējā, apskatīsim, kā Linux kodols apstrādā paketes.

Atgriežoties pie vienkārŔākās tradicionālās ievieÅ”anas, tÄ«kla karte saņem paketi un nosÅ«ta pārtraukt Linux kodolu, ka ir pakotne, kas ir jāapstrādā. Kodols aptur citu darbu, pārslēdz kontekstu uz pārtraukumu apstrādātāju, apstrādā paketi un pēc tam atgriežas pie paÅ”reizējiem uzdevumiem.

Tīkla latentuma atkļūdoŔana pakalpojumā Kubernetes

Å Ä« konteksta pārslēgÅ”ana ir lēna: latentums, iespējams, nebija pamanāms 10 Mbps tÄ«kla kartēs 90. gados, taču mÅ«sdienu 10 G kartēs ar maksimālo caurlaidspēju 15 miljoni pakeÅ”u sekundē katrs neliela astoņu kodolu servera kodols var tikt pārtraukts miljoniem. reizes sekundē.

Lai nepārtraukti netiktu apstrādāti pārtraukumi, pirms daudziem gadiem pievienoja Linux NAPI: tÄ«kla API, ko izmanto visi mÅ«sdienu draiveri, lai uzlabotu veiktspēju lielā ātrumā. Pie maziem ātrumiem kodols joprojām saņem pārtraukumus no tÄ«kla kartes vecajā veidā. Kad tiek saņemts pietiekami daudz pakeÅ”u, kas pārsniedz slieksni, kodols atspējo pārtraukumus un tā vietā sāk aptaujāt tÄ«kla adapteri un savākt paketes gabalos. Apstrāde tiek veikta softirq, tas ir, in programmatÅ«ras pārtraukumu konteksts pēc sistēmas izsaukumiem un aparatÅ«ras pārtraukumiem, kad kodols (pretēji lietotāja vietai) jau darbojas.

Tīkla latentuma atkļūdoŔana pakalpojumā Kubernetes

Tas ir daudz ātrāk, taču rada citu problēmu. Ja pakeÅ”u ir par daudz, tad viss laiks tiek tērēts pakeÅ”u apstrādei no tÄ«kla kartes, un lietotāja telpas procesiem nav laika Ŕīs rindas reāli iztukÅ”ot (nolasot no TCP savienojumiem utt.). Galu galā rindas piepildās, un mēs sākam mest paciņas. Mēģinot atrast lÄ«dzsvaru, kodols nosaka budžetu maksimālajam softirq kontekstā apstrādāto pakeÅ”u skaitam. Kad Å”is budžets ir pārsniegts, tiek pamodināts atseviŔķs pavediens ksoftirqd (jÅ«s redzēsit vienu no tiem ps vienam kodolam), kas apstrādā Å”os softirq ārpus parastā syscall/pārtraukÅ”anas ceļa. Å is pavediens ir ieplānots, izmantojot standarta procesu plānotāju, kas mēģina taisnÄ«gi pieŔķirt resursus.

Tīkla latentuma atkļūdoŔana pakalpojumā Kubernetes

Izpētījis, kā kodols apstrādā paketes, jūs varat redzēt, ka pastāv zināma pārslodzes iespējamība. Ja softirq zvani tiek saņemti retāk, paketēm būs kādu laiku jāgaida, lai tās tiktu apstrādātas tīkla kartes RX rindā. Tas var būt saistīts ar kādu uzdevumu, kas bloķē procesora kodolu, vai kaut kas cits neļauj kodolam darboties softirq.

Apstrādes saŔaurināŔana līdz kodolam vai metodei

Softirq kavÄ“Å”anās pagaidām ir tikai minējums. Bet tam ir jēga, un mēs zinām, ka redzam kaut ko ļoti lÄ«dzÄ«gu. Tātad nākamais solis ir apstiprināt Å”o teoriju. Un, ja tas apstiprinās, tad atrodiet kavÄ“Å”anās iemeslu.

Atgriezīsimies pie mūsu lēnajām paketēm:

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

Kā minēts iepriekÅ”, Ŕīs ICMP paketes tiek sajauktas vienā RX NIC rindā un apstrādātas ar vienu CPU kodolu. Ja vēlamies saprast, kā darbojas Linux, ir noderÄ«gi zināt, kur (kurā CPU kodolā) un kā (softirq, ksoftirqd) Ŕīs pakotnes tiek apstrādātas, lai izsekotu procesam.

Tagad ir pienācis laiks izmantot rÄ«kus, kas ļauj pārraudzÄ«t Linux kodolu reāllaikā. Å eit mēs izmantojām BCC. Å is rÄ«ku komplekts ļauj rakstÄ«t mazas C programmas, kas kodolā piesaista patvaļīgas funkcijas un buferē notikumus lietotāja vietas Python programmā, kas var tos apstrādāt un atgriezt rezultātu. Patvaļīgu funkciju piesaistÄ«Å”ana kodolam ir grÅ«ts bizness, taču utilÄ«ta ir paredzēta maksimālai droŔībai un ir paredzēta, lai izsekotu tieÅ”i tādas ražoÅ”anas problēmas, kuras nav viegli reproducēt testa vai izstrādes vidē.

Plāns Å”eit ir vienkārÅ”s: mēs zinām, ka kodols apstrādā Å”os ICMP ping, tāpēc mēs izmantosim kodola funkciju. icmp_echo, kas pieņem ienākoÅ”o ICMP atbalss pieprasÄ«juma paketi un sāk ICMP atbalss atbildes sÅ«tÄ«Å”anu. Mēs varam identificēt paketi, palielinot icmp_seq numuru, kas parāda hping3 iepriekÅ”.

Kods bcc skripts izskatās sarežģīti, bet tas nav tik biedējoÅ”i, kā Ŕķiet. Funkcija icmp_echo nodod struct sk_buff *skb: Å Ä« ir pakete ar "atbalss pieprasÄ«jumu". Mēs varam to izsekot, izvilkt secÄ«bu echo.sequence (kas salÄ«dzina ar icmp_seq ar hping3 Š²Ń‹ŃˆŠµ) un nosÅ«tiet to uz lietotāja vietu. Ir arÄ« ērti tvert paÅ”reizējā procesa nosaukumu/id. Tālāk ir norādÄ«ti rezultāti, kurus mēs redzam tieÅ”i, kodolam apstrādājot paketes:

TGID PID PROCESA NOSAUKUMS ICMP_SEQ
0 0 mijmaiņa/11 770
0 0 mijmaiņa/11 771
0 0 mijmaiņa/11 772
0 0 mijmaiņa/11 773
0 0 mijmaiņa/11 774
20041 20086 Prometejs 775
0 0 mijmaiņa/11 776
0 0 mijmaiņa/11 777
0 0 mijmaiņa/11 778
4512 4542 spieķi-ziņojumi-s 779

Te gan jāatzÄ«mē, ka kontekstā softirq procesi, kas veica sistēmas izsaukumus, parādÄ«sies kā "procesi", lai gan patiesÄ«bā kodols ir tas, kas droÅ”i apstrādā paketes kodola kontekstā.

Izmantojot Å”o rÄ«ku, mēs varam saistÄ«t konkrētus procesus ar konkrētām pakotnēm, kas parāda aizkavi hping3. PadarÄ«sim to vienkārÅ”u grep par Å”o uztverÅ”anu noteiktām vērtÄ«bām icmp_seq. Paketes, kas atbilst iepriekÅ” minētajām icmp_seq vērtÄ«bām, tika atzÄ«mētas kopā ar to RTT, ko novērojām iepriekÅ” (iekavās ir norādÄ«tas paredzamās RTT vērtÄ«bas paketēm, kuras izfiltrējām, jo ā€‹ā€‹RTT vērtÄ«bas bija mazākas par 50 ms):

TGID PID PROCESA NOSAUKUMS ICMP_SEQ ** RTT
--
10137 10436 cadvisor 1951.g
10137 10436 cadvisor 1952.g
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** (49 ms)
76 76 ksoftirqd/11, 1959 ** (39 ms)
76 76 ksoftirqd/11 1960 ** (29 ms)
76 76 ksoftirqd/11, 1961 ** (19 ms)
76 76 ksoftirqd/11, 1962 ** (9 ms)
--
10137 10436 cadvisor 2068
10137 10436 cadvisor 2069
76 76 ksoftirqd/11 2070 ** 75 ms
76 76 ksoftirqd/11 2071 ** 65 ms
76 76 ksoftirqd/11 2072 ** 55 ms
76 76 ksoftirqd/11 2073** (45 ms)
76 76 ksoftirqd/11 2074** (35 ms)
76 76 ksoftirqd/11 2075 ** (25 ms)
76 76 ksoftirqd/11 2076 ** (15 ms)
76 76 ksoftirqd/11 2077** (5 ms)

Rezultāti mums saka vairākas lietas. Pirmkārt, visas Ŕīs pakotnes apstrādā konteksts ksoftirqd/11. Tas nozÄ«mē, ka Å”im konkrētajam maŔīnu pārim ICMP paketes tika sajauktas lÄ«dz 11. kodolam saņemÅ”anas galā. Mēs arÄ« redzam, ka ikreiz, kad ir iestrēgums, ir paketes, kas tiek apstrādātas sistēmas izsaukuma kontekstā cadvisor. Pēc tam ksoftirqd pārņem uzdevumu un apstrādā uzkrāto rindu: tieÅ”i tik pakeÅ”u skaitu, kas ir sakrājuŔās pēc cadvisor.

Tas, ka tieÅ”i pirms tam vienmēr strādā cadvisor, nozÄ«mē viņa iesaistÄ«Å”anos problēmas risināŔanā. Ironiski, mērÄ·is cadvisor - "analizēt resursu lietojumu un darbināmo konteineru veiktspējas raksturlielumus", nevis izraisÄ«t Å”o veiktspējas problēmu.

Tāpat kā ar citiem konteineru aspektiem, Å”ie visi ir ļoti uzlaboti rÄ«ki, un var rasties darbÄ«bas problēmas dažos neparedzētos apstākļos.

Ko dara cadvisor, kas palēnina pakeÅ”u rindu?

Tagad mums ir diezgan laba izpratne par to, kā notiek avārija, kāds process to izraisa un kurā CPU. Mēs redzam, ka cietās bloÄ·Ä“Å”anas dēļ Linux kodolam nav laika ieplānot ksoftirqd. Un mēs redzam, ka paketes tiek apstrādātas kontekstā cadvisor. Ir loÄ£iski to pieņemt cadvisor palaiž lēnu syscall, pēc kuras tiek apstrādātas visas tajā laikā uzkrātās paketes:

Tīkla latentuma atkļūdoŔana pakalpojumā Kubernetes

Tā ir teorija, bet kā to pārbaudÄ«t? Mēs varam izsekot CPU kodolam Å”ajā procesā, atrast vietu, kur pakeÅ”u skaits pārsniedz budžetu un tiek izsaukts ksoftirqd, un pēc tam paskatÄ«ties nedaudz tālāk, lai redzētu, kas tieÅ”i pirms Ŕī punkta darbojās CPU kodolā. . Tas ir tāpat kā ik pēc dažām milisekundēm veikt CPU rentgenu. Tas izskatÄ«sies apmēram Ŕādi:

Tīkla latentuma atkļūdoŔana pakalpojumā Kubernetes

Ērti to visu var izdarÄ«t ar esoÅ”ajiem instrumentiem. Piemēram, ideāls ieraksts pārbauda doto CPU kodolu noteiktā frekvencē un var Ä£enerēt izsaukumu grafiku uz darbojoÅ”os sistēmu, iekļaujot gan lietotāja vietu, gan Linux kodolu. Varat paņemt Å”o ierakstu un apstrādāt to, izmantojot nelielu programmas dakÅ”iņu FlameGraph no Brendana Grega, kas saglabā steka izsekoÅ”anas kārtÄ«bu. Mēs varam saglabāt vienas rindiņas steka trases ik pēc 1 ms un pēc tam izcelt un saglabāt paraugu 100 milisekundes pirms trasÄ“Å”anas 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

Lūk, rezultāti:

(сŠ¾Ń‚Š½Šø сŠ»ŠµŠ“Š¾Š², ŠŗŠ¾Ń‚Š¾Ń€Ń‹Šµ Š²Ń‹Š³Š»ŃŠ“ят ŠæŠ¾Ń…Š¾Š¶ŠøŠ¼Šø)

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

Å eit ir daudz lietu, taču galvenais ir tas, ka mēs atrodam modeli ā€œcadvisor before ksoftirqdā€, ko redzējām iepriekÅ” ICMP izsekotājs. Ko tas nozÄ«mē?

Katra lÄ«nija ir CPU izsekoÅ”ana noteiktā laika brÄ«dÄ«. Katrs lÄ«nijas izsaukums tiek atdalÄ«ts ar semikolu. LÄ«niju vidÅ« mēs redzam, ka tiek izsaukts syscall: read(): .... ;do_syscall_64;sys_read; .... Tātad cadvisor pavada daudz laika sistēmas zvanam read()kas saistÄ«ti ar funkcijām mem_cgroup_* (zvanu kaudzes augÅ”daļa/lÄ«nijas beigas).

Ir neērti zvana trasē redzēt, kas tieÅ”i tiek nolasÄ«ts, tāpēc palaidÄ«sim strace un paskatÄ«simies, ko dara cadvisor, un atradÄ«sim sistēmas zvanus, kas garāki par 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>

Kā jÅ«s varētu gaidÄ«t, mēs redzam lēnus zvanus read(). No lasÄ«Å”anas darbÄ«bu satura un konteksta mem_cgroup ir skaidrs, ka Å”ie izaicinājumi read() atsaukties uz failu memory.stat, kas parāda atmiņas lietojumu un cgroup ierobežojumus (Docker resursu izolācijas tehnoloÄ£ija). Cadvisor rÄ«ks vaicā Å”o failu, lai iegÅ«tu informāciju par konteineru resursu lietojumu. PārbaudÄ«sim, vai kodols vai cadvisor nedara kaut ko neparedzētu:

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

Tagad mēs varam atveidot kļūdu un saprast, ka Linux kodols saskaras ar patoloģiju.

Kāpēc lasÄ«Å”anas darbÄ«ba ir tik lēna?

Å ajā posmā ir daudz vieglāk atrast ziņojumus no citiem lietotājiem par lÄ«dzÄ«gām problēmām. Kā izrādÄ«jās, cadvisor tracker par Å”o kļūdu tika ziņots kā pārmērÄ«ga CPU izmantoÅ”anas problēma, vienkārÅ”i neviens nepamanÄ«ja, ka latentums nejauÅ”i tiek atspoguļots arÄ« tÄ«kla stekā. TieŔām tika pamanÄ«ts, ka cadvisor patērē vairāk CPU laika nekā gaidÄ«ts, taču tam netika pieŔķirta liela nozÄ«me, jo mÅ«su serveriem ir daudz CPU resursu, tāpēc problēma netika rÅ«pÄ«gi izpētÄ«ta.

Problēma ir tā, ka cgroups ņem vērā atmiņas izmantoÅ”anu nosaukumvietā (konteinerā). Kad visi procesi Å”ajā cgrupā iziet, Docker atbrÄ«vo atmiņas cgrupu. Tomēr "atmiņa" nav tikai procesa atmiņa. Lai gan pati procesa atmiņa vairs netiek izmantota, Ŕķiet, ka kodols joprojām pieŔķir keÅ”atmiņā saglabāto saturu, piemēram, dentries un inodes (direktoriju un failu metadatus), kas tiek saglabāti atmiņas cgrupā. No problēmas apraksta:

zombie cgroups: cgroups, kurām nav procesu un ir izdzēstas, bet joprojām ir atvēlēta atmiņa (manā gadÄ«jumā no dentry cache, bet to var arÄ« pieŔķirt no lapas keÅ”atmiņas vai tmpfs).

Kodola visu keÅ”atmiņā esoÅ”o lapu pārbaude, atbrÄ«vojot cgrupu, var bÅ«t ļoti lēna, tāpēc tiek izvēlēts slinks process: pagaidiet, lÄ«dz Ŕīs lapas atkal tiek pieprasÄ«tas, un tad beidzot notÄ«riet cgrupu, kad patieŔām ir nepiecieÅ”ama atmiņa. LÄ«dz Å”im brÄ«dim, apkopojot statistiku, joprojām tiek ņemta vērā cgroup.

No veiktspējas viedokļa viņi upurēja atmiņu veiktspējai: paātrina sākotnējo tÄ«rÄ«Å”anu, atstājot daļu keÅ”atmiņas. Tas ir labi. Kad kodols izmanto pēdējo keÅ”atmiņā saglabāto atmiņu, cgrupa galu galā tiek notÄ«rÄ«ta, tāpēc to nevar saukt par "noplÅ«di". Diemžēl konkrēta meklÄ“Å”anas mehānisma Ä«stenoÅ”ana memory.stat Å”ajā kodola versijā (4.9) kopā ar milzÄ«go atmiņas apjomu mÅ«su serveros nozÄ«mē, ka ir nepiecieÅ”ams daudz ilgāks laiks, lai atjaunotu jaunākos keÅ”atmiņā saglabātos datus un notÄ«rÄ«tu cgroup zombijus.

Izrādās, ka dažos mūsu mezglos bija tik daudz cgroup zombiju, ka lasīŔanas un latentuma ilgums pārsniedza sekundi.

Cadvisor problēmas risinājums ir nekavējoties atbrÄ«vot dentries/inodes keÅ”atmiņas visā sistēmā, kas nekavējoties novērÅ” lasÄ«Å”anas latentumu, kā arÄ« tÄ«kla latentumu resursdatorā, jo, notÄ«rot keÅ”atmiņu, tiek ieslēgtas keÅ”atmiņā saglabātās cgroup zombiju lapas, un tās arÄ« tiek atbrÄ«votas. Tas nav risinājums, bet apstiprina problēmas cēloni.

IzrādÄ«jās, ka jaunākās kodola versijās (4.19+) zvanu veiktspēja tika uzlabota memory.stat, tāpēc, pārejot uz Å”o kodolu, problēma tika novērsta. Tajā paŔā laikā mums bija rÄ«ki, lai atklātu problemātiskos mezglus Kubernetes klasteros, graciozi tos iztukÅ”otu un atsāknētu. Mēs izÄ·emmējām visas kopas, atradām mezglus ar pietiekami augstu latentumu un pārstartējām tos. Tas deva mums laiku atjaunināt OS atlikuÅ”ajos serveros.

Summējot

Tā kā Ŕī kļūda apturēja RX NIC rindas apstrādi uz simtiem milisekundēm, tā vienlaikus izraisÄ«ja gan lielu latentumu Ä«siem savienojumiem, gan vidēja savienojuma latentumu, piemēram, starp MySQL pieprasÄ«jumiem un atbildes paketēm.

VissvarÄ«gāko sistēmu, piemēram, Kubernetes, veiktspējas izpratne un uzturÄ“Å”ana ir ļoti svarÄ«ga visu uz tām balstÄ«to pakalpojumu uzticamÄ«bai un ātrumam. Katra jÅ«su darbinātā sistēma gÅ«st labumu no Kubernetes veiktspējas uzlabojumiem.

Avots: www.habr.com

Pievieno komentāru