Una història sobre paquets DNS que falten del suport tècnic de Google Cloud

Des de Google Blog Editor: Alguna vegada us heu preguntat com els enginyers de Google Cloud Technical Solutions (TSE) gestionen les vostres sol·licituds d'assistència? Els enginyers de suport tècnic de TSE són responsables d'identificar i corregir les fonts de problemes informades pels usuaris. Alguns d'aquests problemes són bastant senzills, però de vegades et trobes amb un bitllet que requereix l'atenció de diversos enginyers alhora. En aquest article, un dels empleats de TSE ens explicarà un problema molt complicat de la seva pràctica recent: cas de faltar paquets DNS. En aquesta història, veurem com els enginyers van aconseguir resoldre la situació i quines coses noves van aprendre mentre arreglaven l'error. Esperem que aquesta història no només us eduqui sobre un error profundament arrelat, sinó que també us ofereixi informació sobre els processos que s'han de fer per presentar un bitllet d'assistència a Google Cloud.

Una història sobre paquets DNS que falten del suport tècnic de Google Cloud

La resolució de problemes és alhora una ciència i un art. Tot comença amb la construcció d'una hipòtesi sobre el motiu del comportament no estàndard del sistema, després de la qual es prova la força. Tanmateix, abans de formular una hipòtesi, hem de definir clarament i formular el problema amb precisió. Si la pregunta sona massa vaga, haureu d'analitzar-ho tot amb cura; Aquest és l'"art" de la resolució de problemes.

Amb Google Cloud, aquests processos es tornen exponencialment més complexos, ja que Google Cloud fa tot el possible per garantir la privadesa dels seus usuaris. A causa d'això, els enginyers de TSE no tenen accés per editar els vostres sistemes, ni la capacitat de veure les configuracions de manera tan àmplia com ho fan els usuaris. Per tant, per provar qualsevol de les nostres hipòtesis, nosaltres (els enginyers) no podem modificar ràpidament el sistema.

Alguns usuaris creuen que ho arreglarem tot com la mecànica en un servei d'automòbils, i simplement ens enviarem l'identificador d'una màquina virtual, mentre que en realitat el procés es fa en format conversacional: recopilant informació, formant i confirmant (o refutant) hipòtesis, i, al final, els problemes de decisió es basen en la comunicació amb el client.

Problema en qüestió

Avui tenim una història amb un bon final. Un dels motius de la resolució satisfactòria del cas proposat és una descripció molt detallada i precisa del problema. A continuació podeu veure una còpia del primer bitllet (editat per ocultar informació confidencial):
Una història sobre paquets DNS que falten del suport tècnic de Google Cloud
Aquest missatge conté molta informació útil per a nosaltres:

  • VM específica especificada
  • S'indica el problema en si: el DNS no funciona
  • S'indica on es manifesta el problema: VM i contenidor
  • S'indiquen els passos que va fer l'usuari per identificar el problema.

La sol·licitud es va registrar com a "P1: Impacte crític - Servei inutilitzable en producció", el que significa un seguiment constant de la situació les 24 hores del dia, els 7 dies de la setmana, segons l'esquema "Follow the Sun" (podeu llegir més sobre prioritats de les sol·licituds dels usuaris), amb el seu trasllat d'un equip de suport tècnic a un altre amb cada canvi de zona horària. De fet, quan el problema va arribar al nostre equip de Zuric, ja havia donat la volta al món. En aquest moment, l'usuari havia pres mesures de mitigació, però tenia por de repetir la situació en producció, ja que encara no s'havia descobert la causa principal.

Quan el bitllet va arribar a Zuric, ja teníem a mà la informació següent:

  • Contingut /etc/hosts
  • Contingut /etc/resolv.conf
  • Sortida iptables-save
  • Muntat per l'equip ngrep fitxer pcap

Amb aquestes dades, estàvem preparats per començar la fase d'"investigació" i resolució de problemes.

Els nostres primers passos

En primer lloc, vam comprovar els registres i l'estat del servidor de metadades i ens vam assegurar que funcionava correctament. El servidor de metadades respon a l'adreça IP 169.254.169.254 i, entre altres coses, s'encarrega de controlar els noms de domini. També vam comprovar que el tallafoc funciona correctament amb la màquina virtual i no bloqueja paquets.

Va ser una mena de problema estrany: la comprovació nmap va refutar la nostra hipòtesi principal sobre la pèrdua de paquets UDP, així que mentalment vam trobar diverses opcions i maneres de comprovar-los:

  • Es descarten els paquets selectivament? => Comproveu les regles d'iptables
  • No és massa petit? MTU? => Comproveu la sortida ip a show
  • El problema només afecta els paquets UDP o TCP també? => Allunya't dig +tcp
  • Es retornen els paquets generats per dig? => Allunya't tcpdump
  • El libdns funciona correctament? => Allunya't strace per comprovar la transmissió de paquets en ambdues direccions

Aquí decidim trucar a l'usuari per resoldre problemes en directe.

Durant la trucada podem comprovar diverses coses:

  • Després de diverses comprovacions, excloem les regles d'iptables de la llista de motius
  • Comprovem les interfícies de xarxa i les taules d'encaminament i comprovem que la MTU sigui correcta
  • Ho descobrim dig +tcp google.com (TCP) funciona com hauria de ser, però dig google.com (UDP) no funciona
  • Havent fugit tcpdump encara funciona dig, trobem que s'estan retornant paquets UDP
  • Conduïm strace dig google.com i veiem com dig truca correctament sendmsg() и recvms(), però el segon s'interromp per un temps mort

Malauradament, arriba el final del torn i ens veiem obligats a augmentar el problema a la següent zona horària. La sol·licitud, però, va despertar interès en el nostre equip i un company suggereix crear el paquet DNS inicial mitjançant el mòdul scrapy 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())

Aquest fragment crea un paquet DNS i envia la sol·licitud al servidor de metadades.

L'usuari executa el codi, es retorna la resposta DNS i l'aplicació la rep, confirmant que no hi ha cap problema a nivell de xarxa.

Després d'un altre "viatge al món", la sol·licitud torna al nostre equip i la transfereixo completament a mi mateix, pensant que serà més convenient per a l'usuari si la sol·licitud deixa de circular d'un lloc a un altre.

Mentrestant, l'usuari es compromet a proporcionar una instantània de la imatge del sistema. Aquesta és una molt bona notícia: la possibilitat de provar el sistema jo mateix fa que la resolució de problemes sigui molt més ràpida, perquè ja no he de demanar a l'usuari que executi ordres, que m'enviï els resultats i que els analitzi, ho puc fer tot jo mateix!

Els meus companys em comencen a envejar una mica. Durant el dinar parlem de la conversió, però ningú no té ni idea del que està passant. Afortunadament, el mateix usuari ja ha pres mesures per pal·liar les conseqüències i no té pressa, així que tenim temps per analitzar el problema. I com que tenim una imatge, podem fer les proves que ens interessin. Genial!

Fent un pas enrere

Una de les preguntes d'entrevista més populars per als llocs d'enginyer de sistemes és: "Què passa quan feu ping www.google.com? La pregunta és fantàstica, ja que el candidat ha de descriure tot, des de l'intèrpret d'ordres fins a l'espai d'usuari, passant pel nucli del sistema i després fins a la xarxa. Somric: de vegades les preguntes de l'entrevista resulten útils a la vida real...

Decideixo aplicar aquesta pregunta de recursos humans a un problema actual. A grans trets, quan intenteu determinar un nom DNS, passa el següent:

  1. L'aplicació crida a una biblioteca del sistema com ara libdns
  2. libdns comprova la configuració del sistema amb quin servidor DNS ha de contactar (al diagrama és 169.254.169.254, servidor de metadades)
  3. libdns utilitza trucades al sistema per crear un sòcol UDP (SOKET_DGRAM) i enviar paquets UDP amb una consulta DNS en ambdues direccions
  4. Mitjançant la interfície sysctl podeu configurar la pila UDP al nivell del nucli
  5. El nucli interactua amb el maquinari per transmetre paquets a través de la xarxa mitjançant la interfície de xarxa
  6. L'hipervisor captura i transmet el paquet al servidor de metadades en contactar-hi
  7. El servidor de metadades, per màgia, determina el nom DNS i retorna una resposta utilitzant el mateix mètode

Una història sobre paquets DNS que falten del suport tècnic de Google Cloud
Permeteu-me que us recordi quines hipòtesis ja hem considerat:

Hipòtesi: Biblioteques trencades

  • Prova 1: executeu Strace al sistema, comproveu que dig crida a les trucades correctes del sistema
  • Resultat: Es criden les trucades al sistema correctes
  • Prova 2: utilitzant srapy per comprovar si podem determinar noms sense passar per les biblioteques del sistema
  • Resultat: podem
  • Prova 3: executeu rpm –V al paquet libdns i als fitxers de la biblioteca md5sum
  • Resultat: el codi de la biblioteca és completament idèntic al codi del sistema operatiu de treball
  • Prova 4: munteu la imatge del sistema arrel de l'usuari en una màquina virtual sense aquest comportament, executeu chroot, mireu si DNS funciona
  • Resultat: DNS funciona correctament

Conclusió basada en proves: el problema no és a les biblioteques

Hipòtesi: hi ha un error a la configuració del DNS

  • Prova 1: comproveu tcpdump i comproveu si els paquets DNS s'envien i tornen correctament després d'executar dig
  • Resultat: els paquets es transmeten correctament
  • Prova 2: comproveu el servidor /etc/nsswitch.conf и /etc/resolv.conf
  • Resultat: tot és correcte

Conclusió basada en proves: el problema no és la configuració del DNS

Hipòtesi: nucli danyat

  • Prova: instal·leu el nou nucli, comproveu la signatura, reinicieu
  • Resultat: comportament semblant

Conclusió basada en proves: el nucli no està danyat

Hipòtesi: comportament incorrecte de la xarxa d'usuari (o interfície de xarxa de l'hipervisor)

  • Prova 1: comproveu la configuració del vostre tallafoc
  • Resultat: el tallafoc passa paquets DNS tant a l'amfitrió com a GCP
  • Prova 2: interceptar el trànsit i controlar la correcció de la transmissió i el retorn de les peticions DNS
  • Resultat: tcpdump confirma que l'amfitrió ha rebut paquets de retorn

Conclusió basada en proves: el problema no està a la xarxa

Hipòtesi: el servidor de metadades no funciona

  • Prova 1: comproveu els registres del servidor de metadades per detectar anomalies
  • Resultat: no hi ha anomalies en els registres
  • Prova 2: ometeu el servidor de metadades mitjançant dig @8.8.8.8
  • Resultat: la resolució es trenca fins i tot sense utilitzar un servidor de metadades

Conclusió basada en proves: el problema no és amb el servidor de metadades

El resultat final: vam provar tots els subsistemes excepte configuració de temps d'execució!

Submergir-se a la configuració del temps d'execució del nucli

Per configurar l'entorn d'execució del nucli, podeu utilitzar les opcions de línia d'ordres (grub) o la interfície sysctl. Vaig mirar /etc/sysctl.conf i pensa, he descobert diversos paràmetres personalitzats. Tenint la sensació d'haver agafat alguna cosa, vaig descartar tots els paràmetres que no pertanyen a la xarxa o que no són tcp, i em vaig quedar amb la configuració de muntanya. net.core. Llavors vaig anar on es trobaven els permisos de l'amfitrió a la màquina virtual i vaig començar a aplicar la configuració una per una, una darrere l'altra, amb la màquina virtual trencada, fins que vaig trobar el culpable:

net.core.rmem_default = 2147483647

Aquí està, una configuració que trenca el DNS! Vaig trobar l'arma homicida. Però per què passa això? Encara necessitava un motiu.

La mida bàsica de la memòria intermèdia de paquets DNS es configura mitjançant net.core.rmem_default. Un valor típic és d'uns 200 KiB, però si el vostre servidor rep molts paquets DNS, és possible que vulgueu augmentar la mida de la memòria intermèdia. Si la memòria intermèdia està plena quan arriba un paquet nou, per exemple perquè l'aplicació no l'està processant prou ràpid, començareu a perdre paquets. El nostre client va augmentar correctament la mida del buffer perquè tenia por de la pèrdua de dades, ja que utilitzava una aplicació per recollir mètriques mitjançant paquets DNS. El valor que va establir era el màxim possible: 231-1 (si s'estableix en 231, el nucli retornarà "ARGUMENT INVALID").

De sobte em vaig adonar per què nmap i scapy funcionaven correctament: feien servir sòcols en brut! Els endolls en brut són diferents dels endolls normals: obvien els iptables i no estan a la memòria intermèdia!

Però, per què el "buffer massa gran" causa problemes? És evident que no funciona com estava previst.

En aquest punt podria reproduir el problema en diversos nuclis i múltiples distribucions. El problema ja apareixia al nucli 3.x i ara també apareixia al nucli 5.x.

De fet, a l'inici

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

DNS ha deixat de funcionar.

Vaig començar a buscar valors de treball mitjançant un algorisme de cerca binari senzill i vaig trobar que el sistema funcionava amb 2147481343, però aquest nombre era un conjunt de números sense sentit per a mi. Vaig suggerir al client que proves aquest número i em va respondre que el sistema funcionava amb google.com, però que tot i així donava un error amb altres dominis, així que vaig continuar la meva investigació.

He instal·lat dropwatch, una eina que s'hauria d'haver fet servir abans: mostra exactament on va a parar un paquet del nucli. El culpable va ser la funció udp_queue_rcv_skb. Vaig baixar les fonts del nucli i n'he afegit uns quants funcions printk per fer un seguiment d'on acaba exactament el paquet. Vaig trobar ràpidament la condició adequada if, i simplement m'ho vaig mirar durant una estona, perquè va ser llavors quan finalment tot es va unir en una imatge sencera: 231-1, un nombre sense sentit, un domini que no funcionava... Era un tros de codi en __udp_enqueue_schedule_skb:

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

Recordeu:

  • rmem és del tipus int
  • size és de tipus u16 (int de setze bits sense signar) i emmagatzema la mida del paquet
  • sk->sk_rcybuf és de tipus int i emmagatzema la mida de la memòria intermèdia que, per definició, és igual al valor en net.core.rmem_default

Quan sk_rcvbuf s'acosta a 231, la suma de la mida del paquet pot resultar en desbordament de nombres enters. I com que és un int, el seu valor esdevé negatiu, de manera que la condició esdevé certa quan hauria de ser falsa (podeu llegir més sobre això a enllaç).

L'error es pot corregir d'una manera trivial: mitjançant el càsting unsigned int. Vaig aplicar la correcció i vaig reiniciar el sistema i el DNS va tornar a funcionar.

El gust de la victòria

Vaig enviar les meves troballes al client i vaig enviar LKML pegat del nucli. Estic content: cada peça del trencaclosques encaixa, puc explicar exactament per què vam observar allò que vam observar i, el més important, vam poder trobar una solució al problema gràcies al nostre treball en equip!

Val la pena reconèixer que el cas va resultar ser rar i, afortunadament, poques vegades rebem peticions tan complexes dels usuaris.

Una història sobre paquets DNS que falten del suport tècnic de Google Cloud


Font: www.habr.com

Afegeix comentari