ProHoster > Blog > Adminisztráció > Történet a Google Cloud technikai támogatásának hiányzó DNS-csomagjairól
Történet a Google Cloud technikai támogatásának hiányzó DNS-csomagjairól
A Google Blogszerkesztőből: Gondolkozott már azon, hogyan kezelik a Google Cloud Technical Solutions (TSE) mérnökei az Ön támogatási kéréseit? A TSE műszaki támogatási mérnökei felelősek a felhasználók által jelentett problémaforrások azonosításáért és kijavításáért. Néhány ilyen probléma meglehetősen egyszerű, de néha olyan jegyekkel találkozhatunk, amelyek egyszerre több mérnök figyelmét igénylik. Ebben a cikkben a TSE egyik alkalmazottja egy nagyon trükkös problémáról mesél a közelmúltbeli gyakorlatából - hiányzó DNS-csomagok esete. Ebben a történetben azt láthatjuk, hogyan sikerült a mérnököknek megoldani a helyzetet, és milyen újdonságokat tanultak a hiba elhárítása során. Reméljük, hogy ez a történet nem csak egy mélyen gyökerező hibáról ad felvilágosítást, hanem betekintést nyújt a Google Cloud támogatási kérelmének benyújtásához szükséges folyamatokba is.
A hibaelhárítás egyszerre tudomány és művészet. Minden azzal kezdődik, hogy felállítunk egy hipotézist a rendszer nem szabványos viselkedésének okáról, majd teszteljük annak erejét. Mielőtt azonban hipotézist fogalmaznánk meg, egyértelműen meg kell határoznunk és pontosan meg kell fogalmaznunk a problémát. Ha a kérdés túl homályosan hangzik, akkor mindent alaposan elemeznie kell; Ez a hibaelhárítás „művészete”.
A Google Cloud alatt az ilyen folyamatok exponenciálisan bonyolultabbá válnak, mivel a Google Cloud mindent megtesz, hogy garantálja a felhasználók magánéletét. Emiatt a TSE mérnökei nem férhetnek hozzá a rendszer szerkesztéséhez, és nem tekinthetik meg a konfigurációkat olyan széles körben, mint a felhasználók. Ezért, hogy bármelyik hipotézisünket teszteljük, mi (mérnökök) nem tudjuk gyorsan módosítani a rendszert.
Egyes felhasználók úgy gondolják, hogy mindent megjavítunk, mint a mechanikát az autószervizben, és egyszerűen elküldjük nekünk egy virtuális gép azonosítóját, míg a valóságban a folyamat társalgási formában zajlik: információgyűjtés, hipotézisek kialakítása és megerősítése (vagy megcáfolása), és végül a döntési problémák az ügyféllel való kommunikáción alapulnak.
A kérdéses probléma
Ma van egy történetünk, amelynek jó a vége. A javasolt ügy sikeres megoldásának egyik oka a probléma nagyon részletes és pontos leírása. Alább láthatja az első jegy másolatát (a bizalmas információk elrejtésére szerkesztve):
Ez az üzenet sok hasznos információt tartalmaz számunkra:
Meghatározott virtuális gép
Maga a probléma jelzi - a DNS nem működik
Jelzi, hogy hol jelenik meg a probléma - virtuális gép és tároló
Megjelenik a felhasználó által a probléma azonosítására tett lépések.
A kérést „P1: Critical Impact – Service Unusable in Production” néven regisztráltuk, ami a helyzet folyamatos, 24 órás megfigyelését jelenti a „Kövesd a Napot” séma szerint (további információ a a felhasználói kérések prioritásait). Sőt, mire a probléma elérte a zürichi csapatunkat, már körbejárta a világot. Ekkorra a felhasználó enyhítő intézkedéseket hozott, de attól tartott, hogy a gyártás során megismétlődik a helyzet, mivel a kiváltó okot még nem fedezték fel.
Mire a jegy Zürichbe ért, már a következő információkkal rendelkeztünk:
Tartalom /etc/hosts
Tartalom /etc/resolv.conf
Teljesítmény iptables-save
Összeállította a csapat ngrep pcap fájl
Ezekkel az adatokkal készen álltunk a „nyomozási” és hibaelhárítási szakasz megkezdésére.
Első lépéseink
Először is ellenőriztük a metaadat-szerver naplóit és állapotát, és megbizonyosodtunk arról, hogy megfelelően működik. A metaadat-szerver a 169.254.169.254 IP-címre válaszol, és többek között a tartománynevek vezérléséért is felelős. Kétszer is ellenőriztük, hogy a tűzfal megfelelően működik-e a virtuális géppel, és nem blokkolja-e a csomagokat.
Valami furcsa probléma volt: az nmap ellenőrzés megcáfolta az UDP-csomagok elvesztésével kapcsolatos fő hipotézisünket, így gondolatban még több lehetőséget és módot találtunk ezek ellenőrzésére:
Szelektíven dobják el a csomagokat? => Ellenőrizze az iptables szabályokat
Nem túl kicsi? MTU? => Ellenőrizze a kimenetet ip a show
A probléma csak az UDP-csomagokat vagy a TCP-t is érinti? => Vezess el dig +tcp
A dig generált csomagokat visszaküldik? => Vezess el tcpdump
A libdns megfelelően működik? => Vezess el strace hogy ellenőrizze a csomagok átvitelét mindkét irányban
Itt úgy döntünk, hogy felhívjuk a felhasználót a problémák élő elhárítása érdekében.
A hívás során több dolgot ellenőrizhetünk:
Többszöri ellenőrzés után kizárjuk az iptables szabályokat az okok listájából
Ellenőrizzük a hálózati interfészeket és az útválasztási táblákat, és kétszer is ellenőrizzük, hogy az MTU helyes-e
Azt fedezzük fel dig +tcp google.com (TCP) úgy működik, ahogy kell, de dig google.com (UDP) nem működik
Miután elhajtott tcpdump munka közben dig, azt tapasztaljuk, hogy UDP-csomagokat küldenek vissza
Elhajtunk strace dig google.com és látjuk, hogyan dig helyesen hív sendmsg() и recvms(), azonban a másodikat időtúllépés szakítja meg
Sajnos elérkezik a műszak vége, és kénytelenek vagyunk a következő időzónára terelni a problémát. A kérés azonban felkeltette az érdeklődést csapatunkban, és egy kolléga azt javasolja, hogy a kezdeti DNS-csomagot hozzuk létre a scrapy Python modul segítségével.
from scapy.all import *
answer = sr1(IP(dst="169.254.169.254")/UDP(dport=53)/DNS(rd=1,qd=DNSQR(qname="google.com")),verbose=0)
print ("169.254.169.254", answer[DNS].summary())
Ez a töredék létrehoz egy DNS-csomagot, és elküldi a kérést a metaadat-kiszolgálónak.
A felhasználó lefuttatja a kódot, visszaküldi a DNS-választ, és az alkalmazás megkapja azt, megerősítve, hogy hálózati szinten nincs probléma.
Egy újabb „világkörüli utazás” után a kérés visszatér a csapatunkhoz, én pedig teljesen átteszem magamnak, gondolván, hogy a felhasználó számára kényelmesebb lesz, ha a kérés megszűnik egyik helyről a másikra keringeni.
Addig is a felhasználó beleegyezik, hogy pillanatképet készít a rendszerképről. Ez nagyon jó hír: a rendszer saját tesztelésének lehetősége sokkal gyorsabbá teszi a hibaelhárítást, mert már nem kell a felhasználót parancsok futtatására, az eredmények elküldésére és elemzésére kérnem, mindent magam is meg tudok csinálni!
A kollégáim kezdenek kicsit irigyelni. Ebéd közben megbeszéljük az átalakítást, de senkinek fogalma sincs, mi történik. Szerencsére a felhasználó maga is tett már intézkedéseket a következmények enyhítésére, és nem siet, így van időnk a probléma boncolgatására. És mivel van képünk, bármilyen tesztet lefuttathatunk, ami érdekel. Nagy!
Hátrál egy lépést
Az egyik legnépszerűbb interjúkérdés rendszermérnöki pozíciókhoz: „Mi történik, ha pingel? www.google.com? A kérdés nagyszerű, hiszen a jelöltnek mindent le kell írnia a shelltől a felhasználói területig, a rendszermagig, majd a hálózatig. Mosolygok: az interjúkérdések néha a való életben is hasznosnak bizonyulnak...
Úgy döntök, hogy ezt a HR-kérdést egy aktuális problémára alkalmazom. Durván szólva, amikor megpróbálja meghatározni a DNS-nevet, a következő történik:
Az alkalmazás rendszerkönyvtárat hív meg, például a libdns-t
A libdns ellenőrzi a rendszerkonfigurációt, hogy melyik DNS-kiszolgálóval kell kapcsolatba lépnie (a diagramon ez a 169.254.169.254, metaadat-kiszolgáló)
A libdns rendszerhívásokat használ egy UDP socket (SOKET_DGRAM) létrehozására és az UDP-csomagok DNS-lekérdezéssel történő elküldésére mindkét irányban
A sysctl felületen keresztül konfigurálhatja az UDP-vermet a kernel szintjén
A kernel kölcsönhatásba lép a hardverrel, hogy csomagokat továbbítson a hálózaton keresztül a hálózati interfészen keresztül
A hypervisor elkapja és továbbítja a csomagot a metaadat-kiszolgálónak, amikor kapcsolatba lép vele
A metaadat-szerver varázsereje révén meghatározza a DNS-nevet, és ugyanazzal a módszerrel ad vissza választ
Hadd emlékeztessem Önöket arra, hogy milyen hipotézisekkel foglalkoztunk már:
Hipotézis: Törött könyvtárak
1. teszt: futtassa a strace-t a rendszerben, ellenőrizze, hogy a dig a megfelelő rendszerhívásokat hívja-e meg
Eredmény: Megtörténik a megfelelő rendszerhívások hívása
2. teszt: srapy segítségével ellenőrizzük, hogy meg tudjuk-e határozni a rendszerkönyvtárakat megkerülő neveket
Eredmény: megtehetjük
3. teszt: futtassa az rpm –V parancsot a libdns csomagon és az md5sum könyvtárfájlokon
Eredmény: a könyvtár kódja teljesen megegyezik a működő operációs rendszer kódjával
4. teszt: csatolja fel a felhasználó gyökérrendszerének képét egy virtuális gépre anélkül, hogy ez a viselkedés, futtassa a chrootot, ellenőrizze, működik-e a DNS
Eredmény: A DNS megfelelően működik
Következtetés a tesztek alapján: a probléma nem a könyvtárakban van
Hipotézis: Hiba van a DNS-beállításokban
1. teszt: ellenőrizze a tcpdump-ot, és ellenőrizze, hogy a DNS-csomagok elküldése és visszaadása helyes-e a dig futtatása után
Eredmény: a csomagok továbbítása megfelelően megtörtént
2. teszt: ellenőrizze kétszer a szervert /etc/nsswitch.conf и /etc/resolv.conf
Eredmény: minden rendben van
Következtetés a tesztek alapján: a probléma nem a DNS konfigurációval van
Hipotézis: mag sérült
Teszt: új kernel telepítése, aláírás ellenőrzése, újraindítás
Eredmény: hasonló viselkedés
Következtetés a tesztek alapján: a kernel nem sérült
Hipotézis: a felhasználói hálózat (vagy a hypervisor hálózati interfész) helytelen viselkedése
1. teszt: Ellenőrizze a tűzfal beállításait
Eredmény: a tűzfal DNS-csomagokat ad át mind a gazdagépen, mind a GCP-n
2. teszt: elfogja a forgalmat, és figyelemmel kíséri a DNS-kérések átvitelének és visszaküldésének helyességét
Eredmény: A tcpdump megerősíti, hogy a gazdagép megkapta a visszatérő csomagokat
Következtetés a tesztek alapján: a probléma nem a hálózatban van
Hipotézis: a metaadat-szerver nem működik
1. teszt: ellenőrizze a metaadat-kiszolgáló naplóit anomáliák szempontjából
Eredmény: nincsenek anomáliák a naplókban
2. teszt: Kerülje meg a metaadat-kiszolgálót ezen keresztül dig @8.8.8.8
Eredmény: A felbontás még metaadat-szerver használata nélkül is hibás
Következtetés a tesztek alapján: a probléma nem a metaadat szerverrel van
Az alsó sor: minden alrendszert teszteltünk, kivéve futásidejű beállítások!
Búvárkodás a kernel futásidejű beállításaiban
A kernel végrehajtási környezetének konfigurálásához használhatja a parancssori opciókat (grub) vagy a sysctl felületet. benéztem /etc/sysctl.conf és gondolj csak bele, számos egyéni beállítást fedeztem fel. Úgy éreztem, hogy megragadtam valamit, elvetettem minden nem hálózati vagy nem tcp beállítást, és a hegyi beállításoknál maradtam net.core. Aztán odamentem, ahol a gazdajogosultságok voltak a virtuális gépben, és elkezdtem egyenként, egymás után alkalmazni a beállításokat a törött virtuális géppel, amíg meg nem találtam a tettest:
net.core.rmem_default = 2147483647
Íme, egy DNS-törő konfiguráció! Megtaláltam a gyilkos fegyvert. De miért történik ez? Még mindig szükségem volt egy indítékra.
Az alapvető DNS-csomagpuffer mérete a következőn keresztül van konfigurálva net.core.rmem_default. A tipikus érték valahol 200KiB körül van, de ha a szervere sok DNS-csomagot kap, érdemes lehet növelni a puffer méretét. Ha a puffer megtelt, amikor új csomag érkezik, például azért, mert az alkalmazás nem dolgozza fel elég gyorsan, akkor a csomagok elvesznek. Ügyfelünk helyesen növelte a puffer méretét, mert félt az adatvesztéstől, mivel DNS-csomagokon keresztül mérőszámokat gyűjtött. Az általa beállított érték a lehető legnagyobb volt: 231-1 (ha 231-re van állítva, a kernel „INVALID ARGUMENT”-et ad vissza).
Hirtelen rájöttem, hogy az nmap és a scapy miért működik megfelelően: nyers socketeket használnak! A nyers socketek eltérnek a hagyományos socketektől: megkerülik az iptable-t, és nincsenek pufferelve!
De miért okoz problémát a "túl nagy puffer"? Nyilvánvalóan nem úgy működik, ahogy tervezték.
Ezen a ponton több kernelen és több disztribúción is reprodukálhattam a problémát. A probléma már megjelent a 3.x-es kernelen, és most az 5.x-es kernelen is.
Valóban, indításkor
sysctl -w net.core.rmem_default=$((2**31-1))
A DNS leállt.
Elkezdtem keresni a működő értékeket egy egyszerű bináris keresőalgoritmus segítségével, és megállapítottam, hogy a rendszer a 2147481343-mal működik, de ez a szám számomra értelmetlen számkészlet volt. Javasoltam az ügyfélnek, hogy próbálja ki ezt a számot, és ő azt válaszolta, hogy a rendszer működik a google.com webhelyen, de továbbra is hibát adott más domaineknél, ezért folytattam a vizsgálatot.
telepítettem cseppóra, egy olyan eszköz, amelyet korábban kellett volna használni: pontosan megmutatja, hogy a kernelben hova kerül egy csomag. A bűnös a funkció volt udp_queue_rcv_skb. Letöltöttem a kernelforrásokat, és hozzáadtam néhányat függvényprintk nyomon követni, hogy a csomag pontosan hol végződik. Gyorsan megtaláltam a megfelelő állapotot if, és egy ideig egyszerűen csak bámulta, mert ekkor állt végre minden egy teljes képbe: 231-1, értelmetlen szám, nem működő domain... Ez egy kódrészlet volt a __udp_enqueue_schedule_skb:
if (rmem > (size + sk->sk_rcvbuf))
goto uncharge_drop;
Kérjük, vegye figyelembe:
rmem int típusú
size u16 típusú (előjel nélküli tizenhat bites int), és tárolja a csomagméretet
sk->sk_rcybuf int típusú, és a pufferméretet tárolja, amely definíció szerint megegyezik az in értékkel net.core.rmem_default
Mikor sk_rcvbuf megközelíti a 231-et, a csomagméret összegzése azt eredményezheti, hogy integer túlcsordulás. És mivel ez egy int, az értéke negatív lesz, így a feltétel akkor válik igazzá, amikor hamisnak kellene lennie (erről bővebben itt olvashat link).
A hiba triviális módon javítható: öntéssel unsigned int. Alkalmaztam a javítást és újraindítottam a rendszert, és a DNS ismét működött.
A győzelem íze
A leleteimet továbbítottam az ügyfélnek és elküldtem LKML kernel javítás. Örülök: a puzzle minden darabja passzol egymáshoz, pontosan el tudom magyarázni, hogy miért figyeltük meg azt, amit megfigyeltünk, és ami a legfontosabb, csapatmunkánknak köszönhetően tudtunk megoldást találni a problémára!
Érdemes tudni, hogy az eset ritkanak bizonyult, és szerencsére ritkán kapunk ilyen összetett kéréseket a felhasználóktól.