Príbeh o chýbajúcich paketoch DNS od technickej podpory Google Cloud

Z editora blogu Google: Zamysleli ste sa niekedy nad tým, ako inžinieri Google Cloud Technical Solutions (TSE) riešia vaše žiadosti o podporu? Technici technickej podpory TSE sú zodpovední za identifikáciu a nápravu zdrojov problémov nahlásených používateľmi. Niektoré z týchto problémov sú celkom jednoduché, no niekedy narazíte na lístok, ktorý si vyžaduje pozornosť niekoľkých inžinierov naraz. V tomto článku nám jeden zo zamestnancov TSE povie o jednom veľmi ošemetnom probléme zo svojej nedávnej praxe - prípad chýbajúcich DNS paketov. V tomto príbehu uvidíme, ako sa inžinierom podarilo situáciu vyriešiť a čo nové sa naučili pri oprave chyby. Dúfame, že tento príbeh vás nielen poučí o hlboko zakorenenej chybe, ale tiež vám poskytne prehľad o procesoch, ktoré súvisia s podaním žiadosti o podporu v službe Google Cloud.

Príbeh o chýbajúcich paketoch DNS od technickej podpory Google Cloud

Riešenie problémov je veda aj umenie. Všetko to začína vytvorením hypotézy o dôvode neštandardného správania systému, po ktorom sa testuje pevnosť. Kým však sformulujeme hypotézu, musíme jasne definovať a presne sformulovať problém. Ak otázka znie príliš vágne, budete musieť všetko dôkladne analyzovať; Toto je „umenie“ odstraňovania problémov.

V rámci služby Google Cloud sa takéto procesy stávajú exponenciálne zložitejšími, pretože služba Google Cloud sa snaží zo všetkých síl zaručiť súkromie svojich používateľov. Z tohto dôvodu nemajú inžinieri TSE prístup k úprave vašich systémov, ani možnosť prezerať konfigurácie tak široko, ako to robia používatelia. Preto, aby sme otestovali niektorú z našich hypotéz, my (inžinieri) nemôžeme rýchlo upraviť systém.

Niektorí používatelia veria, že všetko opravíme ako mechanici v autoservise a jednoducho nám pošleme ID virtuálneho stroja, zatiaľ čo v skutočnosti tento proces prebieha v konverzačnom formáte: zbieranie informácií, vytváranie a potvrdzovanie (alebo vyvracanie) hypotéz, a v konečnom dôsledku sú rozhodovacie problémy založené na komunikácii s klientom.

Predmetný problém

Dnes tu máme príbeh s dobrým koncom. Jedným z dôvodov úspešného vyriešenia navrhovaného prípadu je veľmi podrobný a presný popis problému. Nižšie môžete vidieť kópiu prvého tiketu (upravený tak, aby skryl dôverné informácie):
Príbeh o chýbajúcich paketoch DNS od technickej podpory Google Cloud
Táto správa obsahuje pre nás množstvo užitočných informácií:

  • Zadaný konkrétny VM
  • Je indikovaný samotný problém - DNS nefunguje
  • Je uvedené, kde sa problém prejavuje - VM a kontajner
  • Sú uvedené kroky, ktoré používateľ vykonal na identifikáciu problému.

Požiadavka bola zaregistrovaná ako „P1: Critical Impact – Service Unusable in production“, čo znamená neustále monitorovanie situácie 24/7 podľa schémy „Follow the Sun“ (viac si môžete prečítať o priority požiadaviek používateľov), s jeho presunom z jedného tímu technickej podpory do druhého pri každom posune časového pásma. V skutočnosti, keď sa problém dostal k nášmu tímu v Zürichu, už obletel celý svet. Používateľ medzitým prijal opatrenia na zmiernenie, ale obával sa opakovania situácie vo výrobe, pretože hlavná príčina ešte nebola odhalená.

V čase, keď letenka dorazila do Zürichu, sme už mali po ruke nasledujúce informácie:

  • obsah /etc/hosts
  • obsah /etc/resolv.conf
  • Výkon iptables-save
  • Zostavené tímom ngrep súbor pcap

S týmito údajmi sme boli pripravení začať fázu „vyšetrovania“ a riešenia problémov.

Naše prvé kroky

Najprv sme skontrolovali denníky a stav servera metadát a ubezpečili sa, že funguje správne. Server metadát odpovedá na IP adresu 169.254.169.254 a okrem iného je zodpovedný za kontrolu doménových mien. Tiež sme dvakrát skontrolovali, či firewall funguje správne s VM a neblokuje pakety.

Bol to nejaký zvláštny problém: kontrola nmap vyvrátila našu hlavnú hypotézu o strate paketov UDP, takže sme v duchu prišli s niekoľkými ďalšími možnosťami a spôsobmi, ako ich skontrolovať:

  • Sú pakety zahadzované selektívne? => Skontrolujte pravidlá iptables
  • Nie je to príliš malé? MTU? => Skontrolujte výstup ip a show
  • Ovplyvňuje problém iba pakety UDP alebo TCP? => Odvezte sa dig +tcp
  • Vrátia sa pakety vygenerované digom? => Odvezte sa tcpdump
  • Funguje libdns správne? => Odvezte sa strace na kontrolu prenosu paketov v oboch smeroch

Tu sa rozhodneme zavolať používateľovi na riešenie problémov naživo.

Počas hovoru sme schopní skontrolovať niekoľko vecí:

  • Po niekoľkých kontrolách vylúčime pravidlá iptables zo zoznamu dôvodov
  • Skontrolujeme sieťové rozhrania a smerovacie tabuľky a dvakrát skontrolujeme, či je MTU správna
  • Zisťujeme to dig +tcp google.com (TCP) funguje ako má, ale dig google.com (UDP) nefunguje
  • Po odjazde tcpdump stále to funguje dig, zistíme, že sa vracajú UDP pakety
  • Odvezieme sa strace dig google.com a vidíme, ako správne volá sendmsg() и recvms(), druhý je však prerušený časovým limitom

Žiaľ, prichádza koniec smeny a my sme nútení problém eskalovať do ďalšieho časového pásma. Požiadavka však vzbudila záujem v našom tíme a kolega navrhuje vytvorenie počiatočného balíka DNS pomocou špinavého modulu 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())

Tento fragment vytvorí paket DNS a odošle požiadavku na server metadát.

Používateľ spustí kód, vráti sa odpoveď DNS a aplikácia ju prijme, čím potvrdí, že na úrovni siete nie je žiadny problém.

Po ďalšej „ceste okolo sveta“ sa požiadavka vráti nášmu tímu a ja ju úplne prenášam na seba, mysliac si, že pre používateľa bude pohodlnejšie, ak žiadosť prestane krúžiť z miesta na miesto.

Používateľ medzitým láskavo súhlasí s poskytnutím snímky obrazu systému. To je veľmi dobrá správa: možnosť otestovať si systém sám výrazne urýchľuje riešenie problémov, pretože už nemusím žiadať používateľa, aby spúšťal príkazy, posielal mi výsledky a analyzoval ich, všetko môžem urobiť sám!

Kolegovia mi začínajú trochu závidieť. Počas obeda diskutujeme o konverzii, ale nikto netuší, čo sa deje. Našťastie samotný používateľ už prijal opatrenia na zmiernenie následkov a nikam sa neponáhľa, takže máme čas problém rozobrať. A keďže máme obrázok, môžeme spustiť akékoľvek testy, ktoré nás zaujímajú. Skvelé!

Urobte krok späť

Jedna z najpopulárnejších otázok na pohovore pre pozície systémových inžinierov je: „Čo sa stane, keď pingnete www.google.com? Otázka je skvelá, pretože kandidát potrebuje opísať všetko od shellu cez užívateľský priestor, jadro systému a potom sieť. Usmejem sa: niekedy sa otázky na pohovore ukážu ako užitočné v reálnom živote...

Rozhodol som sa aplikovať túto HR otázku na aktuálny problém. Zhruba povedané, keď sa pokúsite určiť názov DNS, stane sa toto:

  1. Aplikácia volá systémovú knižnicu, napríklad libdns
  2. libdns skontroluje konfiguráciu systému, na ktorý server DNS sa má obrátiť (v diagrame je to 169.254.169.254, server metadát)
  3. libdns používa systémové volania na vytvorenie zásuvky UDP (SOKET_DGRAM) a odosielanie paketov UDP s dotazom DNS v oboch smeroch
  4. Prostredníctvom rozhrania sysctl môžete nakonfigurovať zásobník UDP na úrovni jadra
  5. Jadro interaguje s hardvérom na prenos paketov cez sieť cez sieťové rozhranie
  6. Hypervízor zachytí a odošle paket na server metadát pri kontakte s ním
  7. Server metadát prostredníctvom svojej mágie určí názov DNS a vráti odpoveď pomocou rovnakej metódy

Príbeh o chýbajúcich paketoch DNS od technickej podpory Google Cloud
Dovoľte mi pripomenúť, aké hypotézy sme už zvážili:

Hypotéza: Rozbité knižnice

  • Test 1: spustite sledovanie v systéme, skontrolujte, či dig volá správne systémové volania
  • Výsledok: Vyvolajú sa správne systémové volania
  • Test 2: pomocou srapy skontrolujte, či dokážeme určiť mená obchádzajúce systémové knižnice
  • Výsledok: môžeme
  • Test 3: spustite rpm –V na balíku libdns a súboroch knižnice md5sum
  • Výsledok: kód knižnice je úplne identický s kódom vo funkčnom operačnom systéme
  • Test 4: pripojte obraz koreňového systému používateľa na VM bez tohto správania, spustite chroot a skontrolujte, či DNS funguje
  • Výsledok: DNS funguje správne

Záver na základe testov: problém nie je v knižniciach

Hypotéza: V nastaveniach DNS je chyba

  • Test 1: skontrolujte tcpdump a skontrolujte, či sa DNS pakety odosielajú a vracajú správne po spustení dig
  • Výsledok: pakety sa prenášajú správne
  • Test 2: dvakrát skontrolujte server /etc/nsswitch.conf и /etc/resolv.conf
  • Výsledok: všetko je správne

Záver na základe testov: problém nie je v konfigurácii DNS

Hypotéza: jadro poškodené

  • Test: nainštalujte nové jadro, skontrolujte podpis, reštartujte
  • Výsledok: podobné správanie

Záver na základe testov: jadro nie je poškodené

Hypotéza: nesprávne správanie používateľskej siete (alebo sieťového rozhrania hypervízora)

  • Test 1: Skontrolujte nastavenia brány firewall
  • Výsledok: brána firewall odovzdáva pakety DNS na hostiteľovi aj GCP
  • Test 2: zachyťte prevádzku a sledujte správnosť prenosu a návratu DNS požiadaviek
  • Výsledok: tcpdump potvrdzuje, že hostiteľ prijal spätné pakety

Záver na základe testov: problém nie je v sieti

Hypotéza: server metadát nefunguje

  • Test 1: skontrolujte, či protokoly servera metadát neobsahujú anomálie
  • Výsledok: v protokoloch nie sú žiadne anomálie
  • Test 2: Obíďte server metadát cez dig @8.8.8.8
  • Výsledok: Rozlíšenie je narušené aj bez použitia servera metadát

Záver na základe testov: problém nie je so serverom metadát

Zrátané a podčiarknuté: testovali sme všetky podsystémy okrem nastavenia runtime!

Ponorenie sa do nastavení Kernel Runtime

Ak chcete nakonfigurovať prostredie vykonávania jadra, môžete použiť voľby príkazového riadka (grub) alebo rozhranie sysctl. Pozrel som sa do /etc/sysctl.conf a len si pomyslite, objavil som niekoľko vlastných nastavení. S pocitom, akoby som sa niečoho chytil, som zahodil všetky nastavenia, ktoré nie sú sieťou alebo protokolom TCP, a zostal som pri horských nastaveniach net.core. Potom som išiel na miesto, kde boli povolenia hostiteľa vo virtuálnom počítači, a začal som aplikovať nastavenia jedno po druhom, jedno po druhom, s poškodeným virtuálnym počítačom, kým som nenašiel vinníka:

net.core.rmem_default = 2147483647

Tu to je, konfigurácia prerušujúca DNS! Našiel som vražednú zbraň. Ale prečo sa to deje? Stále som potreboval motív.

Základná veľkosť vyrovnávacej pamäte DNS paketov sa konfiguruje cez net.core.rmem_default. Typická hodnota je niekde okolo 200 kB, ale ak váš server prijíma veľa paketov DNS, možno budete chcieť zväčšiť veľkosť vyrovnávacej pamäte. Ak je vyrovnávacia pamäť plná, keď príde nový paket, napríklad preto, že ho aplikácia nespracováva dostatočne rýchlo, potom začnete strácať pakety. Náš klient správne zväčšil veľkosť vyrovnávacej pamäte, pretože sa obával straty dát, keďže používal aplikáciu na zber metrík cez DNS pakety. Hodnota, ktorú nastavil, bola maximálna možná hodnota: 231-1 (ak je nastavená na 231, jadro vráti „NEPLATNÝ ARGUMENT“).

Zrazu som si uvedomil, prečo nmap a scapy fungovali správne: používali surové zásuvky! Raw sokety sa líšia od bežných soketov: obchádzajú iptables a nie sú ukladané do vyrovnávacej pamäte!

Prečo však „príliš veľký nárazník“ spôsobuje problémy? Očividne to nefunguje podľa predstáv.

V tomto bode by som mohol problém reprodukovať na viacerých jadrách a viacerých distribúciách. Problém sa objavil už na jadre 3.x a teraz sa objavil aj na jadre 5.x.

Pravdaže pri štarte

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

DNS prestal fungovať.

Začal som hľadať pracovné hodnoty pomocou jednoduchého binárneho vyhľadávacieho algoritmu a zistil som, že systém fungoval s 2147481343, ale toto číslo bolo pre mňa nezmyselnou množinou čísel. Navrhol som klientovi, aby vyskúšal toto číslo, a on odpovedal, že systém funguje s google.com, ale stále zobrazuje chybu s inými doménami, takže som pokračoval vo vyšetrovaní.

Nainštaloval som dropwatch, nástroj, ktorý sa mal použiť skôr: presne ukazuje, kde v jadre paket končí. Na vine bola funkcia udp_queue_rcv_skb. Stiahol som si zdrojové kódy jadra a pridal som ich funkcie printk sledovať, kde presne paket končí. Rýchlo som našiel správny stav if, a chvíľu sa na to len díval, pretože vtedy sa všetko konečne spojilo do jedného celku: 231-1, nezmyselné číslo, nefunkčná doména... Bol to kúsok kódu v __udp_enqueue_schedule_skb:

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

Upozornenie:

  • rmem je typu int
  • size je typu u16 (unsigned sixteen-bit int) a ukladá veľkosť paketu
  • sk->sk_rcybuf je typu int a ukladá veľkosť vyrovnávacej pamäte, ktorá sa podľa definície rovná hodnote in net.core.rmem_default

Kedy sk_rcvbuf blíži 231, výsledkom môže byť súčet veľkosti paketu pretečenie celého čísla. A keďže ide o int, jeho hodnota sa stane zápornou, takže podmienka sa stane pravdivou, keď by mala byť nepravdivá (viac si o tom môžete prečítať na odkaz).

Chybu je možné opraviť triviálnym spôsobom: odliatím unsigned int. Použil som opravu a reštartoval systém a DNS znova fungoval.

Chuť víťazstva

Svoje zistenia som postúpil klientovi a odoslal LKML kernel patch. Teší ma: každý kúsok skladačky do seba zapadá, viem presne vysvetliť, prečo sme pozorovali to, čo sme pozorovali, a čo je najdôležitejšie, vďaka tímovej práci sme dokázali nájsť riešenie problému!

Stojí za to uznať, že prípad sa ukázal ako zriedkavý a našťastie len zriedka dostávame takéto zložité požiadavky od používateľov.

Príbeh o chýbajúcich paketoch DNS od technickej podpory Google Cloud


Zdroj: hab.com

Pridať komentár