ProHoster > Blog > administration > En historie om manglende DNS-pakker fra teknisk support til Google Cloud
En historie om manglende DNS-pakker fra teknisk support til Google Cloud
Fra Google Blog Editor: Har du nogensinde spekuleret på, hvordan Google Cloud Technical Solutions (TSE) ingeniører håndterer dine supportanmodninger? TSE's tekniske supportingeniører er ansvarlige for at identificere og rette brugerrapporterede kilder til problemer. Nogle af disse problemer er ret simple, men nogle gange støder du på en billet, der kræver opmærksomhed fra flere ingeniører på én gang. I denne artikel vil en af TSE-medarbejderne fortælle os om et meget vanskeligt problem fra hans seneste praksis - tilfælde af manglende DNS-pakker. I denne historie vil vi se, hvordan ingeniørerne formåede at løse situationen, og hvilke nye ting de lærte, mens de fik rettet fejlen. Vi håber, at denne historie ikke kun oplyser dig om en dybtliggende fejl, men også giver dig indsigt i de processer, der indgår i at indgive en supportbillet til Google Cloud.
Fejlfinding er både en videnskab og en kunst. Det hele starter med at opbygge en hypotese om årsagen til systemets ikke-standardiserede adfærd, hvorefter det testes for styrke. Men før vi formulerer en hypotese, skal vi klart definere og præcist formulere problemet. Hvis spørgsmålet lyder for vagt, så bliver du nødt til at analysere alt omhyggeligt; Dette er "kunsten" ved fejlfinding.
Under Google Cloud bliver sådanne processer eksponentielt mere komplekse, da Google Cloud gør sit bedste for at garantere brugernes privatliv. På grund af dette har TSE-ingeniører hverken adgang til at redigere dine systemer eller mulighed for at se konfigurationer så bredt, som brugere gør. Derfor, for at teste nogen af vores hypoteser, kan vi (ingeniører) ikke hurtigt ændre systemet.
Nogle brugere tror, at vi vil rette alt som mekanik i en biltjeneste og blot sende os id'et for en virtuel maskine, hvorimod processen i virkeligheden foregår i et samtaleformat: indsamler information, danner og bekræfter (eller afkræfter) hypoteser, og i sidste ende er en beslutningsproblemer baseret på kommunikation med klienten.
Det pågældende problem
I dag har vi en historie med en god slutning. En af årsagerne til den vellykkede løsning af den foreslåede sag er en meget detaljeret og præcis beskrivelse af problemet. Nedenfor kan du se en kopi af den første billet (redigeret for at skjule fortrolige oplysninger):
Denne besked indeholder en masse nyttig information for os:
Specifik VM angivet
Selve problemet er angivet - DNS virker ikke
Det er angivet, hvor problemet manifesterer sig - VM og container
De trin, brugeren tog for at identificere problemet, er angivet.
Forespørgslen blev registreret som "P1: Critical Impact - Service Unusable in production", hvilket betyder konstant overvågning af situationen 24/7 i henhold til "Follow the Sun"-ordningen (du kan læse mere om prioritering af brugeranmodninger), med dens overførsel fra et teknisk supportteam til et andet med hvert tidszoneskift. Faktisk, da problemet nåede vores team i Zürich, havde det allerede cirklet rundt om kloden. På dette tidspunkt havde brugeren truffet afværgeforanstaltninger, men var bange for en gentagelse af situationen i produktionen, da årsagen endnu ikke var blevet opdaget.
Da billetten nåede Zürich, havde vi allerede følgende oplysninger ved hånden:
Indhold /etc/hosts
Indhold /etc/resolv.conf
Output iptables-save
Samlet af holdet ngrep pcap fil
Med disse data var vi klar til at begynde "undersøgelses"- og fejlfindingsfasen.
Vores første skridt
Først og fremmest tjekkede vi logfilerne og status for metadataserveren og sikrede os, at den fungerede korrekt. Metadataserveren svarer på IP-adressen 169.254.169.254 og er blandt andet ansvarlig for at kontrollere domænenavne. Vi har også dobbelttjekket, at firewall'en fungerer korrekt med VM'en og ikke blokerer pakker.
Det var en slags mærkeligt problem: nmap-tjekket tilbageviste vores hovedhypotese om tab af UDP-pakker, så vi mentalt fandt på flere muligheder og måder at kontrollere dem på:
Slippes pakker selektivt? => Tjek iptables regler
Er den ikke for lille? MTU? => Tjek output ip a show
Påvirker problemet kun UDP-pakker eller TCP også? => Kør væk dig +tcp
Fungerer libdns korrekt? => Kør væk strace at kontrollere transmissionen af pakker i begge retninger
Her beslutter vi os for at ringe til brugeren for at fejlfinde problemer live.
Under opkaldet er vi i stand til at kontrollere flere ting:
Efter flere kontroller udelukker vi iptables-regler fra listen over årsager
Vi tjekker netværksgrænseflader og routingtabeller og dobbelttjekker, at MTU'en er korrekt
Det opdager vi dig +tcp google.com (TCP) fungerer som det skal, men dig google.com (UDP) virker ikke
Efter at være kørt væk tcpdump det virker stadig dig, finder vi ud af, at UDP-pakker bliver returneret
Vi kører væk strace dig google.com og vi ser, hvordan grave korrekt kalder sendmsg() и recvms(), men den anden afbrydes af en timeout
Desværre kommer afslutningen på skiftet, og vi er tvunget til at eskalere problemet til næste tidszone. Forespørgslen vakte dog interesse i vores team, og en kollega foreslår at oprette den indledende DNS-pakke ved hjælp af det skrappe Python-modul.
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())
Dette fragment opretter en DNS-pakke og sender anmodningen til metadataserveren.
Brugeren kører koden, DNS-svaret returneres, og applikationen modtager det, hvilket bekræfter, at der ikke er noget problem på netværksniveau.
Efter endnu en "jorden rundt tur" vender anmodningen tilbage til vores team, og jeg overfører den fuldstændig til mig selv, og tænker, at det vil være mere bekvemt for brugeren, hvis anmodningen stopper med at cirkle fra sted til sted.
I mellemtiden accepterer brugeren venligst at give et øjebliksbillede af systembilledet. Dette er meget gode nyheder: evnen til selv at teste systemet gør fejlfinding meget hurtigere, fordi jeg ikke længere behøver at bede brugeren om at køre kommandoer, sende mig resultaterne og analysere dem, jeg kan gøre alt selv!
Mine kolleger begynder at misunde mig lidt. Over frokosten diskuterer vi konverteringen, men ingen aner, hvad der foregår. Heldigvis har brugeren selv allerede truffet foranstaltninger for at afbøde konsekvenserne og har ikke travlt, så vi har tid til at dissekere problemet. Og da vi har et billede, kan vi køre alle test, der interesserer os. Store!
At tage et skridt tilbage
Et af de mest populære interviewspørgsmål til systemingeniørstillinger er: "Hvad sker der, når du pinger www.google.com? Spørgsmålet er stort, da kandidaten skal beskrive alt fra shell til brugerplads, til systemkernen og derefter til netværket. Jeg smiler: nogle gange viser interviewspørgsmål sig at være nyttige i det virkelige liv...
Jeg beslutter mig for at anvende dette HR-spørgsmål på et aktuelt problem. Groft sagt, når du forsøger at bestemme et DNS-navn, sker der følgende:
Applikationen kalder et systembibliotek såsom libdns
libdns kontrollerer systemkonfigurationen til hvilken DNS-server den skal kontakte (i diagrammet er dette 169.254.169.254, metadataserver)
libdns bruger systemkald til at oprette en UDP-socket (SOKET_DGRAM) og sende UDP-pakker med en DNS-forespørgsel i begge retninger
Gennem sysctl-grænsefladen kan du konfigurere UDP-stakken på kerneniveau
Kernen interagerer med hardwaren for at transmittere pakker over netværket via netværksgrænsefladen
Hypervisoren fanger og sender pakken til metadataserveren ved kontakt med den
Metadataserveren bestemmer ved sin magi DNS-navnet og returnerer et svar ved hjælp af samme metode
Lad mig minde dig om, hvilke hypoteser vi allerede har overvejet:
Hypotese: Ødelagte biblioteker
Test 1: kør strace i systemet, tjek at dig kalder de rigtige systemkald
Resultat: Korrekte systemopkald kaldes
Test 2: Brug af srapy til at kontrollere, om vi kan bestemme navne, der går uden om systembiblioteker
Resultat: det kan vi
Test 3: kør rpm –V på libdns-pakken og md5sum-biblioteksfiler
Resultat: bibliotekskoden er fuldstændig identisk med koden i det fungerende operativsystem
Test 4: monter brugerens rodsystembillede på en VM uden denne adfærd, kør chroot, se om DNS virker
Resultat: DNS fungerer korrekt
Konklusion baseret på test: problemet er ikke på bibliotekerne
Hypotese: Der er en fejl i DNS-indstillingerne
Test 1: tjek tcpdump og se om DNS-pakker sendes og returneres korrekt efter at have kørt dig
Resultat: pakker transmitteres korrekt
Test 2: dobbelttjek på serveren /etc/nsswitch.conf и /etc/resolv.conf
Resultat: alt er korrekt
Konklusion baseret på test: problemet er ikke med DNS-konfigurationen
Hypotese: kerne beskadiget
Test: installer ny kerne, tjek signatur, genstart
Resultat: lignende adfærd
Konklusion baseret på test: kernen er ikke beskadiget
Hypotese: forkert opførsel af brugernetværket (eller hypervisor-netværksgrænsefladen)
Test 1: Tjek dine firewallindstillinger
Resultat: firewallen sender DNS-pakker til både værten og GCP
Test 2: opsnappe trafik og overvåge rigtigheden af transmissionen og returneringen af DNS-anmodninger
Resultat: tcpdump bekræfter, at værten har modtaget returpakker
Konklusion baseret på test: problemet er ikke i netværket
Hypotese: Metadataserveren virker ikke
Test 1: Tjek metadataserverlogfilerne for uregelmæssigheder
Resultat: der er ingen uregelmæssigheder i loggene
Test 2: Omgå metadataserveren via dig @8.8.8.8
Resultat: Opløsningen er brudt, selv uden brug af en metadataserver
Konklusion baseret på test: problemet er ikke med metadataserveren
Den nederste linje: vi testede alle undersystemer undtagen runtime indstillinger!
Dykker ned i Kernel Runtime Settings
For at konfigurere kerneudførelsesmiljøet kan du bruge kommandolinjeindstillinger (grub) eller sysctl-grænsefladen. Jeg kiggede ind /etc/sysctl.conf og tænk bare, jeg opdagede flere brugerdefinerede indstillinger. Da jeg følte, at jeg havde grebet fat i noget, kasserede jeg alle ikke-netværks- eller ikke-tcp-indstillinger, forblev med bjergindstillingerne net.core. Så gik jeg til, hvor værtstilladelserne var i VM'en og begyndte at anvende indstillingerne en efter en, en efter en, med den ødelagte VM, indtil jeg fandt synderen:
net.core.rmem_default = 2147483647
Her er den, en DNS-brydende konfiguration! Jeg fandt mordvåbnet. Men hvorfor sker det? Jeg havde stadig brug for et motiv.
Den grundlæggende DNS-pakkebufferstørrelse konfigureres via net.core.rmem_default. En typisk værdi er et sted omkring 200KiB, men hvis din server modtager mange DNS-pakker, vil du måske øge bufferstørrelsen. Hvis bufferen er fuld, når der kommer en ny pakke, for eksempel fordi applikationen ikke behandler den hurtigt nok, vil du begynde at miste pakker. Vores klient øgede bufferstørrelsen korrekt, fordi han var bange for datatab, da han brugte et program til at indsamle metrics gennem DNS-pakker. Værdien han satte var den maksimalt mulige: 231-1 (hvis den er sat til 231, vil kernen returnere "UGYLDIG ARGUMENT").
Pludselig indså jeg, hvorfor nmap og scapy fungerede korrekt: de brugte rå sockets! Raw sockets er forskellige fra almindelige sockets: de omgår iptables, og de er ikke bufferet!
Men hvorfor giver "for stor buffer" problemer? Det virker tydeligvis ikke efter hensigten.
På dette tidspunkt kunne jeg reproducere problemet på flere kerner og flere distributioner. Problemet dukkede allerede op på 3.x-kernen, og nu dukkede det også op på 5.x-kernen.
Faktisk ved opstart
sysctl -w net.core.rmem_default=$((2**31-1))
DNS holdt op med at virke.
Jeg begyndte at lede efter arbejdsværdier gennem en simpel binær søgealgoritme og fandt ud af, at systemet fungerede med 2147481343, men dette tal var et meningsløst sæt tal for mig. Jeg foreslog klienten at prøve dette nummer, og han svarede, at systemet fungerede med google.com, men stadig gav en fejl med andre domæner, så jeg fortsatte min undersøgelse.
jeg har installeret dropwatch, et værktøj, der burde have været brugt tidligere: det viser præcis, hvor i kernen en pakke ender. Synderen var funktionen udp_queue_rcv_skb. Jeg downloadede kernekilderne og tilføjede et par stykker funktionprintk at spore, hvor præcis pakken ender. Jeg fandt hurtigt den rigtige tilstand if, og stirrede simpelthen på det i noget tid, for det var da, at alt endelig kom sammen til et helt billede: 231-1, et meningsløst tal, et ikke-fungerende domæne... Det var et stykke kode i __udp_enqueue_schedule_skb:
if (rmem > (size + sk->sk_rcvbuf))
goto uncharge_drop;
Vær opmærksom:
rmem er af typen int
size er af typen u16 (unsigned sixteen-bit int) og gemmer pakkestørrelsen
sk->sk_rcybuf er af typen int og gemmer bufferstørrelsen, som per definition er lig med værdien i net.core.rmem_default
Hvornår sk_rcvbuf nærmer sig 231, kan summering af pakkestørrelsen resultere i heltalsoverløb. Og da det er en int, bliver dens værdi negativ, så betingelsen bliver sand, når den skulle være falsk (det kan du læse mere om på link).
Fejlen kan rettes på en triviel måde: ved støbning unsigned int. Jeg anvendte rettelsen og genstartede systemet, og DNS virkede igen.
Smag af sejr
Jeg videresendte mine resultater til klienten og sendte LKML kernel patch. Jeg er glad: Hver brik i puslespillet passer sammen, jeg kan forklare præcis, hvorfor vi observerede det, vi observerede, og vigtigst af alt, vi var i stand til at finde en løsning på problemet takket være vores teamwork!
Det er værd at erkende, at sagen viste sig at være sjælden, og heldigvis modtager vi sjældent så komplekse forespørgsler fra brugere.