Zgodba o manjkajočih paketih DNS tehnične podpore Google Cloud

Iz Googlovega urejevalnika spletnih dnevnikov: Ste se kdaj vprašali, kako inženirji Google Cloud Technical Solutions (TSE) obravnavajo vaše zahteve za podporo? Inženirji tehnične podpore TSE so odgovorni za prepoznavanje in odpravljanje virov težav, o katerih poročajo uporabniki. Nekatere od teh težav so precej preproste, včasih pa naletite na napako, ki zahteva pozornost več inženirjev hkrati. V tem članku nam bo eden od zaposlenih v TSE povedal o eni zelo kočljivi težavi iz svoje nedavne prakse - primeru manjkajočih paketov DNS. V tej zgodbi bomo videli, kako je inženirjem uspelo rešiti situacijo in kaj novega so se naučili med odpravljanjem napake. Upamo, da vas ta zgodba ne samo pouči o globoko zakoreninjeni napaki, ampak vam tudi da vpogled v postopke, ki so potrebni za vložitev zahteve za podporo pri Google Cloud.

Zgodba o manjkajočih paketih DNS tehnične podpore Google Cloud

Odpravljanje težav je hkrati znanost in umetnost. Vse se začne z izgradnjo hipoteze o razlogu za nestandardno vedenje sistema, nato pa se testira na trdnost. Preden pa postavimo hipotezo, moramo jasno opredeliti in natančno formulirati problem. Če se vprašanje sliši preveč nejasno, boste morali vse skrbno analizirati; To je "umetnost" odpravljanja težav.

V storitvi Google Cloud postanejo takšni procesi eksponentno bolj zapleteni, saj se Google Cloud trudi po svojih najboljših močeh zagotoviti zasebnost svojih uporabnikov. Zaradi tega inženirji TSE nimajo dostopa za urejanje vaših sistemov niti možnosti ogleda konfiguracij tako široko kot uporabniki. Zato mi (inženirji) ne moremo hitro spremeniti sistema, da bi preizkusili katero koli od naših hipotez.

Nekateri uporabniki menijo, da bomo vse popravili kot mehaniki v avtoservisu, in nam preprosto pošljejo id virtualnega stroja, v resnici pa proces poteka v pogovorni obliki: zbiranje informacij, oblikovanje in potrjevanje (ali ovrženje) hipotez, in na koncu težave pri odločitvi temeljijo na komunikaciji s stranko.

Zadevna težava

Danes imamo zgodbo z dobrim koncem. Eden od razlogov za uspešno rešitev predlaganega primera je zelo podroben in natančen opis problema. Spodaj si lahko ogledate kopijo prve vstopnice (urejeno, da se skrijejo zaupni podatki):
Zgodba o manjkajočih paketih DNS tehnične podpore Google Cloud
To sporočilo vsebuje veliko koristnih informacij za nas:

  • Določen VM
  • Navedena je sama težava - DNS ne deluje
  • Navedeno je, kje se težava manifestira - VM in vsebnik
  • Navedeni so koraki, ki jih je uporabnik izvedel za odkrivanje težave.

Zahteva je bila registrirana kot “P1: Kritičen vpliv - storitev neuporabna v produkciji”, kar pomeni stalno spremljanje situacije 24/7 po shemi “Sledi soncu” (več o prioritete uporabniških zahtev), s prenosom iz ene ekipe tehnične podpore v drugo z vsakim premikom časovnega pasu. Pravzaprav je do takrat, ko je težava dosegla našo ekipo v Zürichu, že obkrožila svet. Uporabnik je v tem času sprejel omilitvene ukrepe, vendar se je bal ponovitve situacije v proizvodnji, saj vzrok še ni bil odkrit.

Ko je vozovnica prispela v Zürich, smo že imeli naslednje informacije:

  • Vsebina /etc/hosts
  • Vsebina /etc/resolv.conf
  • Izhod iptables-save
  • Sestavila ekipa ngrep pcap datoteko

S temi podatki smo bili pripravljeni na začetek faze "preiskave" in odpravljanja težav.

Naši prvi koraki

Najprej smo preverili dnevnike in status metapodatkovnega strežnika ter se prepričali, da deluje pravilno. Metapodatkovni strežnik se odziva na naslov IP 169.254.169.254 in je med drugim odgovoren za nadzor domenskih imen. Dvakrat smo tudi preverili, ali požarni zid pravilno deluje z VM in ne blokira paketov.

Šlo je za nekakšno čudno težavo: preverjanje nmap je ovrglo našo glavno hipotezo o izgubi paketov UDP, zato smo v mislih našli več možnosti in načinov, kako jih preveriti:

  • Ali se paketi odvržejo selektivno? => Preverite pravila iptables
  • Ali ni premajhen? MTU? => Preverite izhod ip a show
  • Ali težava vpliva samo na pakete UDP ali tudi na TCP? => Odpeljite se dig +tcp
  • Ali se vrnejo paketi, ustvarjeni z dig? => Odpeljite se tcpdump
  • Ali libdns deluje pravilno? => Odpeljite se strace za preverjanje prenosa paketov v obe smeri

Tu se odločimo, da uporabnika pokličemo za odpravo težav v živo.

Med klicem lahko preverimo več stvari:

  • Po več preverjanjih izključimo pravila iptables s seznama razlogov
  • Preverimo omrežne vmesnike in usmerjevalne tabele ter dvakrat preverimo, ali je MTU pravilen
  • To odkrijemo dig +tcp google.com (TCP) deluje kot mora, vendar dig google.com (UDP) ne deluje
  • Ko se je odpeljal tcpdump še vedno deluje dig, ugotovimo, da se vračajo paketi UDP
  • Odpeljemo se strace dig google.com in vidimo, kako dig pravilno kliče sendmsg() и recvms(), vendar je drugi prekinjen s časovno omejitvijo

Na žalost pride konec izmene in prisiljeni smo težavo eskalirati v naslednji časovni pas. Zahteva pa je v naši ekipi vzbudila zanimanje in kolega predlaga izdelavo začetnega paketa DNS z uporabo scrapy modula Python.

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())

Ta fragment ustvari paket DNS in pošlje zahtevo strežniku metapodatkov.

Uporabnik zažene kodo, vrne se odgovor DNS in aplikacija ga prejme, kar potrjuje, da na ravni omrežja ni težave.

Po drugem "potovanju po svetu" se zahteva vrne naši ekipi in jo popolnoma prenesem k sebi, saj menim, da bo za uporabnika bolj priročno, če zahteva preneha krožiti od kraja do kraja.

V vmesnem času se uporabnik vljudno strinja s posredovanjem posnetka slike sistema. To je zelo dobra novica: z možnostjo, da sam testiram sistem, je odpravljanje težav veliko hitrejše, ker mi ni več treba prositi uporabnika, da zažene ukaze, mi pošlje rezultate in jih analizira, vse lahko naredim sam!

Kolegi mi začnejo kar malo zavidati. Med kosilom se pogovarjamo o pretvorbi, vendar nihče nima pojma, kaj se dogaja. Na srečo je uporabnik sam že sprejel ukrepe za omilitev posledic in se mu nikamor ne mudi, tako da imamo čas za seciranje problema. In ker imamo sliko, lahko izvajamo vse teste, ki nas zanimajo. Super!

Korak nazaj

Eno izmed najbolj priljubljenih vprašanj na intervjuju za položaje sistemskega inženirja je: »Kaj se zgodi, ko pingate www.google.com? Vprašanje je super, saj mora kandidat opisati vse od lupine do uporabniškega prostora, do sistemskega jedra in nato do omrežja. Nasmehnem se: včasih se vprašanja za razgovor izkažejo za koristna v resničnem življenju ...

Odločil sem se, da bom to kadrovsko vprašanje uporabil za trenutni problem. Grobo rečeno, ko poskušate določiti ime DNS, se zgodi naslednje:

  1. Aplikacija kliče sistemsko knjižnico, kot je libdns
  2. libdns preveri sistemsko konfiguracijo, s katerim DNS strežnikom se mora povezati (v diagramu je to 169.254.169.254, metapodatkovni strežnik)
  3. libdns uporablja sistemske klice za ustvarjanje vtičnice UDP (SOKET_DGRAM) in pošiljanje paketov UDP s poizvedbo DNS v obe smeri
  4. Preko vmesnika sysctl lahko konfigurirate sklad UDP na ravni jedra
  5. Jedro sodeluje s strojno opremo za prenos paketov po omrežju prek omrežnega vmesnika
  6. Hipervizor ujame in posreduje paket metapodatkovnemu strežniku ob stiku z njim
  7. Metapodatkovni strežnik po svoji čarovniji določi ime DNS in vrne odgovor z isto metodo

Zgodba o manjkajočih paketih DNS tehnične podpore Google Cloud
Naj vas spomnim, katere hipoteze smo že obravnavali:

Hipoteza: pokvarjene knjižnice

  • Preizkus 1: zaženite strace v sistemu, preverite, ali dig kliče pravilne sistemske klice
  • Rezultat: Poklicani so pravilni sistemski klici
  • Test 2: uporaba srapy za preverjanje, ali lahko določimo imena mimo sistemskih knjižnic
  • Rezultat: zmoremo
  • Test 3: zaženite rpm –V na paketu libdns in datotekah knjižnice md5sum
  • Rezultat: koda knjižnice je popolnoma enaka kodi v delujočem operacijskem sistemu
  • Preizkus 4: namestite sliko korenskega sistema uporabnika na VM brez tega vedenja, zaženite chroot, preverite, ali DNS deluje
  • Rezultat: DNS deluje pravilno

Zaključek na podlagi testov: problem ni v knjižnicah

Hipoteza: Prišlo je do napake v nastavitvah DNS

  • Test 1: preverite tcpdump in preverite, ali so paketi DNS pravilno poslani in vrnjeni po izvajanju dig
  • Rezultat: paketi se prenašajo pravilno
  • Test 2: dvakrat preverite na strežniku /etc/nsswitch.conf и /etc/resolv.conf
  • Rezultat: vse je pravilno

Zaključek na podlagi testov: težava ni v konfiguraciji DNS

Hipoteza: jedro poškodovano

  • Test: namestite novo jedro, preverite podpis, znova zaženite
  • Rezultat: podobno vedenje

Zaključek na podlagi testov: jedro ni poškodovano

Hipoteza: nepravilno obnašanje uporabniškega omrežja (ali omrežnega vmesnika hipervizorja)

  • 1. preizkus: Preverite nastavitve požarnega zidu
  • Rezultat: požarni zid posreduje DNS pakete gostitelju in GCP
  • Test 2: prestrezanje prometa in spremljanje pravilnosti prenosa in vračanja DNS zahtev
  • Rezultat: tcpdump potrdi, da je gostitelj prejel povratne pakete

Zaključek na podlagi testov: problem ni v omrežju

Hipoteza: metapodatkovni strežnik ne deluje

  • 1. preizkus: preverite, ali so v dnevnikih strežnika metapodatkov nepravilnosti
  • Rezultat: v dnevnikih ni nobenih nepravilnosti
  • Preizkus 2: Obidite strežnik metapodatkov prek dig @8.8.8.8
  • Rezultat: Ločljivost je motena tudi brez uporabe strežnika metapodatkov

Zaključek na podlagi testov: težava ni v strežniku metapodatkov

Bottom line: testirali smo vse podsisteme razen nastavitve časa izvajanja!

Poglobitev v nastavitve izvajalnega okolja jedra

Če želite konfigurirati okolje za izvajanje jedra, lahko uporabite možnosti ukazne vrstice (grub) ali vmesnik sysctl. Pogledal sem noter /etc/sysctl.conf in samo pomislite, odkril sem več nastavitev po meri. Občutek, kot da sem nekaj prijel, sem zavrgel vse neomrežne ali ne-tcp nastavitve in ostal pri gorskih nastavitvah net.core. Nato sem šel tja, kjer so bila dovoljenja gostitelja v VM, in začel uporabljati nastavitve eno za drugo, eno za drugo, s pokvarjenim VM, dokler nisem našel krivca:

net.core.rmem_default = 2147483647

Tukaj je konfiguracija, ki zlomi DNS! Našel sem morilsko orožje. Toda zakaj se to dogaja? Še vedno sem potreboval motiv.

Osnovna velikost medpomnilnika paketov DNS je konfigurirana prek net.core.rmem_default. Tipična vrednost je nekje okoli 200 KB, vendar če vaš strežnik prejme veliko paketov DNS, boste morda želeli povečati velikost medpomnilnika. Če je medpomnilnik poln, ko prispe nov paket, na primer zato, ker ga aplikacija ne obdeluje dovolj hitro, boste začeli izgubljati pakete. Naš naročnik je pravilno povečal velikost medpomnilnika, ker se je bal izgube podatkov, saj je uporabljal aplikacijo za zbiranje metrik preko DNS paketov. Vrednost, ki jo je nastavil, je bila največja možna: 231-1 (če je nastavljena na 231, bo jedro vrnilo "NEVELJAVNI ARGUMENT").

Nenadoma sem ugotovil, zakaj sta nmap in scapy delovala pravilno: uporabljala sta neobdelana vtičnica! Neobdelane vtičnice se razlikujejo od običajnih vtičnic: obidejo iptables in niso medpomnilniki!

Toda zakaj "buffer prevelik" povzroča težave? Očitno ne deluje, kot je predvideno.

Na tej točki bi lahko reproduciral težavo na več jedrih in več distribucijah. Težava se je pojavila že na jedru 3.x, sedaj pa se je pojavila tudi na jedru 5.x.

Dejansko ob zagonu

sysctl -w net.core.rmem_default=$((2**31-1))

DNS je prenehal delovati.

Začel sem iskati delujoče vrednosti s preprostim algoritmom binarnega iskanja in ugotovil, da sistem deluje z 2147481343, vendar je bila ta številka zame nesmiselna množica številk. Stranki sem predlagal, da poskusi to številko, on pa je odgovoril, da sistem deluje z google.com, vendar še vedno daje napako z drugimi domenami, zato sem nadaljeval preiskavo.

namestil sem padajoča ura, orodje, ki bi ga morali uporabiti prej: natančno pokaže, kje v jedru konča paket. Krivec je bila funkcija udp_queue_rcv_skb. Prenesel sem izvorne kode jedra in jih nekaj dodal funkcije printk da sledite, kje točno konča paket. Hitro sem našel pravo stanje if, in nekaj časa preprosto strmel vanjo, saj se je takrat končno vse skupaj sestavilo v celoto: 231-1, nesmiselna številka, nedelujoča domena ... To je bil delček kode v __udp_enqueue_schedule_skb:

if (rmem > (size + sk->sk_rcvbuf))
		goto uncharge_drop;

Prosimo, upoštevajte:

  • rmem je vrste int
  • size je tipa u16 (nepredznačeno šestnajstbitno int) in shrani velikost paketa
  • sk->sk_rcybuf je tipa int in shrani velikost medpomnilnika, ki je po definiciji enaka vrednosti v net.core.rmem_default

Pri sk_rcvbuf se približa 231, lahko seštevek velikosti paketa povzroči celoštevilski preliv. In ker je int, njegova vrednost postane negativna, tako da pogoj postane resničen, ko bi moral biti false (več o tem lahko preberete na povezava).

Napako je mogoče popraviti na trivialen način: z ulivanjem unsigned int. Uporabil sem popravek in znova zagnal sistem in DNS je spet deloval.

Okus zmage

Svoje ugotovitve sem posredoval naročniku in poslal LKML popravek jedra. Zadovoljen sem: vsak košček sestavljanke se ujema, natančno lahko pojasnim, zakaj smo opazili to, kar smo opazili, in kar je najpomembneje, zahvaljujoč timskemu delu smo lahko našli rešitev problema!

Treba je priznati, da se je izkazalo, da je primer redek in na srečo le redko prejmemo tako zapletene zahteve uporabnikov.

Zgodba o manjkajočih paketih DNS tehnične podpore Google Cloud


Vir: www.habr.com

Dodaj komentar