Dall'editor del blog Google: Ti sei mai chiesto come gli ingegneri Google Cloud Technical Solutions (TSE) gestiscono le tue richieste di supporto tecnico? Gli ingegneri del supporto tecnico TSE si occupano di identificare e risolvere i problemi segnalati dagli utenti. Alcuni di questi problemi sono piuttosto semplici, ma a volte ci sono richieste che richiedono l'attenzione di più ingegneri. In questo articolo, uno dei membri del team TSE ci parlerà di un problema bastante complicato che ha affrontato recentemente — . Durante questo racconto vedremo come gli ingegneri sono riusciti a risolvere la situazione e cosa hanno imparato nella risoluzione dell'errore. Speriamo che questa storia non solo ti racconti di un bug profondamente radicato, ma ti dia anche una comprensione dei processi coinvolti nella presentazione di una richiesta di supporto a Google Cloud.

La risoluzione dei problemi è sia una scienza che un'arte. Tutto inizia con l'ipotesi sulla causa del comportamento anomalo del sistema, che poi viene testata. Tuttavia, prima di formulare un'ipotesi, dobbiamo chiaramente definire e articolare il problema. Se la domanda è troppo vaga, dovrai analizzare attentamente la situazione; è qui che entra in gioco l'«arte» della risoluzione dei problemi.
Nel contesto di Google Cloud, tali processi sono ulteriormente complicati, poiché Google Cloud si impegna a garantire la privacy dei propri utenti. Pertanto, gli ingegneri TSE non hanno accesso per modificare i tuoi sistemi né possono esaminare le configurazioni nello stesso modo in cui lo fanno gli utenti. Di conseguenza, per verificare una delle nostre ipotesi, noi (ingegneri) non possiamo modificare rapidamente il sistema.
Alcuni utenti pensano che noi correggeremo tutto come tecnici in un'officina, e ci inviano semplicemente l'id della macchina virtuale, mentre in realtà il processo avviene in forma di dialogo: raccolta di informazioni, formulazione e conferma (o smentita) di ipotesi, e alla fine la risoluzione del problema si basa sulla comunicazione con il cliente.
Il problema in questione
Oggi abbiamo davanti a noi una storia con un lieto fine. Una delle ragioni per il successo della risoluzione del caso proposto risiede nella descrizione molto dettagliata e precisa del problema. Qui sotto puoi vedere una copia del primo ticket (modificato, per nascondere informazioni riservate):

In questo messaggio ci sono molte informazioni utili per noi:
- È specificata una VM concreta
- È specificato il problema stesso — il DNS non funziona
- È indicato dove si manifesta il problema — VM e contenitore
- Sono indicati i passi compiuti dall'utente per identificare il problema
La richiesta è stata registrata come «P1: Impatto Critico — Servizio non utilizzabile in produzione», il che significa monitoraggio costante della situazione 24/7 secondo lo schema «Follow the Sun» (puoi leggere di più su ), con un passaggio da un team di supporto all'altro ad ogni cambio di fuso orario. In sostanza, nel momento in cui il problema è arrivato al nostro team a Zurigo, aveva già fatto il giro del mondo. A quel punto l'utente aveva intrapreso misure per ridurre le conseguenze, ma temeva una ripetizione della situazione in produzione, poiché la causa principale non era ancora stata scoperta.
Al momento in cui il ticket è arrivato a Zurigo, avevamo già le seguenti informazioni:
- Contenuto
/etc/hosts - Contenuto
/etc/resolv.conf - Conclusione
iptables-save - Raccolto dal team
ngrepfile pcap
Con questi dati eravamo pronti a iniziare la fase di «indagine» e risoluzione dei problemi.
I nostri primi passi
Per prima cosa abbiamo controllato i log e lo stato del server dei metadati e ci siamo assicurati che funzionasse correttamente. Il server dei metadati risponde all'indirizzo IP 169.254.169.254 e, tra l'altro, si occupa del controllo dei nomi di dominio. Abbiamo anche ricontrollato che il firewall funzionasse correttamente con la VM e non bloccasse i pacchetti.
Era un problema piuttosto strano: il controllo di nmap ha smentito la nostra ipotesi principale sulla perdita dei pacchetti UDP, quindi abbiamo pensato ad altre opzioni e modi per verificarle:
- I pacchetti scompaiono in modo selettivo? => Controllare le regole di iptables
- Non è troppo piccolo ? => Проверить вывод
ip a show - Il problema riguarda solo i pacchetti UDP o anche i TCP? => Eseguire
dig +tcp - I pacchetti generati da dig ritornano? => Eseguire
tcpdump - Funziona correttamente libdns? => Eseguire
straceper verificare il passaggio dei pacchetti in entrambe le direzioni
Qui decidiamo di contattare l'utente per risolvere i problemi di persona.
Durante la chiamata riusciamo a controllare diverse cose:
- Dopo alcune verifiche escludiamo le regole iptables dall'elenco delle cause.
- Controlliamo le interfacce di rete e le tabelle di instradamento, e verifichiamo la correttezza dell'MTU.
- Scopriamo che
dig +tcp google.com(TCP) funziona come dovrebbe, mentredig google.com(UDP) non funziona. - Eseguendo
tcpdumpfinora funzionadig, scopriamo che i pacchetti UDP vengono restituiti. - Eseguiamo
strace dig google.come vediamo come dig chiama correttamentesendmsg()erecvms(),tuttavia il secondo viene interrotto per timeout.
Sfortunatamente, la fine del turno arriva e siamo costretti a trasferire il problema al prossimo fuso orario. Tuttavia, questa richiesta ha suscitato interesse nel nostro team, e un collega propone di creare un pacchetto DNS sorgente utilizzando il modulo 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())Questo frammento crea un pacchetto DNS e invia la richiesta al server di metadati.
L'utente esegue il codice, la risposta DNS viene restituita e l'applicazione la riceve, confermando l'assenza di problemi a livello di rete.
Dopo un'altra 'discesa intorno al mondo', la richiesta torna al nostro team, e mi prendo completamente in carico la situazione, ritenendo che sia più comodo per l'utente se la richiesta smettesse di vagare.
Nel frattempo, l'utente gentilmente accetta di fornire un'immagine del sistema. Queste sono ottime notizie: avere la possibilità di testare il sistema da solo accelera significativamente la risoluzione dei problemi, poiché non devo più chiedere all'utente di eseguire comandi, inviarmi i risultati e analizzarli, posso fare tutto io!
I colleghi cominciano a provare un po' di invidia. Durante il pranzo discutiamo della richiesta, ma nessuno ha idee su cosa stia succedendo. Fortunatamente, lo stesso utente ha già preso misure per mitigare le conseguenze e non ha fretta, quindi abbiamo tempo per esaminare il problema. E poiché abbiamo l'immagine, possiamo eseguire tutti i test che ci interessano. Ottimo!
Tornando indietro di un passo,
una delle domande più comuni nei colloqui per la posizione di ingegnere di sistema è: 'Cosa succede quando pinghi ?» Вопрос шикарный, так как кандидату необходимо описать пусть от оболочки до пользовательского пространства, до ядра системы и далее к сети. Я улыбаюсь: иногда вопросы с интервью оказываются полезны и в реальной жизни…
Decido di applicare questa domanda tipica agli attuali problemi. In sintesi, quando cerchi di determinare un nome DNS, accade quanto segue:
- L'applicazione chiama la libreria di sistema, ad esempio libdns
- libdns controlla la configurazione del sistema riguardo a quale server DNS deve contattare (nella diagramma è 169.254.169.254, il server dei metadati)
- libdns utilizza chiamate di sistema per creare un socket UDP (SOCK_DGRAM) e inviare pacchetti UDP con la richiesta DNS in entrambe le direzioni
- Attraverso l'interfaccia sysctl è possibile configurare lo stack UDP a livello di kernel
- Il kernel interagisce con l'hardware per trasmettere pacchetti in rete attraverso l'interfaccia di rete
- L'ipervisor intercetta e invia il pacchetto al server dei metadati quando interagisce con esso
- Il server dei metadati, con la sua magia, determina il nome DNS e restituisce la risposta nello stesso modo

Ricordo quali ipotesi abbiamo già considerato:
Ipotesi: le librerie sono danneggiate
- Test 1: eseguire strace nel sistema, verificare che dig chiami le corrette chiamate di sistema
- Risultato: vengono chiamate le corrette chiamate di sistema
- Test 2: tramite srapy verificare se possiamo determinare i nomi evitando le librerie di sistema
- Risultato: possiamo farlo
- Test 3: eseguire rpm -V sul pacchetto libdns e md5sum sui file della libreria
- Risultato: il codice della libreria è identico al codice nel sistema operativo funzionante
- Test 4: montare un'immagine del sistema radice dell'utente su una VM senza questo comportamento, eseguire chroot, vedere se DNS funziona
- Risultato: DNS funziona correttamente
Conclusione basata sui test: il problema non riguarda le librerie
Ipotesi: c'è un errore nelle impostazioni DNS
- Test 1: controllare tcpdump e osservare se i pacchetti DNS vengono inviati e restituiti correttamente dopo aver eseguito dig
- Risultato: i pacchetti vengono trasmessi correttamente
- Test 2: ricontrollare sul server
/etc/nsswitch.confe/etc/resolv.conf - Risultato: tutto è corretto
Conclusione basata sui test: il problema non è nella configurazione DNS
Ipotesi: il kernel è danneggiato
- Test: installare un nuovo kernel, verificare la firma, riavviare
- Risultato: comportamento simile
Conclusione basata sui test: il kernel non è danneggiato
Ipotesi: comportamento errato della rete utente (o dell'interfaccia di rete dell'ipervisor)
- Test 1: controllare le impostazioni del firewall
- Risultato: il firewall consente il passaggio dei pacchetti DNS sia sull'host che su GCP
- Test 2: intercettare il traffico e monitorare la correttezza della trasmissione e del rimando delle richieste DNS
- Risultato: tcpdump conferma che l'host riceve i pacchetti di risposta
Conclusione basata sui test: il problema non è nella rete
Ipotesi: il server dei metadati non funziona
- Test 1: controllare i log del server dei metadati per anomalie
- Risultato: non ci sono anomalie nei log
- Test 2: aggirare il server dei metadati tramite
dig @8.8.8.8 - Risultato: la risoluzione viene violata anche senza l'uso del server dei metadati
Conclusione basata sui test: il problema non è nel server dei metadati
Risultato: abbiamo testato tutti i sottosistemi tranne le impostazioni dell'ambiente di esecuzione!
Addentrandosi nelle impostazioni dell'ambiente di esecuzione del kernel
Per configurare l'ambiente di esecuzione del kernel, puoi utilizzare le opzioni della linea di comando (grub) oppure l'interfaccia sysctl. Ho dato un'occhiata a /etc/sysctl.conf e pensa un po', ho scoperto alcune impostazioni personalizzate. Sentendomi come se avessi afferrato qualcosa, ho scartato tutte le impostazioni non di rete o non tcp, rimanendo con un pugno di impostazioni net.core. Poi mi sono rivolto a dove sono le autorizzazioni host della VM e ho iniziato ad applicare una dopo l'altra le impostazioni dalla VM danneggiata, fino a trovare il colpevole:
net.core.rmem_default = 2147483647Eccola, la configurazione DNS che rompe tutto! Ho trovato l'arma del delitto. Ma perché sta succedendo? Avevo ancora bisogno di un movente.
La configurazione della dimensione del buffer dei pacchetti DNS avviene tramite net.core.rmem_default. Un valore tipico varia attorno ai 200KiB, ma se il tuo server riceve molti pacchetti DNS, puoi aumentare la dimensione del buffer. Se al momento dell'arrivo di un nuovo pacchetto il buffer è pieno, ad esempio perché l'applicazione non lo elabora abbastanza rapidamente, inizierai a perdere pacchetti. Il nostro cliente ha correttamente aumentato la dimensione del buffer poiché temeva perdite di dati, dato che utilizzava un'applicazione di raccolta metriche attraverso pacchetti DNS. Il valore che ha impostato era il massimo possibile: 231-1 (impostare 231 restituisce
All'improvviso ho realizzato perché nmap e scapy funzionavano correttamente: utilizzavano socket raw! Gli socket raw sono diversi da quelli normali: lavorano aggirando iptables e non vengono bufferizzati!
Ma perché un 'buffer troppo grande' causa problemi? Chiaramente non funziona come dovrebbe.
A questo punto riuscivo a riprodurre il problema su diversi kernel e molte distribuzioni. Il problema già si manifestava sul kernel 3.x e ora si manifestava anche sul kernel 5.x.
Infatti, eseguendo
sysctl -w net.core.rmem_default=$((2**31-1))DNS smetteva di funzionare.
Ho iniziato a cercare valori funzionanti attraverso un semplice algoritmo di ricerca binaria e ho scoperto che funzionava con 2147481343, ma per me quel numero era solo una serie insignificante di cifre. Ho suggerito al cliente di provare quel numero e mi ha risposto che il sistema ha funzionato con google.com, ma continuava a generare errori con altri domini, quindi ho proseguito le mie indagini.
Ho installato , uno strumento che avrei dovuto usare molto tempo fa: mostra esattamente dove nel kernel arriva il pacchetto. La colpevole era la funzione udp_queue_rcv_skb. Ho scaricato i sorgenti del kernel e ho aggiunto alcune per tracciare esattamente dove finisce il pacchetto. Ho rapidamente scoperto la condizione necessaria if, e per un po' ho semplicemente fissato quella condizione, perché è proprio allora che tutto finalmente si è unito in un quadro coerente: 231-1, un numero insignificante, un dominio non funzionante… Il problema era in un pezzo di codice in __udp_enqueue_schedule_skb:
if (rmem > (size + sk->sk_rcvbuf))
goto uncharge_drop;Attenzione:
rmemè di tipo intsizeè di tipo u16 (int non firmato a sedici bit) e memorizza la dimensione del pacchettosk->sk_rcybufè di tipo int e memorizza la dimensione del buffer che per definizione è uguale al valore innet.core.rmem_default
Quando sk_rcvbuf si avvicina a 231, la somma della dimensione del pacchetto può portare a . E dato che è un int, il suo valore diventa negativo, quindi la condizione diventa vera quando dovrebbe essere falsa (puoi trovare maggiori informazioni su ).
L'errore viene corretto in modo triviale: effettuando un casting a unsigned int. Ho applicato la correzione e riavviato il sistema, dopo di che il DNS ha ripreso a funzionare.
Il gusto della vittoria
Ho inoltrato le mie scoperte al cliente e inviato la patch del kernel. Sono soddisfatto: ogni pezzo del puzzle si è unito in un tutto unico, posso spiegare esattamente perché abbiamo osservato ciò che abbiamo osservato e, cosa più importante, abbiamo potuto trovare una soluzione al problema grazie al lavoro di squadra!
Va detto che il caso si è rivelato raro e, fortunatamente, riceviamo di rado richieste così complesse dagli utenti.
Fonte: habr.com
