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.

En historie om manglende DNS-pakker fra teknisk support 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):
En historie om manglende DNS-pakker fra teknisk support til Google Cloud
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
  • Returneres grave-genererede pakker? => Kør væk tcpdump
  • 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:

  1. Applikationen kalder et systembibliotek såsom libdns
  2. libdns kontrollerer systemkonfigurationen til hvilken DNS-server den skal kontakte (i diagrammet er dette 169.254.169.254, metadataserver)
  3. libdns bruger systemkald til at oprette en UDP-socket (SOKET_DGRAM) og sende UDP-pakker med en DNS-forespørgsel i begge retninger
  4. Gennem sysctl-grænsefladen kan du konfigurere UDP-stakken på kerneniveau
  5. Kernen interagerer med hardwaren for at transmittere pakker over netværket via netværksgrænsefladen
  6. Hypervisoren fanger og sender pakken til metadataserveren ved kontakt med den
  7. Metadataserveren bestemmer ved sin magi DNS-navnet og returnerer et svar ved hjælp af samme metode

En historie om manglende DNS-pakker fra teknisk support til Google Cloud
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 funktion printk 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.

En historie om manglende DNS-pakker fra teknisk support til Google Cloud


Kilde: www.habr.com

Tilføj en kommentar