Dall'editor del blog Google: Vi siete mai chiesti come gli ingegneri di Google Cloud Technical Solutions (TSE) gestiscono le vostre richieste di supporto tecnico? La responsabilità degli ingegneri TSE è quella di identificare e risolvere le fonti di problemi segnalate dagli utenti. Alcuni di questi problemi sono abbastanza semplici, ma a volte ci sono richieste che richiedono l'attenzione di più ingegneri. In questo articolo, uno dei membri del TSE ci parlerà di un problema molto complicato che ha incontrato di recente — . Durante questo racconto, scopriremo come gli ingegneri sono riusciti a risolvere la situazione e cosa hanno appreso durante la risoluzione del problema. Speriamo che questa storia non solo vi racconti di un bug radicato, ma offra anche una comprensione dei processi coinvolti nella presentazione di richieste di supporto a Google Cloud.

La risoluzione dei problemi è sia una scienza che un'arte. Tutto inizia con la formulazione di un'ipotesi sulla causa del comportamento anomalo del sistema, che poi viene testata rigorosamente. Tuttavia, prima di formulare un'ipotesi, è fondamentale definire chiaramente e descrivere con precisione il problema. Se la domanda è troppo vaga, sarà necessario analizzare tutto in dettaglio; è qui che si manifesta l'"arte" della risoluzione dei problemi.
Nel contesto di Google Cloud, tali processi diventano notevolmente più complessi, poiché Google Cloud si impegna a garantire la privacy dei propri utenti. Di conseguenza, gli ingegneri TSE non hanno accesso alla modifica dei vostri sistemi, né la possibilità di esaminare le configurazioni con la stessa ampiezza degli utenti. Pertanto, per verificare una qualsiasi delle nostre ipotesi, noi (ingegneri) non possiamo modificare rapidamente il sistema.
Alcuni utenti pensano che possiamo risolvere tutto come meccanici in un'officina, inviandoci semplicemente l'ID della macchina virtuale. In realtà, il processo avviene attraverso un dialogo: raccolta di informazioni, formulazione e conferma (o smentita) di ipotesi, e, infine, la risoluzione del problema si basa sulla comunicazione con il cliente.
Problema in esame
Oggi ci troviamo di fronte a una storia con un lieto fine. Una delle ragioni del successo nella risoluzione del caso presentato è stata la descrizione molto dettagliata e precisa del problema. Di seguito è riportata una copia del primo ticket (modificata per nascondere informazioni riservate):

In questo messaggio ci sono molte informazioni utili per noi:
- È stata specificata la VM concreta
- È stata indicata la problematica stessa — DNS non funzionante
- È stato indicato dove si manifesta il problema — VM e contenitore
- Sono stati specificati i passaggi che l'utente ha compiuto per identificare il problema
La richiesta è stata registrata come «P1: Impatto Critico — Servizio Inutilizzabile in produzione», il che significa monitoraggio continuo della situazione 24/7 secondo lo schema «Follow the Sun» (puoi leggere di più sui ), con il passaggio da un team di supporto all'altro ogni volta che si spostavano i fusi orari. Praticamente, al momento in cui il problema è arrivato al nostro team a Zurigo, aveva già fatto il giro del mondo. A quel punto, l'utente aveva adottato misure per ridurre le conseguenze, ma temeva una ripetizione della situazione in produzione, poiché la causa principale non era ancora stata individuata.
Quando il ticket è arrivato a Zurigo, avevamo già le seguenti informazioni:
- Contenuto
/etc/hosts - Contenuto
/etc/resolv.conf - Risultato
iptables-save - File raccolto dal team
ngrepfile pcap
Con questi dati eravamo pronti a passare alla fase di 'indagine' e risoluzione dei problemi.
I nostri primi passi
Per prima cosa, abbiamo controllato i registri 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 le altre cose, è responsabile del controllo dei nomi di dominio. Abbiamo anche verificato che il firewall funzionasse correttamente con la VM e non bloccasse i pacchetti.
Era un problema piuttosto strano: il controllo con nmap ha confutato la nostra ipotesi principale sulla perdita di pacchetti UDP, quindi abbiamo elaborato mentalmente altre opzioni e modalità per verificarle:
- I pacchetti scompaiono in modo selettivo? => Controlla le regole iptables
- Non è troppo piccolo? ? => Проверить вывод
ip a show - Il problema riguarda solo i pacchetti UDP o anche i TCP? => Esegui un test
dig +tcp - I pacchetti generati da dig vengono restituiti? => Esegui un test
tcpdump - Funziona correttamente libdns? => Esegui un test
straceper controllare il passaggio dei pacchetti in entrambe le direzioni
Qui decidiamo di contattare l'utente per risolvere il problema in tempo reale.
Durante la chiamata riusciamo a controllare alcune cose:
- Dopo vari controlli escludiamo le regole iptables dalla lista delle cause
- Verifichiamo le interfacce di rete e le tabelle di routing, controllando nuovamente la correttezza dell'MTU.
- Scopriamo che
dig +tcp google.com(TCP) funziona come previsto, madig google.com(UDP) non funziona - Eseguendo un test
tcpdumpfinora funzionadig, scopriamo che i pacchetti UDP vengono restituiti - Eseguiamo
strace dig google.come vediamo come dig chiama correttamentesendmsg()erecvmsg(), tuttavia il secondo si interrompe a causa di un timeout
Sfortunatamente, finisce il turno e dobbiamo trasferire il problema al prossimo fuso orario. Tuttavia, la questione ha suscitato interesse nel nostro team, e un collega suggerisce di creare il pacchetto DNS originale 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 dei metadati.
L'utente esegue il codice, riceve la risposta DNS e l'applicazione la riceve, confermando l'assenza di problemi a livello di rete.
Dopo un ulteriore 'giro intorno al mondo', la richiesta torna al nostro team, e io la gestisco completamente, pensando che per l'utente sia più comodo se la richiesta smetta di vagare senza meta.
Nel frattempo, l'utente gentilmente accetta di fornire uno screenshot dell'immagine di sistema. Questa è un'ottima notizia: avere la possibilità di testare il sistema da solo accelera notevolmente la risoluzione dei problemi, poiché non devo più chiedere all'utente di eseguire comandi, inviarmi i risultati e analizzarli; posso fare tutto da solo!
I colleghi cominciano a invidiarmi un po'. Durante il pranzo discutiamo della segnalazione, ma nessuno ha idee su cosa stia succedendo. Fortunatamente, l'utente stesso ha già preso provvedimenti per mitigare le conseguenze e non ha fretta, quindi abbiamo tempo per analizzare il problema. E dato che abbiamo un'immagine, possiamo eseguire qualsiasi test ci interessi. Ottimo!
Tornando indietro di un passo
Una delle domande più comuni durante i colloqui per la posizione di ingegnere di sistema è: «Cosa succede quando pingi ?» Вопрос шикарный, так как кандидату необходимо описать пусть от оболочки до пользовательского пространства, до ядра системы и далее к сети. Я улыбаюсь: иногда вопросы с интервью оказываются полезны и в реальной жизни…
Decido di applicare questa domanda HR al problema attuale. In poche parole, quando si tenta di risolvere un nome DNS, succede quanto segue:
- L'applicazione chiama una libreria di sistema, ad esempio libdns
- libdns controlla la configurazione di sistema per determinare a quale server DNS rivolgersi (nello schema è 169.254.169.254, il server dei metadati)
- libdns utilizza chiamate di sistema per creare un socket UDP (SOCK_DGRAM) e trasmettere 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 i pacchetti in rete attraverso l'interfaccia di rete
- L'iper-vizor cattura e trasmette il pacchetto al server dei metadati al momento del contatto.
- Il server dei metadati, con la sua magia, determina il nome DNS e restituisce la risposta con lo stesso metodo.

Ricordo quali ipotesi abbiamo già considerato:
Ipotesi: Le librerie sono corrotte.
- Test 1: eseguire strace nel sistema per verificare se dig invoca chiamate di sistema corrette.
- Risultato: vengono invocate chiamate di sistema corrette.
- Test 2: tramite srapy controllare 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 è completamente identico a quello del sistema operativo funzionante.
- Test 4: montare l'immagine del sistema root dell'utente su VM senza tale comportamento, eseguire chroot e verificare se DNS funziona.
- Risultato: DNS funziona correttamente.
Conclusione basata sui test: il problema non è nelle 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 avviato dig.
- Risultato: i pacchetti vengono trasferiti 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, controllare la firma, riavviare
- Risultato: comportamento analogo
Conclusione basata sui test: il kernel non è danneggiato
Ipotesi: comportamento errato della rete utente (o dell'interfaccia di rete del hypervisor)
- Test 1: controllare le impostazioni del firewall
- Risultato: il firewall consente pacchetti DNS sia sull'host che su GCP
- Test 2: intercettare il traffico e monitorare la correttezza della trasmissione e della ricezione delle richieste DNS
- Risultato: tcpdump conferma la ricezione dei pacchetti di ritorno dall'host
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: eludere il server dei metadati tramite
dig @8.8.8.8 - Risultato: la risoluzione fallisce anche senza utilizzare il 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!
Esplorando le impostazioni dell'ambiente di esecuzione del kernel
Per configurare l'ambiente di esecuzione del kernel puoi utilizzare le opzioni della riga di comando (grub) o l'interfaccia sysctl. Ho dato un'occhiata a /etc/sysctl.conf E pensare che 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 gruppo di impostazioni. net.core. Poi sono andato dove si trovano le autorizzazioni dell'host per la VM e ho iniziato ad applicare a uno a uno, in sequenza, le impostazioni con la VM rotta, finché non sono arrivato al colpevole:
net.core.rmem_default = 2147483647Eccola, la configurazione DNS che rompe tutto! Ho trovato l'arma del delitto. Ma perché succede? Avevo ancora bisogno di un movente.
La configurazione della dimensione di base del buffer dei pacchetti DNS avviene tramite net.core.rmem_default. Il valore tipico varia intorno ai 200 KiB, tuttavia, 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 velocemente, inizierai a perdere pacchetti. Il nostro cliente ha correttamente aumentato la dimensione del buffer poiché temeva perdite di dati, essendo utilizzatore di un'applicazione per la raccolta di metriche tramite pacchetti DNS. Il valore che ha impostato era il massimo possibile: 231-1 (se impostato a 231, il kernel restituisce «ARGOMENTO NON VALIDO»).
All'improvviso ho realizzato perché nmap e scapy funzionavano correttamente: utilizzavano socket raw! I socket raw si distinguono da quelli normali: operano al di fuori di iptables e non sono 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 si manifestava già sul kernel 3.x e ora si manifestava allo stesso modo sul kernel 5.x.
Infatti, eseguendo
sysctl -w net.core.rmem_default=$((2**31-1))il DNS smetteva di funzionare.
Ho iniziato a cercare valori funzionanti attraverso un semplice algoritmo di ricerca binaria e ho scoperto che a 2147481343 il sistema funzionava, ma quel numero era per me una sequenza di cifre priva di significato. Ho suggerito al cliente di provare quel numero e lui ha risposto che il sistema funzionava con google.com, ma continuava a mostrare errori con altri domini, quindi ho continuato la mia indagine.
Ho installato , uno strumento che avrei dovuto utilizzare prima: mostra esattamente dove nel kernel arriva il pacchetto. Il colpevole si è rivelata la funzione udp_queue_rcv_skb. Ho scaricato i sorgenti del kernel e aggiunto alcune per monitorare dove va esattamente il pacchetto. Ho rapidamente individuato la condizione necessaria in etcdhelper, che non modificherà il servizio kube-dns., e per un po' lo fissa semplicemente, perché proprio allora tutto finalmente si incastrava in un quadro coerente: 231-1, un numero privo di senso, 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;Nota bene:
rmemè di tipo intdimensioneè di tipo u16 (intero unsigned a sedici bit) e contiene la dimensione del pacchettosk->sk_rcybufè di tipo int e contiene 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 poiché è un int, il suo valore diventa negativo, quindi la condizione diventa vera quando dovrebbe essere falsa (puoi saperne di più su ).
L'errore può essere corretto in modo semplice: tramite un cast a unsigned int. Ho applicato la correzione e riavviato il sistema, dopo di che il DNS ha di nuovo funzionato.
Il gusto della vittoria
Ho inoltrato le mie scoperte al cliente e ho inviato patch del kernel. Sono soddisfatto: ogni pezzo del puzzle si è incastrato, posso spiegare esattamente perché abbiamo osservato ciò che abbiamo osservato e, cosa più importante, siamo riusciti a trovare una soluzione al problema grazie al lavoro di squadra!
Bisogna ammettere che il caso è stato raro e, fortunatamente, riceviamo raramente richieste così complesse da parte degli utenti.
Fonte: habr.com
