Povestea despre pachetele DNS pierdute de la suportul tehnic Google Cloud

De la redactorul blogului Google: Te-ai întrebat vreodată cum intervin inginerii Google Cloud Technical Solutions (TSE) în solicitările tale de suport tehnic? Inginerii de suport tehnic TSE au responsabilitatea de a identifica și rezolva sursele problemelor semnalate de utilizatori. Unele dintre aceste probleme sunt destul de simple, dar uneori apare o solicitare care necesită atenția a mai multor ingineri. În acest articol, unul dintre angajații TSE ne va povesti despre o problemă foarte complexă din practica sa recentă — cazul pachetelor DNS pierdute. În timpul acestei povești, vom observa cum au reușit inginerii să rezolve situația și ce nouă cunoștințe au dobândit în timpul remedierii erorii. Sperăm că această poveste nu doar că va explica un bug adânc înrădăcinat, ci va oferi și o înțelegere a proceselor care au loc atunci când se face o solicitare de suport la Google Cloud.

Povestea despre pachetele DNS pierdute de la suportul tehnic Google Cloud

Remedierea problemelor este atât știință, cât și artă. Totul începe cu formularea unei ipoteze despre cauza comportamentului anormal al sistemului, după care aceasta este testată. Cu toate acestea, înainte de a formula o ipoteză, trebuie să definim clar și să formulăm cu precizie problema. Dacă întrebarea este prea vagă, va trebui să analizezi totul în profunzime; acesta este „arta” remediării problemelor.

În contextul Google Cloud, aceste procese sunt mult mai complicate, deoarece Google Cloud se străduiește din răsputeri să garanteze confidențialitatea utilizatorilor săi. Din acest motiv, inginerii TSE nu au acces la editarea sistemelor tale, nici la o analiză atât de detaliată a configurațiilor, așa cum o fac utilizatorii. Prin urmare, pentru a verifica vreo ipoteză, noi (inginerii) nu putem modifica rapid sistemul.

Unii utilizatori cred că vom rezolva totul ca mecanicii într-un service auto și pur și simplu ne trimit ID-ul mașinii virtuale, în timp ce, în realitate, procesul decurge într-un format bazat pe discuție: colectarea informațiilor, formularea și confirmarea (sau infirmarea) ipotezelor, iar, în cele din urmă, soluționarea problemei se bazează pe comunicarea cu clientul.

Problema în discuție

Astăzi avem în față o poveste cu un final fericit. Unul dintre motivele pentru care cazul propus a fost soluționat cu succes se datorează descrierii foarte detaliate și precise a problemei. Mai jos, puteți vedea o copie a primului tichet (editat pentru a ascunde informațiile confidențiale):
Povestea despre pachetele DNS pierdute de la suportul tehnic Google Cloud
Acest mesaj conține foarte multe informații utile pentru noi:

  • Este specificată VM-ul concretă
  • Problema în sine este specificată — nu funcționează DNS-ul
  • Este indicat unde se manifestă problema — VM și container
  • Sunt enumerate pașii parcurși de utilizator pentru identificarea problemei

Solicitarea a fost înregistrată ca „P1: Impact critic — Serviciu inutilizabil în producție”, ceea ce înseamnă monitorizarea constantă a situației 24/7 conform schemei „Follow the Sun” (puteți citi mai multe despre prioritățile solicitărilor utilizatorilor), transferându-se de la o echipă de asistență tehnică la alta la fiecare schimbare a fusului orar. Practic, până la momentul în care problema a ajuns la echipa noastră din Zurich, a reușit să ocolească globul. Între timp, utilizatorul a luat măsuri pentru a diminua impactul, dar se temea de repetarea situației în producție, deoarece cauza principală nu fusese încă descoperită.

La momentul în care tichetul a ajuns la Zurich, aveam deja următoarele informații:

  • Conținut /etc/hosts
  • Conținut /etc/resolv.conf
  • Ieșire iptables-save
  • Fișierul ngrep pcap adunat de echipă

Cu aceste date, eram pregătiți să începeți faza de „investigație” și depanare.

Primii noștri pași

În primul rând, am verificat jurnalele și statusul serverului de metadate și ne-am asigurat că funcționează corect. Serverul de metadate răspunde la adresa IP 169.254.169.254 și, printre altele, se ocupă de gestionarea numelui de domeniu. Am verificat, de asemenea, că firewall-ul funcționează corect cu VM-ul și nu blochează pachetele.

A fost o problemă ciudată: verificarea nmap a infirmat principala noastră ipoteză despre pierderile de pachete UDP, așa că am conceput mental alte câteva opțiuni și metode de verificare:

  • Pachetele dispar selectiv? => Verificați regulile iptables
  • Nu e cumva prea mic MTU? => Проверить вывод ip a show
  • Problema afectează doar pachetele UDP sau și cele TCP? => Rulați dig +tcp
  • Se returnează pachetele generate de dig? => Rulați tcpdump
  • Funcționează corect libdns? => Rulați strace pentru verificarea transmiterii pachetelor în ambele direcții

Aici decidem să facem un apel cu utilizatorul pentru a depana live.

În timpul apelului, reușim să verificăm mai multe lucruri:

  • După câteva verificări, excludem regulile iptables din lista cauzelor
  • Verificăm interfețele rețelei și tabelele de rutare și re-verificăm corectitudinea MTU-ului
  • Descoperim că dig +tcp google.com (TCP) funcționează corect, dar dig google.com (UDP) nu funcționează
  • După ce rulăm tcpdump funcționează până acum dig, descoperim că pachetele UDP se întorc
  • Rulăm strace dig google.com și vedem cum dig apelează corect sendmsg() și recvms(), totuși a doua este întreruptă din cauza timeout-ului

Din păcate, se termină schimbul și suntem nevoiți să transferăm problema în zona orară următoare. Totuși, apelul a stârnit interes în echipa noastră și un coleg propune să creăm pachetul DNS de bază folosind modulul python scrapy.

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

Acest fragment creează un pachet DNS și trimite cererea serverului de metadata.

Utilizatorul rulează codul, răspunsul DNS se întoarce, și aplicația îl primește, confirmând absența problemei la nivelul rețelei.

După încă o „călătorie în jurul lumii”, apelul revine echipei noastre, iar eu îmi asum complet responsabilitatea, considerând că utilizatorului îi va fi mai convenabil dacă apelul nu mai rătăcește.

Între timp, utilizatorul este de acord să ne ofere un instantaneu al imaginii sistemului. Acestea sunt vești excelente: posibilitatea de a testa singur sistemul accelerează semnificativ procesul de depanare, deoarece nu mai trebuie să cer utilizatorului să ruleze comenzi, să-mi trimită rezultate și să le analizez, pot face totul eu!

Colegii mei încep să mă invidieze puțin. La prânz discutăm despre apel, dar nimeni nu are idei despre ce se întâmplă. Din fericire, utilizatorul a luat deja măsuri de atenuare a efectelor și nu se grăbește, așa că avem timp să disecăm problema. Și deoarece avem imaginea, putem efectua orice teste ne interesează. Excelent!

Revenind un pas în urmă

Una dintre cele mai populare întrebări la interviul pentru postul de inginer sistem este: „Ce se întâmplă când pingi www.google.com?» Вопрос шикарный, так как кандидату необходимо описать пусть от оболочки до пользовательского пространства, до ядра системы и далее к сети. Я улыбаюсь: иногда вопросы с интервью оказываются полезны и в реальной жизни…

Decid să aplic această întrebare de resurse umane la problema curentă. În esență, atunci când încerci să determinți un nume DNS, se întâmplă următoarele:

  1. Aplicația apelează o bibliotecă de sistem, cum ar fi libdns
  2. libdns verifică configurația sistemului pentru a determina la ce server DNS să se conecteze (în diagramă este 169.254.169.254, serverul de metadate)
  3. libdns utilizează apeluri de sistem pentru a crea un socket UDP (SOCKET_DGRAM) și pentru a trimite pachete UDP cu cererea DNS în ambele direcții
  4. Prin intermediul interfeței sysctl, se poate configura stiva UDP la nivel de nucleu
  5. Nucleul interacționează cu hardware-ul pentru a transmite pachete prin rețea prin intermediul interfeței de rețea
  6. Hypervisorul captează și transmite pachetul către serverul de metadate la contactul cu acesta
  7. Serverul de metadate cu magia sa determină numele DNS și returnează un răspuns prin același proces

Povestea despre pachetele DNS pierdute de la suportul tehnic Google Cloud
Îmi amintesc ce ipoteze am discutat până acum:

Ipoteza: Bibliotecile sunt defecte

  • Test 1: să rulăm strace în sistem, să verificăm dacă dig apelează apeluri de sistem corecte
  • Rezultatul: se apelează apeluri de sistem corecte
  • Test 2: prin srapy să verificăm dacă putem determina numele ocolind bibliotecile de sistem
  • Rezultatul: putem
  • Test 3: să rulăm rpm -V pe pachetul libdns și md5sum pe fișierele bibliotecii
  • Rezultatul: codul bibliotecii este identic cu codul din sistemul de operare funcțional
  • Test 4: să montăm imaginea sistemului de fișiere rădăcină a utilizatorului pe VM fără un comportament similar, să rulăm chroot, să vedem dacă DNS funcționează
  • Rezultatul: DNS funcționează corect

Concluzie pe baza testelor: problema nu este în biblioteci

Ipoteza: Există o eroare în configurațiile DNS

  • Test 1: să verificăm tcpdump și să observăm dacă pachetele DNS sunt trimise și primite corect după rularea dig
  • Rezultatul: pachetele sunt transmise corect
  • Test 2: să verificăm din nou pe server /etc/nsswitch.conf și /etc/resolv.conf
  • Rezultatul: totul este corect

Concluzie pe baza testelor: problema nu este în configurația DNS

Ipoteza: nucleul este deteriorat

  • Test: să instalăm un nou nucleu, să verificăm semnătura, să repornim
  • Rezultatul: comportament similar

Concluzie pe baza testelor: nucleul nu este deteriorat

Ipoteza: comportament incorect al rețelei utilizatorului (sau al interfeței de rețea a hypervisorului)

  • Test 1: să verificăm setările firewall-ului
  • Rezultatul: firewall-ul permite pachetele DNS atât pe gazdă, cât și pe GCP
  • Test 2: să interceptăm traficul și să urmărim corectitudinea transmiterii și întoarcerii cererilor DNS
  • Rezultatul: tcpdump confirmă primirea pachetelor reversibile de către gazdă

Concluzie pe baza testelor: problema nu este în rețea

Ipoteza: serverul de metadate nu funcționează

  • Test 1: să verificăm logurile serverului de metadate pentru anomalii
  • Rezultatul: nu există anomalii în loguri
  • Test 2: ocolind serverul de metadate prin dig @8.8.8.8
  • Rezultatul: rezolvarea este afectată chiar și fără utilizarea serverului de metadate

Concluzie pe baza testelor: problema nu este în serverul de metadate

Concluzie: am testat toate subsistemele cu excepția setărilor de mediu!

Adâncindu-mă în setările de mediu ale nucleului

Pentru configurarea mediului de execuție al nucleului, puteți utiliza opțiunile din linia de comandă (grub) sau interfața sysctl. Am verificat /etc/sysctl.conf și să ne gândim, am descoperit câteva setări personalizate. Simțind că m-am prins de ceva, am exclus toate setările ne-rețea sau non-tcp, rămânând cu un grup de setări net.core. Apoi m-am dus acolo unde VM-urile au permisiuni de gazdă și am început să aplic setările una câte una de la VM-ul defect, până am găsit autorul:

net.core.rmem_default = 2147483647

Iată-l, configurația DNS defectuoasă! Am găsit instrumentul crimei. Dar de ce se întâmplă asta? Aveam nevoie în continuare de un motiv.

Configurarea dimensiunii de bază a bufferului pachetelor DNS se face prin net.core.rmem_default. Valoarea tipică variază undeva între 200KiB, totuși, dacă serverul dumneavoastră primește multe pachete DNS, puteți mări dimensiunea bufferului. Dacă, în momentul în care sosește un nou pachet, bufferul este plin, de exemplu, deoarece aplicația nu-l procesează suficient de repede, veți începe să pierdeți pachete. Clientul nostru a crescut corect dimensiunea bufferului deoarece se temea de pierderea datelor, deoarece utiliza o aplicație pentru colectarea metricilor prin pachete DNS. Valoarea pe care a stabilit-o a fost maximul posibil: 231-1 (dacă setați 231, nucleul va returna „ARGUMENT INVALID”).

Deodată, am realizat de ce nmap și scapy funcționează corect: ele folosesc socketuri brute! Socketurile brute diferă de cele normale: ele ocolesc iptables și nu sunt tamponate!

Dar de ce „buffer prea mare” provoacă probleme? El evident nu funcționează așa cum ar trebui.

Până în acest moment, am reușit să reproduc problema pe mai multe nuclee și numeroase distribuții. Problema s-a manifestat deja pe nucleul 3.x și acum apare și pe nucleul 5.x.

Într-adevăr, când rulam

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

DNS-ul înceta să funcționeze.

Am început să caut valori funcționale printr-un algoritm simplu de căutare binară și am descoperit că sistemul funcționează cu 2147481343, însă acest număr era pentru mine o secvență de cifre fără sens. I-am propus clientului să încerce acest număr, iar el a răspuns că sistemul a funcționat cu google.com, dar continua să returneze o eroare cu alte domenii, așa că am continuat investigația.

Am instalat dropwatch, un instrument de care ar fi trebuit să mă folosesc mai devreme: arată exact unde ajunge un pachet în nucleu. Vinovată s-a dovedit a fi funcția udp_queue_rcv_skb. Am descărcat sursele nucleului și am adăugat câteva funcții printk pentru a urmări exact unde ajunge pachetul. Am descoperit rapid condiția necesară if, și o vreme pur și simplu m-am uitat la ea, pentru că atunci totul s-a prins în imaginea de ansamblu: 231-1, un număr absurd, un domeniu nefuncțional… A fost vorba de un fragment de cod în __udp_enqueue_schedule_skb:

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

Rețineți:

  • rmem are tipul int
  • dimensiune are tipul u16 (un int semn neînscris pe 16 biți) și păstrează dimensiunea pachetului
  • sk->sk_rcybuf are tipul int și păstrează dimensiunea buffers care este definită ca valoarea din net.core.rmem_default

Când sk_rcvbuf aproape de 231, iar suma dimensiunii pachetului poate duce la supraîncărcarea integer. Și fiindcă este int, valoarea sa devine negativă, astfel încât condiția devine adevărată atunci când ar trebui să fie falsă (mai multe despre asta puteți afla din linkul).

Eroarea se rezolvă într-un mod trivial: prin conversie la unsigned int. Am aplicat soluția și am repornit sistemul, după care DNS-ul a început din nou să funcționeze.

Gustul victoriei

Am trimis descoperirile mele clientului și am expediat LKML patch-ul nucleului. Sunt mulțumit: fiecare piesă a puzzle-ului a fost completă, pot explica exact de ce am observat ceea ce am observat și, cel mai important, am reușit să găsim soluția problemei datorită muncii în echipă!

Trebuie să recunoaștem că acest caz a fost rar, iar din fericire, utilizatorii noștri nu ne trimit adesea cereri atât de complexe.

Povestea despre pachetele DNS pierdute de la suportul tehnic Google Cloud


Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster