Kubernetes дахь сүлжээний хоцролтыг дибаг хийх

Kubernetes дахь сүлжээний хоцролтыг дибаг хийх

Хэдэн жилийн өмнө Кубернетес аль хэдийн хэлэлцсэн албан ёсны GitHub блог дээр. Түүнээс хойш энэ нь үйлчилгээг нэвтрүүлэх стандарт технологи болсон. Кубернетес одоо дотоод болон төрийн үйлчилгээний нэлээд хэсгийг удирдаж байна. Манай кластерууд нэмэгдэж, гүйцэтгэлийн шаардлага улам чангарах тусам Kubernetes дээрх зарим үйлчилгээнүүд хааяа хоцрогддогийг бид анзаарч эхэлсэн бөгөөд үүнийг програмын ачааллаас холбон тайлбарлах аргагүй юм.

Үндсэндээ, програмууд нь санамсаргүй мэт санагдах сүлжээний хоцролтыг 100 мс ба түүнээс дээш хугацаагаар мэдэрдэг бөгөөд энэ нь завсарлага эсвэл дахин оролдлого үүсгэдэг. Үйлчилгээнүүд нь хүсэлтэд 100 мс-ээс хамаагүй хурдан хариу өгөх боломжтой байсан. Гэхдээ холболт өөрөө маш их цаг зарцуулдаг бол энэ нь боломжгүй юм. Тус тусад нь бид миллисекундэд шаардагдах маш хурдан MySQL асуулгыг ажигласан бөгөөд MySQL нь миллисекундэд дуусгавар болсон боловч хүсэлт гаргаж буй програмын үүднээс авч үзвэл хариу 100 мс ба түүнээс дээш хугацаа зарцуулсан.

Дуудлага Кубернетесийн гаднаас ирсэн байсан ч зөвхөн Kubernetes зангилаанд холбогдох үед л асуудал үүссэн нь тэр даруй тодорхой болсон. Асуудлыг дахин гаргах хамгийн хялбар арга бол тест юм Ургамал, ямар ч дотоод хостоос ажилладаг бөгөөд Kubernetes үйлчилгээг тодорхой порт дээр туршиж, өндөр хоцролтыг хааяа бүртгэдэг. Энэ нийтлэлд бид энэ асуудлын шалтгааныг хэрхэн олж мэдэхийг авч үзэх болно.

Гинжин дэх шаардлагагүй төвөгтэй байдлыг арилгах нь бүтэлгүйтэлд хүргэдэг

Ижил жишээг хуулбарласнаар бид асуудлын гол анхаарлаа багасгаж, шаардлагагүй төвөгтэй давхаргыг арилгахыг хүссэн. Эхэндээ, Вегета болон Кубернетес хонхоруудын хоорондох урсгалд хэтэрхий олон элемент байсан. Сүлжээний илүү гүнзгий асуудлыг тодорхойлохын тулд та тэдгээрийн заримыг нь хасах хэрэгтэй.

Kubernetes дахь сүлжээний хоцролтыг дибаг хийх

Үйлчлүүлэгч (Vegeta) нь кластерын дурын зангилаатай TCP холболт үүсгэдэг. Kubernetes нь ашигладаг давхардсан сүлжээ (одоо байгаа мэдээллийн төвийн сүлжээн дээр) байдлаар ажилладаг IPIP, өөрөөр хэлбэл, энэ нь дата төвийн IP пакетуудын дотор давхардсан сүлжээний IP пакетуудыг багтаадаг. Эхний зангилаа руу холбогдох үед сүлжээний хаягийн орчуулга хийгддэг Сүлжээний хаягийн орчуулга (NAT) Kubernetes зангилааны IP хаяг болон портыг давхардсан сүлжээн дэх IP хаяг болон порт руу (ялангуяа, програмтай pod) хөрвүүлэх төлөвтэй. Ирж буй пакетуудын хувьд үйлдлүүдийн урвуу дарааллыг гүйцэтгэдэг. Энэ нь үйлчилгээг байршуулах, шилжүүлэх явцад байнга шинэчлэгдэж, өөрчлөгддөг маш олон төлөв, олон элемент бүхий цогц систем юм.

Хэрэгсэл tcpdump Vegeta тест дээр TCP гар барих үед (SYN ба SYN-ACK хооронд) саатал гарсан байна. Энэ шаардлагагүй нарийн төвөгтэй байдлыг арилгахын тулд та ашиглаж болно hping3 SYN пакетуудтай энгийн "pings"-ийн хувьд. Бид хариултын багцад саатал байгаа эсэхийг шалгаж, холболтыг дахин тохируулна. Бид зөвхөн 100 мс-ээс том пакетуудыг оруулахын тулд өгөгдлийг шүүж, Вегета дахь сүлжээний 7-р түвшний бүрэн тестээс илүү хялбар аргаар асуудлыг дахин гаргах боломжтой. Хамгийн удаан хариултаар шүүсэн 30927 ms интервалтайгаар "зангилааны порт" (10) дээр TCP SYN/SYN-ACK ашиглан Kubernetes зангилаа "pings" байна:

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

Тэр даруй анхны ажиглалтыг хийж чадна. Дарааллын дугаар, цаг хугацаанаас нь харахад эдгээр нь нэг удаагийн түгжрэл биш нь ойлгомжтой. Саатал нь ихэвчлэн хуримтлагдаж, эцэст нь боловсруулагддаг.

Дараа нь бид түгжрэл үүсэхэд ямар бүрэлдэхүүн хэсгүүд нөлөөлж болохыг олж мэдэхийг хүсч байна. Магадгүй эдгээр нь NAT дээрх хэдэн зуун iptables дүрмийн зарим нь юм болов уу? Эсвэл сүлжээнд IPIP туннел хийхэд ямар нэг асуудал байна уу? Үүнийг шалгах нэг арга бол системийн алхам бүрийг арилгах замаар шалгах явдал юм. Хэрэв та NAT болон галт ханын логикийг устгаж, зөвхөн IPIP хэсгийг үлдээвэл юу болох вэ:

Kubernetes дахь сүлжээний хоцролтыг дибаг хийх

Аз болоход, хэрэв машин нэг сүлжээнд байгаа бол Linux нь IP давхаргын давхаргад шууд хандахад хялбар болгодог:

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

Үр дүнгээс харахад асуудал хэвээр байна! Үүнд iptables болон NAT хамаарахгүй. Тэгэхээр асуудал нь TCP байна уу? Ердийн ICMP пинг хэрхэн явагддагийг харцгаая:

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

Асуудал арилаагүй нь үр дүнгээс харагдаж байна. Магадгүй энэ нь IPIP хонгил юм болов уу? Туршилтыг илүү хялбарчилъя:

Kubernetes дахь сүлжээний хоцролтыг дибаг хийх

Эдгээр хоёр хостын хооронд бүх пакет илгээгдсэн үү?

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

Бид Кубернетесийн хоёр зангилаа бие биедээ ямар ч пакет илгээдэг, тэр ч байтугай ICMP пинг хүртэл нөхцөл байдлыг хялбаршуулсан. Зорилтот хост нь "муу" (зарим нь бусдаас муу) байвал тэд хоцролтыг харсаар байна.

Одоо сүүлийн асуулт: яагаад саатал зөвхөн kube-node сервер дээр гардаг вэ? Kube-node нь илгээгч эсвэл хүлээн авагч байх үед тохиолддог уу? Аз болоход, Кубернетесээс гадуурх хостоос пакет илгээх замаар үүнийг тодорхойлоход хялбар байдаг, гэхдээ ижил "муу" хүлээн авагчтай. Таны харж байгаагаар асуудал арилаагүй байна:

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

Дараа нь бид өмнөх эх kube зангилаанаас гадаад хост руу ижил хүсэлтүүдийг ажиллуулна (энэ нь ping нь RX болон 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

Хоцрогдсон пакетуудыг судалснаар бид нэмэлт мэдээлэл олж авлаа. Тодруулбал, илгээгч (доод талд) энэ завсарлагыг харж байгаа боловч хүлээн авагч (дээд) нь харахгүй байна - Дельта баганыг (секундэд) харна уу:

Kubernetes дахь сүлжээний хоцролтыг дибаг хийх

Нэмж дурдахад, хэрэв та хүлээн авагч тал дээрх TCP болон ICMP пакетуудын дарааллын ялгааг (дарааллын дугаараар) харвал ICMP пакетууд үргэлж илгээсэн дарааллаар нь ирдэг боловч өөр өөр цаг хугацаатай байдаг. Үүний зэрэгцээ TCP пакетууд заримдаа хоорондоо холбогдож, зарим нь гацдаг. Ялангуяа, хэрэв та SYN пакетуудын портуудыг шалгаж үзвэл тэдгээр нь илгээгчийн талд байгаа боловч хүлээн авагчийн талд биш юм.

Хэрхэн хийх талаар нарийн ялгаа бий сүлжээний картууд орчин үеийн серверүүд (манай дата төвд байдаг шиг) TCP эсвэл ICMP агуулсан пакетуудыг боловсруулдаг. Пакет ирэхэд сүлжээний адаптер нь "холболт бүрт үүнийг хэш болгодог", өөрөөр хэлбэл холболтуудыг дараалалд хувааж, дараалал бүрийг тусдаа процессорын цөм рүү илгээхийг оролддог. TCP-ийн хувьд энэ хэш нь эх сурвалж болон очих IP хаяг, портыг хоёуланг нь агуулна. Өөрөөр хэлбэл, холболт бүр өөр өөр (боломжтой) байна. ICMP-ийн хувьд порт байхгүй тул зөвхөн IP хаягуудыг хэш болгодог.

Өөр нэг шинэ ажиглалт: энэ хугацаанд бид хоёр хост хоорондын бүх харилцаанд ICMP саатал гарч байгааг харж байна, гэхдээ TCP тийм биш. Энэ нь шалтгаан нь RX дарааллын хэштэй холбоотой байж болзошгүйг харуулж байна: түгжрэл нь хариу илгээхэд биш RX пакетуудыг боловсруулахад байгаа нь гарцаагүй.

Энэ нь боломжит шалтгаануудын жагсаалтаас пакет илгээхийг арилгадаг. Зарим kube зангилаа сервер дээр пакет боловсруулах асуудал хүлээн авах тал дээр байгааг бид одоо мэдэж байна.

Линукс цөм дэх пакет боловсруулалтыг ойлгох

Зарим kube зангилаа серверийн хүлээн авагч дээр яагаад асуудал гардагийг ойлгохын тулд Linux цөм пакетуудыг хэрхэн боловсруулдагийг харцгаая.

Хамгийн энгийн уламжлалт хэрэгжилт рүү буцахдаа сүлжээний карт нь пакетыг хүлээн авч илгээдэг таслах боловсруулах шаардлагатай багц байгаа Linux цөм. Цөм нь бусад ажлыг зогсоож, контекстийг тасалдал зохицуулагч руу шилжүүлж, пакетыг боловсруулж, дараа нь одоогийн ажлууд руу буцдаг.

Kubernetes дахь сүлжээний хоцролтыг дибаг хийх

Энэ контекст шилжих нь удаан байна: 10-ээд оны үед 90Mbps сүлжээний картуудад хоцролт нь анзаарагдахгүй байсан ч хамгийн ихдээ секундэд 10 сая пакет дамжуулах чадвартай орчин үеийн 15G картууд дээр найман цөмт жижиг серверийн цөм бүр сая сая тасалдаж болно. секундэд удаа.

Тасалдлыг байнга зохицуулахгүйн тулд олон жилийн өмнө Линукс нэмсэн NAPI: Орчин үеийн бүх драйверууд өндөр хурдтай гүйцэтгэлийг сайжруулахад ашигладаг сүлжээний API. Бага хурдтай үед цөм нь хуучин аргаар сүлжээний картаас тасалдлыг хүлээн авдаг. Босго хэмжээнээс хэтэрсэн хангалттай багц ирсний дараа цөм нь тасалдлыг идэвхгүй болгож, сүлжээний адаптераас санал асуулга авч пакетуудыг хэсэг хэсгээр нь авч эхэлдэг. Боловсруулалтыг softirq, өөрөөр хэлбэл in програм хангамжийн тасалдлын контекст системийн дуудлага болон техник хангамж тасалдсаны дараа цөм (хэрэглэгчийн зайнаас ялгаатай) аль хэдийн ажиллаж байх үед.

Kubernetes дахь сүлжээний хоцролтыг дибаг хийх

Энэ нь илүү хурдан боловч өөр асуудал үүсгэдэг. Хэрэв хэтэрхий олон пакет байгаа бол бүх цагийг сүлжээний картаас пакет боловсруулахад зарцуулдаг бөгөөд хэрэглэгчийн зайны процессууд эдгээр дарааллыг хоослох цаг байдаггүй (TCP холболтоос унших гэх мэт). Эцэст нь дараалал дүүрч, бид пакетуудыг хаяж эхэлдэг. Тэнцвэрийг олохын тулд цөм нь softirq контекстэд боловсруулсан пакетуудын хамгийн их тооны төсвийг тогтоодог. Энэ төсөв хэтэрсэн тохиолдолд тусдаа утас сэрдэг ksoftirqd (та тэдгээрийн аль нэгийг нь харах болно ps нэг цөм) нь эдгээр softirq-уудыг ердийн системийн дуудлагын/тасалдалтын замаас гадуур зохицуулдаг. Энэ хэлхээ нь нөөцийг шударгаар хуваарилахыг оролддог стандарт процесс төлөвлөгчийг ашиглан хуваарьтай.

Kubernetes дахь сүлжээний хоцролтыг дибаг хийх

Цөм нь пакетуудыг хэрхэн боловсруулдагийг судалсны дараа түгжрэл үүсэх магадлал тодорхой байгааг харж болно. Хэрэв softirq дуудлагыг бага хүлээн авдаг бол багцуудыг сүлжээний карт дээрх RX дараалалд боловсруулахын тулд хэсэг хугацаа хүлээх шаардлагатай болно. Энэ нь процессорын цөмийг хааж байгаа ямар нэг ажил эсвэл ямар нэг зүйл нь цөмийг softirq ажиллуулахаас сэргийлж байгаатай холбоотой байж болох юм.

Боловсруулалтыг цөм буюу арга хүртэл нарийсгах

Softirq саатал нь одоогоор зөвхөн таамаглал юм. Гэхдээ энэ нь утга учиртай бөгөөд бид маш төстэй зүйлийг харж байгаагаа мэдэж байна. Тиймээс дараагийн алхам бол энэ онолыг батлах явдал юм. Хэрэв энэ нь батлагдвал хойшлогдсон шалтгааныг олж мэдээрэй.

Удаан пакетууд руугаа буцъя:

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

Өмнө дурьдсанчлан, эдгээр ICMP пакетуудыг нэг RX NIC дараалалд оруулан, нэг CPU цөмөөр боловсруулдаг. Хэрэв бид Линукс хэрхэн ажилладагийг ойлгохыг хүсвэл процессыг хянахын тулд эдгээр багцуудыг хаана (ямар CPU-ийн цөм дээр), хэрхэн (softirq, ksoftirqd) боловсруулдагийг мэдэх нь ашигтай байдаг.

Одоо Линуксийн цөмийг бодит цаг хугацаанд хянах боломжийг олгодог хэрэгслүүдийг ашиглах цаг болжээ. Энд бид ашигласан б. Энэхүү багц хэрэгслүүд нь цөмд дурын функцүүдийг залгах жижиг C программуудыг бичиж, үйл явдлуудыг хэрэглэгчийн орон зайн Python программд буфер болгон тэдгээрийг боловсруулж үр дүнг танд буцааж өгөх боломжийг олгодог. Цөмд дурын функцүүдийг залгах нь төвөгтэй бизнес боловч уг хэрэгсэл нь хамгийн дээд аюулгүй байдлыг хангах зорилготой бөгөөд туршилт эсвэл хөгжүүлэлтийн орчинд хялбархан хуулбарлагдаагүй үйлдвэрлэлийн асуудлуудыг яг таг хянах зорилготой юм.

Энд байгаа төлөвлөгөө нь энгийн: цөм нь эдгээр ICMP пингүүдийг боловсруулдаг гэдгийг бид мэдэж байгаа тул бид цөмийн функц дээр дэгээ тавих болно. icmp_echo, энэ нь ирж буй ICMP echo хүсэлтийн пакетыг хүлээн авч, ICMP echo хариултыг илгээж эхэлдэг. Бид icmp_seq дугаарыг нэмэгдүүлэх замаар пакетийг тодорхойлж болно hping3 илүү өндөр.

Хууль bcc скрипт төвөгтэй харагдаж байгаа ч энэ нь санагдсан шиг аймшигтай биш юм. Чиг үүрэг icmp_echo дамжуулдаг struct sk_buff *skb: Энэ бол "цуурай хүсэлт" бүхий пакет юм. Бид үүнийг дагаж, дарааллыг нь гаргаж чадна echo.sequence (үүнтэй харьцуулж байна icmp_seq hping3-аар выше), хэрэглэгчийн орон зайд илгээнэ үү. Одоогийн процессын нэр/id-г авах нь бас тохиромжтой. Цөм нь пакетуудыг боловсруулах явцад шууд харагдах үр дүнг доор харуулав.

TGID PID ПРОЦЕССИЙН НЭР ICMP_SEQ
0 0 swapper/11
770 0 swapper/0
11 771 swapper/0
0 11 swapper/772
0 0 swapper/11
773 0 прометей 0
11 774 swapper/20041
20086 775 swapper/0
0 11 swapper/776
0 0 илтгэгч 11

Энэ хүрээнд гэдгийг энд тэмдэглэх нь зүйтэй softirq Системийн дуудлага хийсэн процессууд нь цөмийн контекст дэх пакетуудыг аюулгүй боловсруулдаг цөм байдаг бол "процесс" гэж харагдах болно.

Энэ хэрэглүүрийн тусламжтайгаар бид тодорхой процессуудыг хоцролтыг харуулсан тодорхой багцуудтай холбож болно hping3. Үүнийг энгийн болгоё grep тодорхой утгуудын хувьд энэ барих дээр icmp_seq. Дээрх icmp_seq утгуудтай таарч байгаа пакетуудыг RTT-ийнх нь хамт тэмдэглэсэн байна (хаалтанд RTT-ийн 50 мс-ээс бага утгын улмаас шүүсэн пакетуудын хүлээгдэж буй RTT утгууд байна):

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

Үр дүн нь бидэнд хэд хэдэн зүйлийг хэлж өгдөг. Нэгдүгээрт, эдгээр бүх багцыг контекстээр боловсруулдаг ksoftirqd/11. Энэ нь тухайн хос машинуудын хувьд ICMP пакетуудыг хүлээн авагч талын 11-р цөм рүү хэш хийсэн гэсэн үг. Мөн бид саатал гарах бүрт системийн дуудлагын хүрээнд боловсруулагдсан пакетууд байдгийг харж байна. cadvisor…. Дараа нь ksoftirqd даалгаврыг хүлээн авч, хуримтлагдсан дарааллыг боловсруулдаг: дараа нь хуримтлагдсан пакетуудын яг тоо. cadvisor.

Энэ нь нэн даруй өмнө нь үргэлж ажилладаг гэдгийг баримт cadvisor, асуудалд түүний оролцоог илэрхийлдэг. Хачирхалтай нь зорилго кадвизор - Энэ гүйцэтгэлийн асуудлыг үүсгэхийн оронд "ажиллаж байгаа савны нөөцийн ашиглалт, гүйцэтгэлийн шинж чанарыг шинжлэх".

Савны бусад талуудын нэгэн адил эдгээр нь бүгд өндөр дэвшилтэт хэрэгслүүд бөгөөд урьдчилан тооцоолоогүй зарим тохиолдолд гүйцэтгэлийн асуудалтай тулгарах болно.

Пакетийн дарааллыг удаашруулдаг cadvisor юу хийдэг вэ?

Одоо бид эвдрэл хэрхэн үүсдэг, ямар процессоос болж, ямар CPU дээр ажилладаг талаар нэлээд сайн ойлголттой болсон. Хатуу блоклосоны улмаас Линуксийн цөмд хуваарь гаргах цаг байхгүй байгааг бид харж байна ksoftirqd. Мөн бид пакетуудыг контекстоор боловсруулж байгааг харж байна cadvisor. Ингэж үзэх нь логик юм cadvisor нь удаан системийн дуудлагыг эхлүүлж, дараа нь тухайн үед хуримтлагдсан бүх пакетуудыг боловсруулдаг.

Kubernetes дахь сүлжээний хоцролтыг дибаг хийх

Энэ бол онол, гэхдээ үүнийг хэрхэн шалгах вэ? Бидний хийж чадах зүйл бол энэ процессын туршид CPU-ийн цөмийг мөшгиж, багцын тоо төсвөөс давж, ksoftirqd гэж нэрлэгддэг цэгийг олж, дараа нь бага зэрэг эргэж харвал энэ цэгийн өмнөхөн CPU-ийн цөм дээр яг юу ажиллаж байгааг харах болно. . Энэ нь CPU-г хэдэн миллисекунд тутамд рентген зураг авахтай адил юм. Энэ нь иймэрхүү харагдах болно:

Kubernetes дахь сүлжээний хоцролтыг дибаг хийх

Тохиромжтойгоор энэ бүгдийг одоо байгаа багаж хэрэгслээр хийж болно. Жишээлбэл, төгс рекорд өгөгдсөн CPU-ийн цөмийг тодорхой давтамжтайгаар шалгадаг бөгөөд хэрэглэгчийн орон зай болон Линуксийн цөмийг багтаасан ажиллаж байгаа систем рүү залгах хуваарийг үүсгэж болно. Та энэ бичлэгийг аваад програмын жижиг сэрээ ашиглан боловсруулж болно FlameGraph стекийн мөрийн дарааллыг хадгалсан Брендан Греггээс. Бид 1 мс тутамд нэг мөрт стекийн ул мөрийг хадгалж, дараа нь ул мөр хүрэхээс 100 миллисекунд өмнө жишээг тодруулж хадгалах боломжтой. 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

Үр дүн нь энд байна:

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

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

Энд маш олон зүйл бий, гэхдээ гол зүйл бол бид ICMP tracer дээр өмнө нь харсан "ksoftirqd-ийн өмнөх cadvisor" загварыг олж авдаг. Энэ нь юу гэсэн үг вэ?

Мөр бүр нь тодорхой цаг хугацааны CPU-ийн ул мөр юм. Мөр дээрх стекийг доошлуулах дуудлага бүр цэг таслалаар тусгаарлагдана. Мөрүүдийн дунд бид системийн дуудлагыг харж байна: read(): .... ;do_syscall_64;sys_read; .... Тиймээс cadvisor системийн дуудлагад маш их цаг зарцуулдаг read()функцуудтай холбоотой mem_cgroup_* (дуудлагын дээд хэсэг / шугамын төгсгөл).

Дуудлагаар яг юу уншиж байгааг харах нь тохиромжгүй тул гүйцгээе strace мөн cadvisor юу хийдгийг харцгаая, мөн 100 мс-ээс урт системийн дуудлагыг олъё:

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>

Таны таамаглаж байгаачлан бид энд удаан дуудлага ирж байгааг харж байна read(). Унших үйлдлүүд болон контекстийн агуулгаас mem_cgroup эдгээр сорилтууд байгаа нь тодорхой read() файл руу хандана уу memory.stat, санах ойн ашиглалт болон бүлгийн хязгаарыг харуулдаг (Докерын нөөц тусгаарлах технологи). Cadvisor хэрэгсэл нь савны нөөц ашиглалтын мэдээллийг авахын тулд энэ файлаас асуудаг. Цөм эсвэл кадвизор гэнэтийн зүйл хийж байгаа эсэхийг шалгая:

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

Одоо бид алдааг дахин гаргаж, Линуксийн цөм эмгэгтэй тулгарч байгааг ойлгож чадна.

Унших ажиллагаа яагаад ийм удаан байдаг вэ?

Энэ үе шатанд ижил төстэй асуудлын талаар бусад хэрэглэгчдийн мессежийг олох нь илүү хялбар байдаг. Кадвисор трекер дээр энэ алдааг мэдээлсэн нь тодорхой болсон CPU-ийн хэт их хэрэглээний асуудал, зүгээр л хоцролт нь сүлжээний стект санамсаргүй байдлаар тусгалаа олсон гэдгийг хэн ч анзаарсангүй. Кадвизор нь тооцоолж байснаас илүү CPU-ийн цаг зарцуулж байгаа нь үнэхээр анзаарагдсан боловч манай серверүүд CPU-ийн нөөц ихтэй тул асуудлыг сайтар судлаагүй.

Асуудал нь бүлгүүд нэрийн зай (контейнер) доторх санах ойн ашиглалтыг харгалзан үздэгт оршино. Энэ бүлгийн бүх процессууд гарахад Docker санах ойн бүлгийг гаргадаг. Гэсэн хэдий ч "санах ой" нь зөвхөн процессын санах ой биш юм. Хэдийгээр процессын санах ойг өөрөө ашиглахаа больсон ч цөм нь санах ойн бүлэгт хадгалагдсан dentries болон inode (заахлах ба файлын мета өгөгдөл) зэрэг кэшлэгдсэн агуулгыг хуваарилсаар байх шиг байна. Асуудлын тайлбараас:

Zombie бүлгүүд: ямар ч процессгүй, устгагдсан боловч санах ойг хуваарилсан хэвээр байгаа бүлгүүд (миний хувьд, шүдний кэшээс, гэхдээ үүнийг хуудасны кэш эсвэл tmpfs-ээс бас хуваарилж болно).

Бүлгийг суллах үед кэш дэх бүх хуудсыг цөм шалгах нь маш удаан байж болох тул залхуу процессыг сонгосон: эдгээр хуудсуудыг дахин хүсэх хүртэл хүлээгээд дараа нь санах ой үнэхээр шаардлагатай үед cgroup-ийг цэвэрлэ. Энэ хүртэл статистик мэдээлэл цуглуулахдаа cgroup-ийг харгалзан үзсэн хэвээр байна.

Гүйцэтгэлийн үүднээс тэд санах ойг гүйцэтгэлийн төлөө золиослосон: зарим кэш санах ойг үлдээснээр эхний цэвэрлэгээг хурдасгасан. Энэ зүгээр. Цөм нь кэшийн санах ойн сүүлчийн хэсгийг ашиглах үед бүлэг эцэстээ цэвэрлэгддэг тул үүнийг "алдагдах" гэж нэрлэх боломжгүй. Харамсалтай нь хайлтын механизмын тодорхой хэрэгжилт memory.stat Энэхүү цөмийн хувилбарт (4.9) манай серверүүд дээрх асар их хэмжээний санах ойтой хослуулсан нь хамгийн сүүлийн үеийн кэш өгөгдлийг сэргээж, бүлгийн зомбинуудыг арилгахад илүү их хугацаа шаардагдана гэсэн үг.

Бидний зарим зангилаанууд маш олон бүлэг зомбитой байсан тул унших болон хоцролт нь секундээс хэтэрсэн байна.

Кадвизорын асуудлыг шийдвэрлэх арга зам нь систем даяар dentries/inode кэшийг нэн даруй чөлөөлөх явдал бөгөөд энэ нь унших хоцролт болон хост дээрх сүлжээний хоцролтыг нэн даруй арилгадаг, учир нь кэшийг цэвэрлэх нь кэштэй cgroup зомби хуудсуудыг идэвхжүүлж, мөн тэдгээрийг чөлөөлдөг. Энэ нь шийдэл биш боловч асуудлын шалтгааныг баталж байна.

Цөмийн шинэ хувилбаруудад (4.19+) дуудлагын гүйцэтгэл сайжирсан нь тогтоогдсон memory.stat, тиймээс энэ цөм рүү шилжсэнээр асуудлыг зассан. Үүний зэрэгцээ бид Кубернетес кластерууд дахь асуудалтай зангилаануудыг илрүүлж, тэдгээрийг сайтар устгаж, дахин ачаалах хэрэгслүүдтэй байсан. Бид бүх кластеруудыг самнаж, хангалттай өндөр хоцрогдолтой зангилаа олоод дахин ачааллаа. Энэ нь үлдсэн серверүүд дээрх үйлдлийн системийг шинэчлэх цагийг бидэнд өгсөн.

Дүгнэж хэлэх

Энэ алдаа нь RX NIC дарааллын боловсруулалтыг хэдэн зуун миллисекундын турш зогсоосон тул богино холболтууд болон MySQL хүсэлтүүд болон хариултын пакетуудын хооронд өндөр хоцролт болон холболтын дунд хугацааны хоцролтыг нэгэн зэрэг үүсгэсэн.

Kubernetes гэх мэт хамгийн үндсэн системүүдийн гүйцэтгэлийг ойлгож, хадгалах нь тэдгээрт суурилсан бүх үйлчилгээний найдвартай байдал, хурдад чухал ач холбогдолтой юм. Таны ажиллуулж буй систем бүр Kubernetes-ийн гүйцэтгэлийн сайжруулалтаас ашиг тус хүртдэг.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх