Kubernetes-də şəbəkə gecikməsinin aradan qaldırılması

Kubernetes-də şəbəkə gecikməsinin aradan qaldırılması

Bir neçə il əvvəl Kubernetes artıq müzakirə olunub rəsmi GitHub bloqunda. O vaxtdan bəri bu, xidmətlərin yerləşdirilməsi üçün standart texnologiyaya çevrildi. Kubernetes indi daxili və ictimai xidmətlərin əhəmiyyətli bir hissəsini idarə edir. Klasterlərimiz böyüdükcə və performans tələbləri daha sərtləşdikcə, biz Kubernetes-də bəzi xidmətlərin proqramın özünün yüklənməsi ilə izah edilə bilməyən gecikmələr olduğunu müşahidə etməyə başladıq.

Əsasən, proqramlar 100 ms-ə qədər və ya daha çox təsadüfi şəbəkə gecikməsini yaşayır, nəticədə fasilələr və ya təkrar cəhdlər olur. Xidmətlərin sorğulara 100 ms-dən çox daha sürətli cavab verə biləcəyi gözlənilirdi. Ancaq əlaqənin özü bu qədər vaxt aparırsa, bu mümkün deyil. Ayrı-ayrılıqda, biz millisaniyələr çəkməli olan çox sürətli MySQL sorğularını müşahidə etdik və MySQL millisaniyələrdə tamamlandı, lakin sorğu edən tətbiqin nöqteyi-nəzərindən cavab 100 ms və ya daha çox çəkdi.

Dərhal aydın oldu ki, zəng Kubernetes xaricindən gəlsə belə, problem yalnız Kubernetes qovşağına qoşulduqda baş verib. Problemi düzəltməyin ən asan yolu testdir Vegeta, istənilən daxili hostdan işləyən, Kubernetes xidmətini müəyyən bir portda sınaqdan keçirir və yüksək gecikməni sporadik olaraq qeyd edir. Bu yazıda bu problemin səbəbini necə izləyə bildiyimizə baxacağıq.

Zəncirdə uğursuzluğa səbəb olan lazımsız mürəkkəbliyin aradan qaldırılması

Eyni nümunəni təkrarlayaraq, problemin diqqətini daraltmaq və lazımsız mürəkkəblik qatlarını aradan qaldırmaq istədik. Başlanğıcda, Vegeta və Kubernetes podları arasındakı axınlarda həddindən artıq çox element var idi. Daha dərin bir şəbəkə problemini müəyyən etmək üçün onlardan bəzilərini istisna etməlisiniz.

Kubernetes-də şəbəkə gecikməsinin aradan qaldırılması

Müştəri (Vegeta) klasterdəki istənilən qovşaqla TCP əlaqəsi yaradır. Kubernetes istifadə edən üst-üstə düşən şəbəkə (mövcud məlumat mərkəzi şəbəkəsinin üstündə) kimi fəaliyyət göstərir IPIP, yəni məlumat mərkəzinin IP paketləri daxilində üst-üstə düşən şəbəkənin IP paketlərini əhatə edir. Birinci node qoşulduqda şəbəkə ünvanının tərcüməsi həyata keçirilir Şəbəkə ünvanının tərcüməsi (NAT) Kubernetes qovşağının IP ünvanını və portunu üst-üstə düşən şəbəkədəki IP ünvanına və porta (xüsusilə, proqram ilə pod) tərcümə etmək üçün vəziyyətə uyğundur. Daxil olan paketlər üçün hərəkətlərin tərs ardıcıllığı yerinə yetirilir. Bu, xidmətlər yerləşdirildikdə və köçürüldükcə daim yenilənən və dəyişdirilən çoxlu dövlət və bir çox elementləri olan mürəkkəb bir sistemdir.

Kommunal tcpdump Vegeta testində TCP əl sıxma zamanı gecikmə var (SYN və SYN-ACK arasında). Bu lazımsız mürəkkəbliyi aradan qaldırmaq üçün istifadə edə bilərsiniz hping3 SYN paketləri ilə sadə "pinglər" üçün. Cavab paketində gecikmə olub olmadığını yoxlayırıq və sonra əlaqəni sıfırlayırıq. Biz məlumatları yalnız 100 ms-dən böyük paketləri daxil etmək üçün süzgəcdən keçirə bilərik və Vegeta-nın tam şəbəkə səviyyəsi 7 testindən fərqli olaraq problemi yenidən yaratmaq üçün daha asan yol əldə edə bilərik. Budur, ən yavaş cavablarla süzülmüş 30927 ms intervalla "qovşaq portu" (10) xidmətində TCP SYN/SYN-ACK istifadə edən Kubernetes node "pingləri":

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

Dərhal ilk müşahidəni edə bilər. Ardıcıllıq nömrələrinə və vaxtlara əsasən aydın olur ki, bunlar birdəfəlik tıxaclar deyil. Gecikmə tez-tez yığılır və nəticədə emal olunur.

Sonra, tıxacın yaranmasında hansı komponentlərin iştirak edə biləcəyini öyrənmək istəyirik. Bəlkə bunlar NAT-da yüzlərlə iptables qaydalarından bəziləridir? Yoxsa şəbəkədə IPIP tunellə bağlı hər hansı problem var? Bunu yoxlamağın bir yolu sistemin hər bir addımını aradan qaldıraraq sınaqdan keçirməkdir. Yalnız IPIP hissəsini tərk edərək NAT və firewall məntiqini silsəniz nə olacaq:

Kubernetes-də şəbəkə gecikməsinin aradan qaldırılması

Xoşbəxtlikdən, Linux, əgər maşın eyni şəbəkədədirsə, birbaşa IP üst qatına daxil olmağı asanlaşdırır:

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

Nəticələrə görə, problem hələ də qalır! Bu, iptables və NAT-ı istisna edir. Yəni problem TCP-dir? Gəlin adi ICMP pinginin necə getdiyini görək:

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

Nəticələr göstərir ki, problem aradan qalxmayıb. Bəlkə bu, IPIP tunelidir? Testi daha da sadələşdirək:

Kubernetes-də şəbəkə gecikməsinin aradan qaldırılması

Bütün paketlər bu iki host arasında göndərilir?

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

Biz vəziyyəti bir-birinə istənilən paketi, hətta ICMP pingini də göndərən iki Kubernetes qovşağına asanlaşdırdıq. Hədəf ev sahibi "pis" olduqda (bəziləri digərlərindən daha pisdir) hələ də gecikməni görürlər.

İndi son sual: niyə gecikmə yalnız kube node serverlərində baş verir? Və kube node göndərici və ya alıcı olduqda baş verir? Xoşbəxtlikdən, bunu Kubernetes xaricindəki bir hostdan paket göndərməklə anlamaq çox asandır, lakin eyni "məlum pis" alıcı ilə. Gördüyünüz kimi, problem aradan qalxmayıb:

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

Daha sonra əvvəlki mənbə kube-qovşağından xarici hosta eyni sorğuları icra edəcəyik (bu, ping həm RX, həm də TX komponentini ehtiva etdiyi üçün mənbə hostu istisna edir):

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

Gecikmə paketlərinin çəkilişlərini araşdıraraq bəzi əlavə məlumatlar əldə etdik. Xüsusilə, göndərən (aşağı) bu fasiləni görür, lakin alıcı (yuxarı) görmür - Delta sütununa baxın (saniyələrlə):

Kubernetes-də şəbəkə gecikməsinin aradan qaldırılması

Bundan əlavə, alıcı tərəfdə TCP və ICMP paketlərinin (ardıcıllıq nömrələri ilə) sıralanmasındakı fərqə baxsanız, ICMP paketləri həmişə göndərildikləri ardıcıllıqla, lakin fərqli vaxtla gəlirlər. Eyni zamanda, TCP paketləri bəzən bir-birinə qarışır və bəziləri ilişib qalır. Xüsusilə, SYN paketlərinin portlarını yoxlasanız, onlar göndərən tərəfdə qaydasındadır, lakin qəbul edən tərəfdə deyil.

Necə olduğuna dair incə bir fərq var şəbəkə kartları müasir serverlər (məlumat mərkəzimizdəki kimi) TCP və ya ICMP ehtiva edən paketləri emal edir. Paket gəldikdə, şəbəkə adapteri onu "bir əlaqə üçün hash edir", yəni əlaqələri növbələrə ayırmağa və hər növbəni ayrıca prosessor nüvəsinə göndərməyə çalışır. TCP üçün bu hash həm mənbə, həm də təyinat IP ünvanı və portu ehtiva edir. Başqa sözlə, hər bir əlaqə (potensial olaraq) fərqli şəkildə hash edilir. ICMP üçün portlar olmadığı üçün yalnız IP ünvanları hashing edilir.

Başqa bir yeni müşahidə: bu müddət ərzində biz iki host arasında bütün rabitələrdə ICMP gecikmələrini görürük, lakin TCP bunu etmir. Bu, bizə səbəbin çox güman ki, RX növbəsinin heşinqi ilə əlaqəli olduğunu söyləyir: tıxac, demək olar ki, cavabların göndərilməsində deyil, RX paketlərinin emalındadır.

Bu, mümkün səbəblər siyahısından paketlərin göndərilməsini aradan qaldırır. İndi bilirik ki, paketlərin işlənməsi problemi bəzi kube node serverlərində qəbul tərəfindədir.

Linux nüvəsində paket emalını başa düşmək

Bəzi kube node serverlərində problemin qəbuledicidə niyə baş verdiyini anlamaq üçün gəlin Linux nüvəsinin paketləri necə emal etdiyinə baxaq.

Ən sadə ənənəvi tətbiqə qayıdaraq, şəbəkə kartı paketi qəbul edir və göndərir kəsmək emal edilməsi lazım olan bir paketin olduğunu göstərən Linux nüvəsi. Kernel digər işləri dayandırır, konteksti kəsmə idarəçisinə keçir, paketi emal edir və sonra cari tapşırıqlara qayıdır.

Kubernetes-də şəbəkə gecikməsinin aradan qaldırılması

Bu kontekstdə keçid yavaşdır: 10-cı illərdə 90Mbps şəbəkə kartlarında gecikmə nəzərəçarpacaq olmaya bilərdi, lakin saniyədə maksimum 10 milyon paket ötürmə qabiliyyəti olan müasir 15G kartlarında kiçik səkkiz nüvəli serverin hər nüvəsi milyonlarla kəsilə bilər. saniyədə dəfə.

Kesintiləri daim idarə etməmək üçün bir çox illər əvvəl Linux əlavə etdi NAPI: Bütün müasir sürücülərin yüksək sürətlə performansı artırmaq üçün istifadə etdiyi şəbəkə API. Aşağı sürətlərdə nüvə hələ də köhnə üsulla şəbəkə kartından kəsilmələr alır. Kifayət qədər həddi aşan paketlər gəldikdən sonra nüvə fasilələri söndürür və bunun əvəzinə şəbəkə adapterini sorğulamağa və paketləri parçalara ayırmağa başlayır. Emal softirqdə yəni in proqram təminatının kəsilməsi konteksti sistem çağırışlarından və aparat fasilələrindən sonra, kernel (istifadəçi məkanından fərqli olaraq) artıq işlədiyi zaman.

Kubernetes-də şəbəkə gecikməsinin aradan qaldırılması

Bu, daha sürətli, lakin fərqli bir problemə səbəb olur. Əgər paketlər həddən artıq çoxdursa, onda bütün vaxt şəbəkə kartından paketlərin işlənməsinə sərf olunur və istifadəçi məkanı proseslərinin bu növbələri (TCP bağlantılarından oxumaq və s.) faktiki olaraq boşaltmağa vaxtı yoxdur. Nəhayət, növbələr dolur və biz paketləri atmağa başlayırıq. Balans tapmaq cəhdi olaraq, nüvə softirq kontekstində işlənmiş paketlərin maksimum sayı üçün büdcə təyin edir. Bu büdcə keçdikdən sonra ayrı bir mövzu oyanır ksoftirqd (onlardan birini içəridə görəcəksiniz ps bu softirqləri normal sistem zəngi/kesinti yolundan kənarda idarə edən hər nüvəyə görə). Bu mövzu resursları ədalətli şəkildə bölüşdürməyə çalışan standart proses planlayıcısından istifadə etməklə planlaşdırılıb.

Kubernetes-də şəbəkə gecikməsinin aradan qaldırılması

Kernelin paketləri necə emal etdiyini öyrəndikdən sonra müəyyən bir tıxanma ehtimalının olduğunu görə bilərsiniz. softirq zəngləri daha az qəbul edilərsə, paketlər şəbəkə kartındakı RX növbəsində emal olunmaq üçün bir müddət gözləməli olacaqlar. Bu prosessorun nüvəsini bloklayan hansısa vəzifə ilə bağlı ola bilər və ya başqa bir şey nüvənin softirq işləməsinə mane olur.

Emalı əsas və ya metoda qədər daraltmaq

Softirq gecikmələri hələlik sadəcə bir təxmindir. Ancaq bunun mənası var və biz çox oxşar bir şey gördüyümüzü bilirik. Beləliklə, növbəti addım bu nəzəriyyəni təsdiqləməkdir. Və təsdiqlənərsə, gecikmələrin səbəbini tapın.

Yavaş paketlərimizə qayıdaq:

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

Daha əvvəl müzakirə edildiyi kimi, bu ICMP paketləri tək bir RX NIC növbəsinə yığılır və bir CPU nüvəsi tərəfindən işlənir. Linux-un necə işlədiyini anlamaq istəyiriksə, prosesi izləmək üçün bu paketlərin harada (hansı CPU nüvəsində) və necə (softirq, ksoftirqd) işləndiyini bilmək faydalıdır.

İndi Linux nüvəsini real vaxt rejimində izləməyə imkan verən vasitələrdən istifadə etməyin vaxtıdır. Burada istifadə etdik Bcc. Bu alətlər dəsti sizə nüvədə ixtiyari funksiyaları birləşdirən və hadisələri onları emal edə və nəticəni sizə qaytara bilən istifadəçi məkanı Python proqramına bufer edən kiçik C proqramlarını yazmağa imkan verir. Nüvəyə ixtiyari funksiyaların qoşulması mürəkkəb məsələdir, lakin yardım proqramı maksimum təhlükəsizlik üçün nəzərdə tutulub və sınaq və ya inkişaf mühitində asanlıqla təkrarlana bilməyən istehsal məsələlərini dəqiq izləmək üçün nəzərdə tutulub.

Buradakı plan sadədir: biz bilirik ki, kernel bu ICMP pingləri emal edir, ona görə də nüvə funksiyasına çəngəl qoyacağıq. icmp_echo, daxil olan ICMP əks-səda sorğusu paketini qəbul edir və ICMP əks-səda cavabını göndərməyə başlayır. Biz göstərən icmp_seq nömrəsini artırmaqla paketi müəyyən edə bilərik hping3 daha yüksəkdir.

Kod bcc skripti mürəkkəb görünür, amma göründüyü qədər qorxulu deyil. Funksiya icmp_echo ötürür struct sk_buff *skb: Bu, "echo sorğusu" olan bir paketdir. Biz onu izləyə bilərik, ardıcıllığı çıxara bilərik echo.sequence (bu ilə müqayisə edir icmp_seq hping3 tərəfindən выше) və istifadəçi sahəsinə göndərin. Cari proses adını/id-i tutmaq da rahatdır. Aşağıda kernel paketləri emal edərkən birbaşa gördüyümüz nəticələrdir:

TGID PID PROSESİ ADI ICMP_SEQ 0 0 dəyişdirici/11 770 0 0 dəyişdirici/11 771 0 0 dəyişdirici/11 772 0 0 dəyişdirici/11 773 0 0 dəyişdirici/11 774 20041 pro theus 20086 775 dəyişdirici/0 0 11 776 0 dəyişdirici/0 11 777 0 sözlər-hesabatlar 0

Burada qeyd etmək lazımdır ki, kontekstdə softirq sistem çağırışları edən proseslər "proseslər" kimi görünəcək, çünki əslində nüvə kontekstində paketləri təhlükəsiz şəkildə emal edən nüvədir.

Bu alətlə biz xüsusi prosesləri gecikmə göstərən xüsusi paketlərlə əlaqələndirə bilərik hping3. Gəlin bunu sadə edək grep müəyyən dəyərlər üçün bu ələ icmp_seq. Yuxarıdakı icmp_seq dəyərlərinə uyğun gələn paketlər yuxarıda müşahidə etdiyimiz RTT ilə birlikdə qeyd edildi (mötərizədə RTT dəyərlərinin 50 ms-dən az olması səbəbindən süzgəcdən keçirdiyimiz paketlər üçün gözlənilən RTT dəyərləri verilmişdir):

TGID PID PROSESİNİN ADI ICMP_SEQ ** RTT -- 10137 10436 cadvisor 1951 10137 10436 cadvisor 1952 76 76 ksoftirqd/11 1953 ** 99ms 76 76 ms of 11d 1954d ksoftir qd/89 76 ** 76ms 11 1955 ksoftirqd/ 79 76 ** 76ms 11 1956 ksoftirqd/69 76 ** 76ms 11 1957 ksoftirqd/59 76 ** (76ms) 11 1958 ksoftirqd/49 76 ** (76ms) 11msq (1959ms) **39d 76 76 ksoft irqd/ 11 1960 ** (29ms) 76 76 ksoftirqd/11 1961 ** (19ms) -- 76 76 cadvisor 11 1962 9 cadvisor 10137 10436 2068 ms of 10137ksof ksoftirqd/10436 2069 ** 76ms 76 11 ksoftirqd/ 2070 75 ** 76ms 76 11 ksoftirqd/2071 65 ** (76ms) 76 11 ksoftirqd/2072 55 ** (76ms) 76 11 ksoftirqd/2073 45msd/76 **76q11q (2074ms) ** (35 ms ) 76 76 ksoftirqd/11 2075 ** (25ms)

Nəticələr bizə bir neçə şey deyir. Birincisi, bütün bu paketlər kontekst tərəfindən işlənir ksoftirqd/11. Bu o deməkdir ki, bu xüsusi maşın cütü üçün ICMP paketləri qəbuledici tərəfdə nüvə 11-ə heşlənmişdir. Həm də görürük ki, tıxac olduqda, sistem çağırışı kontekstində işlənən paketlər var. cadvisor... Sonra ksoftirqd tapşırığı öz üzərinə götürür və yığılmış növbəni emal edir: sonra yığılmış paketlərin dəqiq sayı cadvisor.

Fakt budur ki, dərhal əvvəl həmişə işləyir cadvisor, problemdə onun iştirakını nəzərdə tutur. Qəribədir, məqsəd kadvisor - Bu performans probleminə səbəb olmaqdansa, "işləyən konteynerlərin resurs istifadəsini və performans xüsusiyyətlərini təhlil edin".

Konteynerlərin digər aspektlərində olduğu kimi, bunların hamısı yüksək inkişaf etmiş alətlərdir və bəzi gözlənilməz şəraitdə performans problemləri ilə qarşılaşacağı gözlənilir.

Cadvisor paket növbəsini yavaşlatan nə edir?

İndi qəzanın necə baş verdiyini, hansı prosesin ona səbəb olduğunu və hansı CPU-da olduğunu çox yaxşı başa düşürük. Biz görürük ki, sərt bloklamaya görə Linux nüvəsinin qrafiki tərtib etməyə vaxtı yoxdur ksoftirqd. Və paketlərin kontekstdə işləndiyini görürük cadvisor. Bunu güman etmək məntiqlidir cadvisor yavaş bir sistem çağırışını işə salır, bundan sonra o zaman yığılan bütün paketlər işlənir:

Kubernetes-də şəbəkə gecikməsinin aradan qaldırılması

Bu bir nəzəriyyədir, amma onu necə yoxlamaq olar? Bizim edə biləcəyimiz şey bu proses boyunca CPU nüvəsini izləmək, paketlərin sayının büdcəni aşdığı və ksoftirqd adlandırıldığı nöqtəni tapmaq və sonra bir az geriyə baxmaq və bu nöqtədən bir qədər əvvəl CPU nüvəsində tam olaraq nə işlədiyini görməkdir. . Bu, bir neçə millisaniyədən bir CPU-nun rentgenoqrafiyasına bənzəyir. Bu kimi bir şey görünəcək:

Kubernetes-də şəbəkə gecikməsinin aradan qaldırılması

Rahatlıqla, bütün bunlar mövcud alətlərlə edilə bilər. Misal üçün, mükəmməl rekord müəyyən bir tezlikdə verilmiş CPU nüvəsini yoxlayır və həm istifadəçi sahəsi, həm də Linux nüvəsi daxil olmaqla işləyən sistemə zənglər cədvəlini yarada bilər. Siz bu qeydi götürüb proqramın kiçik çəngəlindən istifadə edərək emal edə bilərsiniz FlameGraph yığın izinin sırasını qoruyan Brendan Gregg-dən. Biz hər 1 ms-dən bir tək sətirli yığın izlərini saxlaya bilərik və sonra nümunəni 100 millisaniyədə iz vurmazdan əvvəl vurğulayaraq yadda saxlaya bilərik. 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

Budur nəticələr:

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

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

Burada çox şey var, amma əsas odur ki, biz əvvəllər ICMP izləyicisində gördüyümüz “ksoftirqddən əvvəl cadvisor” nümunəsini tapırıq. Bunun mənası nədi?

Hər bir xətt müəyyən bir zaman nöqtəsində CPU izidir. Xəttdəki yığının hər bir zəngi nöqtəli vergüllə ayrılır. Sətirlərin ortasında sistemin çağırıldığını görürük: read(): .... ;do_syscall_64;sys_read; .... Beləliklə, cadvisor sistem çağırışına çox vaxt sərf edir read()funksiyaları ilə bağlıdır mem_cgroup_* (zəng yığınının üstü/xəttin sonu).

Zəngdə tam olaraq nə oxunduğunu görmək əlverişsizdir, gəlin qaçaq strace və gəlin cadvisorun nə etdiyini görək və 100 ms-dən uzun sistem zənglərini tapaq:

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>

Gözlədiyiniz kimi, biz burada yavaş zəngləri görürük read(). Oxu əməliyyatlarının məzmunundan və kontekstdən mem_cgroup bu çətinliklərin olduğu aydındır read() fayla müraciət edin memory.stat, yaddaş istifadəsini və qrup məhdudiyyətlərini göstərir (Docker-in resurs izolyasiya texnologiyası). Cadvisor aləti konteynerlər üçün resurs istifadəsi məlumatını əldə etmək üçün bu faylı sorğulayır. Gəlin bunun nüvənin və ya kadvisorun gözlənilməz bir şey etdiyini yoxlayaq:

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

İndi səhvi təkrarlaya bilərik və Linux nüvəsinin patoloji ilə üzləşdiyini başa düşə bilərik.

Niyə oxuma əməliyyatı bu qədər yavaşdır?

Bu mərhələdə digər istifadəçilərin oxşar problemlərlə bağlı mesajlarını tapmaq daha asandır. Məlum olub ki, kadvisor izləyicisində bu səhv kimi bildirilir həddindən artıq CPU istifadəsi problemi, sadəcə olaraq, gecikmənin də şəbəkə yığınında təsadüfi şəkildə əks olunduğunu heç kim fərq etmədi. Həqiqətən də kadvisorun gözlənildiyindən daha çox CPU vaxtı sərf etdiyi müşahidə olundu, lakin buna o qədər də əhəmiyyət verilmədi, çünki serverlərimiz çoxlu CPU resursuna malikdir, ona görə də problem diqqətlə öyrənilməmişdir.

Problem ondadır ki, qruplar adlar məkanında (konteyner) yaddaş istifadəsini nəzərə alır. Bu qrupdakı bütün proseslər çıxdıqda, Docker yaddaş qruplarını buraxır. Bununla belə, “yaddaş” sadəcə proses yaddaşı deyil. Proses yaddaşının özü artıq istifadə olunmasa da, belə görünür ki, nüvə hələ də yaddaş qruplarında keşlənmiş dentries və inodlar (kataloq və fayl metaməlumatları) kimi keşlənmiş məzmunları təyin edir. Problemin təsvirindən:

zombi qrupları: heç bir prosesi olmayan və silinmiş, lakin hələ də yaddaşı ayrılmış qruplar (mənim vəziyyətimdə, dentry cache-dən, lakin o, həm də səhifə keşindən və ya tmpf-lərdən ayrıla bilər).

Qrupu azad edərkən nüvənin keşdəki bütün səhifələri yoxlaması çox yavaş ola bilər, ona görə də tənbəl proses seçilir: bu səhifələr yenidən tələb olunana qədər gözləyin və nəhayət, yaddaşa həqiqətən ehtiyac olduqda qrup qrupu təmizləyin. Bu vaxta qədər statistik məlumatlar toplanarkən cgroup hələ də nəzərə alınır.

Performans nöqteyi-nəzərindən onlar performans üçün yaddaşı qurban verdilər: bəzi keşlənmiş yaddaşı arxada qoyaraq ilkin təmizləməni sürətləndirdilər. Bu yaxşıdır. Kernel keşlənmiş yaddaşın sonuncusundan istifadə etdikdə, qrup nəhayət təmizlənir, ona görə də onu "sızma" adlandırmaq olmaz. Təəssüf ki, axtarış mexanizminin xüsusi tətbiqi memory.stat bu kernel versiyasında (4.9), serverlərimizdə böyük həcmdə yaddaşla birlikdə, ən son keşlənmiş məlumatların bərpası və qrup zombilərini təmizləmək üçün daha çox vaxt tələb olunur.

Məlum oldu ki, bəzi qovşaqlarımızda o qədər çox qrup zombi var ki, oxunma və gecikmə bir saniyəni keçdi.

Cadvisor probleminin həlli sistem daxilində dentries/inode keşlərini dərhal boşaltmaqdır ki, bu da oxunma gecikməsini, eləcə də hostda şəbəkə gecikməsini dərhal aradan qaldırır, çünki keşin təmizlənməsi keşlənmiş zombi qrup səhifələrini işə salır və onları da azad edir. Bu həll yolu deyil, lakin problemin səbəbini təsdiqləyir.

Məlum oldu ki, nüvənin daha yeni versiyalarında (4.19+) zəng performansı yaxşılaşdırılıb memory.stat, buna görə də bu nüvəyə keçid problemi həll etdi. Eyni zamanda, Kubernetes klasterlərində problemli qovşaqları aşkar etmək, onları zərif şəkildə boşaltmaq və yenidən işə salmaq üçün alətlərimiz var idi. Bütün klasterləri taradıq, kifayət qədər yüksək gecikmə ilə qovşaqlar tapdıq və onları yenidən başladıq. Bu bizə qalan serverlərdə OS-ni yeniləmək üçün vaxt verdi.

Yekunlaşdırma

Bu səhv RX NIC növbəsinin işlənməsini yüzlərlə millisaniyəlik dayandırdığından, eyni zamanda MySQL sorğuları və cavab paketləri arasında qısa bağlantılarda yüksək gecikmə və orta əlaqə gecikməsinə səbəb oldu.

Kubernetes kimi ən fundamental sistemlərin performansını başa düşmək və saxlamaq onlara əsaslanan bütün xidmətlərin etibarlılığı və sürəti üçün çox vacibdir. İşlətdiyiniz hər sistem Kubernetes performans təkmilləşdirmələrindən faydalanır.

Mənbə: www.habr.com

Добавить комментарий