Vom Google-Blog-Redakteur: Haben Sie sich jemals gefragt, wie die Ingenieure von Google Cloud Technical Solutions (TSE) Ihre Supportanfragen bearbeiten? Die Verantwortung der TSE-Supportingenieure liegt darin, die von Benutzern gemeldeten Problemquellen zu identifizieren und zu beheben. Einige dieser Probleme sind ziemlich einfach, aber manchmal tritt eine Anfrage auf, die die Aufmerksamkeit mehrerer Ingenieure erfordert. In diesem Artikel wird uns einer der TSE-Mitarbeiter von einem besonders kniffligen Problem aus seiner jüngsten Praxis berichten — . Während dieser Erzählung werden wir sehen, wie es den Ingenieuren gelang, die Situation zu lösen, und welche neuen Erkenntnisse sie bei der Behebung des Fehlers gewonnen haben. Wir hoffen, dass diese Geschichte Ihnen nicht nur von einem tief verwurzelten Bug erzählt, sondern auch Einblicke in die Abläufe gibt, die bei der Einreichung einer Anfrage beim Google Cloud-Support stattfinden.

Fehlerbehebung ist gleichzeitig Wissenschaft und Kunst. Alles beginnt mit der Aufstellung einer Hypothese über die Ursache des abnormalen Verhaltens des Systems, die dann auf die Probe gestellt wird. Bevor wir jedoch eine Hypothese formulieren, müssen wir das Problem klar definieren und präzise formulieren. Wenn die Frage zu vage klingt, müssen Sie alle Informationen sorgfältig analysieren; darin besteht die „Kunst“ der Fehlerbehebung.
Im Kontext von Google Cloud wird dieser Prozess erheblich erschwert, da Google Cloud sich bemüht, die Privatsphäre seiner Nutzer zu gewährleisten. Daher haben die Ingenieure von TSE weder Zugang zur Bearbeitung Ihrer Systeme noch die Möglichkeit, Konfigurationen so umfangreich zu überprüfen, wie es die Benutzenden tun. Daher können wir (Ingenieure) zum Testen einer unserer Hypothesen die Systeme nicht schnell modifizieren.
Einige Benutzer glauben, dass wir alles so reparieren wie Mechaniker in einer Werkstatt und senden uns einfach die ID der virtuellen Maschine, während der Prozess in Wirklichkeit in Form eines Dialogs verläuft: Informationssammlung, Hypothesenbildung und -verifizierung (oder widerlegung) und schließlich wird die Lösung des Problems auf der Kommunikation mit dem Kunden aufgebaut.
Das betrachtete Problem
Heute haben wir eine Geschichte mit einem positiven Ende. Einer der Gründe für die erfolgreiche Lösung des vorgelegten Falls liegt in der sehr detaillierten und präzisen Beschreibung des Problems. Unten sehen Sie eine Kopie des ersten Tickets (bearbeitet, um vertrauliche Informationen zu verbergen):

In dieser Nachricht sind viele nützliche Informationen für uns enthalten:
- Eine spezifische VM wurde genannt.
- Das Problem wurde beschrieben – DNS funktioniert nicht.
- Es wurde angegeben, wo sich das Problem zeigt – VM und Container.
- Die Schritte, die der Benutzer zur Feststellung des Problems unternommen hat, wurden aufgeführt.
Die Anfrage wurde als „P1: Kritischer Einfluss – Dienst in der Produktion unbrauchbar“ registriert, was ständige Überwachung der Situation 24/7 nach dem „Follow the Sun“-Schema bedeutet (hier können Sie mehr über die ) erfahren, wobei sie von einem Support-Team zum anderen bei jedem Wechsel der Zeitzonen übergeben wird. Im Wesentlichen hatte das Problem die Erde umrundet, bis es zu unserem Team in Zürich gelangte. Zu diesem Zeitpunkt hatte der Benutzer Maßnahmen zur Minderung der Auswirkungen ergriffen, aber befürchtete eine Wiederholung der Situation in der Produktion, da die Hauptursache noch nicht gefunden worden war.
Als das Ticket in Zürich ankam, hatten wir bereits folgende Informationen:
- Inhalt
/etc/hosts - Inhalt
/etc/resolv.conf - Ausgabe
iptables-save - Die gesammelte Datei
ngreppcap
Mit diesen Daten waren wir bereit, mit der „Untersuchung“ und Fehlerbehebung zu beginnen.
Unsere ersten Schritte
Zunächst überprüften wir die Protokolle und den Status des Metadatenservers und stellten sicher, dass er ordnungsgemäß funktioniert. Der Metadatenserver antwortet auf die IP-Adresse 169.254.169.254 und ist unter anderem für die Kontrolle von Domainnamen zuständig. Wir haben auch überprüft, ob die Firewall korrekt mit der VM arbeitet und keine Pakete blockiert.
Es war ein seltsames Problem: Die nmap-Überprüfung widerlegte unsere Hauptannahme über den Verlust von UDP-Paketen, also hatten wir einige weitere Optionen und Möglichkeiten, diese zu überprüfen, im Kopf:
- Werden Pakete selektiv verloren? => iptables-Regeln überprüfen.
- Ist die ? => Проверить вывод
ip a show - Betroffen sind nur UDP-Pakete oder auch TCP? =>
dig +tcp - Kommen die generierten dig-Pakete zurück? =>
tcpdump - Funktioniert libdns korrekt? =>
straceUm die Paketübertragung in beide Richtungen zu überprüfen.
Hierfür entscheiden wir uns, mit dem Benutzer zur Fehlerbehebung live zu telefonieren.
Während des Anrufs können wir mehrere Dinge überprüfen:
- Nach mehreren Prüfungen schließen wir die iptables-Regeln als mögliche Ursachen aus
- Wir überprüfen die Netzwerkschnittstellen und die Routing-Tabellen und vergewissern uns über die Korrektheit des MTU
- Wir stellen fest, dass
dig +tcp google.com(TCP) funktioniert einwandfrei, aberdig google.com(UDP) funktioniert nicht - Nach dem Testen
tcpdumpläuft es bisherdig, stellen wir fest, dass UDP-Pakete zurückgesendet werden - Wir führen aus
strace dig google.comund sehen, dass dig korrektsendmsg()undrecvmsg(), aber der zweite bricht aufgrund eines Zeitlimits ab
Leider endet der Dienst und wir müssen das Problem in die nächste Zeitzone übergeben. Das Anliegen hat jedoch das Interesse in unserem Team geweckt, und ein Kollege schlägt vor, ein ursprüngliches DNS-Paket mit dem Python-Modul scrapy zu erstellen.
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())Dieser Code erstellt ein DNS-Paket und sendet die Anfrage an den Metadatenserver.
Der Benutzer führt den Code aus, die DNS-Antwort wird zurückgegeben, und die Anwendung erhält sie, was das Fehlen eines Problems auf Netzwerkebene bestätigt.
Nach einer weiteren 'Weltreise' kommt die Anfrage zurück zu unserem Team, und ich übernehme sie vollständig, da ich denke, dass es für den Benutzer einfacher ist, wenn die Anfrage nicht weiter hin- und hergeschickt wird.
Inzwischen ist der Benutzer netterweise bereit, ein Snapshot des Systems bereitzustellen. Das sind sehr gute Nachrichten: Die Möglichkeit, das System selbst zu testen, beschleunigt die Fehlersuche erheblich, da ich den Benutzer nicht mehr bitten muss, Befehle auszuführen, mir die Ergebnisse zuzusenden und sie zu analysieren. Ich kann alles selbst erledigen!
Die Kollegen beginnen, ein wenig neidisch auf mich zu werden. Während des Mittagessens besprechen wir das Anliegen, aber niemand hat Ideen, was los sein könnte. Glücklicherweise hat der Benutzer bereits Maßnahmen zur Schadensminderung ergriffen und hat es nicht eilig, daher haben wir Zeit, das Problem genauer zu untersuchen. Und da wir ein Abbild haben, können wir alle interessanten Tests durchführen. Großartig!
Gehen wir einen Schritt zurück
Eine der häufigsten Fragen im Vorstellungsgespräch für die Position eines Systemingenieurs lautet: „Was passiert, wenn Sie pingen? ?» Вопрос шикарный, так как кандидату необходимо описать пусть от оболочки до пользовательского пространства, до ядра системы и далее к сети. Я улыбаюсь: иногда вопросы с интервью оказываются полезны и в реальной жизни…
Ich beschließe, diese HR-Frage auf das aktuelle Problem anzuwenden. Grob gesagt, wenn Sie versuchen, einen DNS-Namen aufzulösen, passiert Folgendes:
- Die Anwendung ruft eine Systembibliothek auf, zum Beispiel libdns
- libdns überprüft die Systemkonfiguration, um zu welchem DNS-Server sie Kontakt aufnehmen soll (in diesem Diagramm ist es 169.254.169.254, der Metadatenserver)
- libdns verwendet Systemaufrufe, um ein UDP-Socket (SOCK_DGRAM) zu erstellen und UDP-Pakete mit DNS-Anfragen in beide Richtungen zu übertragen
- Über die Schnittstelle sysctl kann der UDP-Stack auf Kernel-Ebene konfiguriert werden
- Der Kernel interagiert mit der Hardware, um Pakete über das Netzwerk über die Netzwerkschnittstelle zu übertragen
- Der Hypervisor fängt das Paket ab und leitet es an den Metadatenserver weiter, wenn er Kontakt mit ihm hat
- Der Metadatenserver bestimmt mit seiner Magie den DNS-Namen und gibt die Antwort auf ähnliche Weise zurück

Ich erinnere daran, welche Hypothesen wir bereits betrachtet haben:
Hypothese: Bibliotheken sind beschädigt
- Test 1: strace im System ausführen, um zu überprüfen, ob dig die richtigen Systemaufrufe tätigt
- Ergebnis: die richtigen Systemaufrufe werden aufgerufen
- Test 2: Über srapy überprüfen, ob wir Namen ohne Systembibliotheken definieren können
- Ergebnis: das können wir
- Test 3: rpm –V auf dem Paket libdns ausführen und md5sum der Bibliotheksdateien überprüfen
- Ergebnis: der Code der Bibliothek ist identisch mit dem Code im funktionierenden Betriebssystem
- Test 4: Das Image des Wurzelverzeichnisses des Benutzers auf einer VM ohne solches Verhalten einhängen, chroot ausführen und sehen, ob DNS funktioniert
- Ergebnis: DNS funktioniert korrekt
Fazit basierend auf den Tests: das Problem liegt nicht in den Bibliotheken
Hypothese: Es liegt ein Fehler in den DNS-Einstellungen vor
- Test 1: tcpdump überprüfen und beobachten, ob die DNS-Pakete nach dem Start von dig korrekt gesendet und empfangen werden
- Ergebnis: die Pakete werden korrekt übertragen
- Test 2: am Server erneut überprüfen
/etc/nsswitch.confund/etc/resolv.conf - Ergebnis: alles ist korrekt
Fazit basierend auf den Tests: das Problem liegt nicht in der DNS-Konfiguration
Hypothese: Der Kernel ist beschädigt
- Test: neues Kernel installieren, Signatur überprüfen, neu starten
- Ergebnis: ähnliches Verhalten
Fazit basierend auf den Tests: der Kernel ist nicht beschädigt
Hypothese: Falsches Verhalten des Benutzernetzwerks (oder der Netzwerkschnittstelle des Hypervisors)
- Test 1: Firewall-Einstellungen überprüfen
- Ergebnis: Die Firewall lässt DNS-Pakete sowohl auf dem Host als auch auf GCP durch
- Test 2: Den Datenverkehr abfangen und die Korrektheit der Übertragung und Rückgabe von DNS-Anfragen verfolgen
- Ergebnis: tcpdump bestätigt den Erhalt der Rückwärts-Pakete durch den Host
Fazit basierend auf den Tests: das Problem liegt nicht im Netzwerk
Hypothese: Der Metadatenserver funktioniert nicht
- Test 1: Die Protokolle des Metadatenservers auf Anomalien überprüfen
- Ergebnis: In den Protokollen gibt es keine Anomalien
- Test 2: Um den Metadatenserver zu umgehen
dig @8.8.8.8 - Ergebnis: die Auflösung schlägt fehl, selbst ohne den Metadatenserver zu nutzen
Fazit basierend auf den Tests: das Problem liegt nicht am Metadatenserver
Fazit: wir haben alle Teilsysteme außer den Ausführungseinstellungen getestet!
Tief in die Kern-Ausführungseinstellungen eintauchend
Um die Ausführungseinstellungen des Kerns zu konfigurieren, können Sie die Befehlszeilenoptionen (grub) oder die sysctl-Schnittstelle verwenden. Ich schaute in /etc/sysctl.conf und denken Sie nur, ich fand einige benutzerdefinierte Einstellungen. Es fühlte sich an, als hätte ich etwas erwischt, ich schloss alle nicht-netzwerk- oder nicht-tcp Einstellungen aus und blieb mit einer Handvoll Einstellungen zurück net.core. Dann wandte ich mich dorthin, wo die VM die Berechtigungen des Hosts hat und begann nacheinander die Einstellungen von der defekten VM anzuwenden, bis ich den Übeltäter fand:
net.core.rmem_default = 2147483647Hier ist sie, die DNS-Konfiguration, die kaputt macht! Ich fand die Waffe des Verbrechens. Aber warum passiert das? Ich brauchte immer noch ein Motiv.
Die Konfiguration der Grundgröße des DNS-Paket-Puffers erfolgt über net.core.rmem_default. Ein typischer Wert variiert etwa zwischen 200 KiB, jedoch, wenn Ihr Server viele DNS-Pakete erhält, können Sie die Puffergröße erhöhen. Wenn der Puffer zum Zeitpunkt des Eintreffens eines neuen Pakets voll ist, zum Beispiel weil die Anwendung es nicht schnell genug verarbeitet, fangen Sie an, Pakete zu verlieren. Unser Kunde hat die Puffergröße korrekt erhöht, da er Datenverluste befürchtete, weil er eine Anwendung zur Erfassung von Metriken über DNS-Pakete verwendete. Der Wert, den er eingestellt hat, war der maximal mögliche: 231-1 (wenn Sie 231 einstellen, gibt der Kernel „UNGÜLTIGES ARGUMENT“ zurück).
Plötzlich wurde mir klar, warum nmap und scapy korrekt funktionierten: sie nutzten Rohsockets! Rohsockets unterscheiden sich von normalen: sie umgehen iptables und sie sind nicht gepuffert!
Aber warum verursacht ein „zu großer Puffer“ Probleme? Er funktioniert offensichtlich nicht so, wie beabsichtigt.
Zu diesem Zeitpunkt konnte ich das Problem auf mehreren Kernen und verschiedenen Distributionen reproduzieren. Das Problem trat bereits auf Kernel 3.x auf und trat jetzt auch auf Kernel 5.x auf.
Tatsächlich, beim Ausführen von
sysctl -w net.core.rmem_default=$((2**31-1))hörte DNS auf zu funktionieren.
Ich begann, nach funktionierenden Werten durch einen einfachen binären Suchalgorithmus zu suchen und stellte fest, dass das System mit 2147481343 arbeitete, aber diese Zahl war für mich eine bedeutungslose Ziffernreihe. Ich schlug dem Kunden vor, diese Zahl auszuprobieren, und er antwortete, dass das System mit google.com funktionierte, aber immer noch Fehler mit anderen Domains ausgab, also setzte ich meine Ermittlungen fort.
Ich installierte , ein Tool, das ich früher hätte nutzen sollen: Es zeigt, wo genau das Paket im Kern ankommt. Verursacht wurde es durch die Funktion udp_queue_rcv_skb. Ich lud den Quellcode des Kernels herunter und fügte einige hinzu, um genau zu verfolgen, wo das Paket landet. Ich entdeckte schnell die benötigte Bedingung if, und eine Zeit lang starrte ich einfach darauf, denn es war genau dann, als endlich alles in ein stimmiges Gesamtbild zusammenfiel: 231-1, eine bedeutungslose Zahl, eine nicht funktionierende Domain… Es lag an einem Code-Schnipsel in __udp_enqueue_schedule_skb:
if (rmem > (size + sk->sk_rcvbuf))
goto uncharge_drop;Bitte beachten Sie:
rmemhat den Typ intGrößehat den Typ u16 (nicht-signierter 16-Bit-Integer) und speichert die Paketgrößesk->sk_rcybufhat den Typ int und speichert die Größe des Puffers, die per Definition dem Wert innet.core.rmem_default
Wenn sk_rcvbuf nahezu 231 entspricht. Die Summierung der Paketgröße kann zu . Und da es int ist, wird sein Wert negativ, wodurch die Bedingung wahr wird, wenn sie falsch sein sollte (mehr dazu kann man erfahren über ).
Der Fehler wird auf einfache Weise behoben: durch Umwandlung in unsigned int. Ich wandte den Fix an und startete das System neu, danach funktionierte der DNS wieder.
Der Geschmack des Sieges
Ich überwies meine Entdeckungen an den Kunden und schickte den Kernel-Patch. Ich bin zufrieden: Jedes Puzzlestück fügte sich zusammen, ich kann genau erklären, warum wir das beobachtet haben, was wir beobachtet haben, und das Wichtigste ist, dass wir das Problem durch Zusammenarbeit lösen konnten!
Es ist zugegeben, dass dieser Fall selten war und glücklicherweise erhalten wir von den Nutzern selten so komplizierte Anfragen.
Quelle: habr.com
