Vom Blog-Redakteur von Google: Haben Sie sich jemals gefragt, wie die Ingenieure von Google Cloud Technical Solutions (TSE) mit Ihren Supportanfragen umgehen? Die Verantwortung der TSE-Supportingenieure liegt darin, die von den Nutzern angegebenen Probleme zu identifizieren und zu beheben. Einige dieser Probleme sind relativ einfach, aber manchmal gibt es Anfragen, die die Aufmerksamkeit mehrerer Ingenieure erfordern. In diesem Artikel wird uns ein TSE-Mitarbeiter von einem sehr kniffligen Problem aus seiner jüngsten Praxis erzählen — . In dieser Erzählung werden wir sehen, wie es den Ingenieuren gelungen ist, die Situation zu klären und was sie während der Fehlersuche gelernt haben. Wir hoffen, dass diese Geschichte Ihnen nicht nur von einem tief verwurzelten Bug berichtet, sondern auch Einblicke in die Abläufe gibt, die bei der Einreichung einer Anfrage an den Google Cloud Support stattfinden.

Fehlerbehebung ist sowohl eine Wissenschaft als auch eine Kunst. Alles beginnt mit der Hypothese über die Ursache des unerwarteten 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 alles gründlich analysieren; darin besteht die "Kunst" der Fehlerbehebung.
Im Umfeld von Google Cloud wird dieser Prozess erheblich komplizierter, da Google Cloud sich intensiv bemüht, die Privatsphäre seiner Nutzer zu gewährleisten. Aus diesem Grund haben TSE-Ingenieure weder Zugang zur Bearbeitung Ihrer Systeme noch die Möglichkeit, Konfigurationen so umfassend zu überprüfen wie die Benutzer. Daher können wir (die Ingenieure) für die Überprüfung einer unserer Hypothesen nicht schnell das System anpassen.
Einige Nutzer glauben, wir könnten alle Mechanismen wie eine Autowerkstatt beheben und senden uns einfach die ID der virtuellen Maschine. In Wirklichkeit verläuft der Prozess jedoch im Rahmen eines Gesprächs: Informationssammlung, Hypothesenbildung und -bestätigung (oder -verwerfung), und letztendlich wird die Problemlösung im Dialog mit dem Kunden erarbeitet.
Das betrachtete Problem
Heute haben wir eine Geschichte mit gutem Ausgang. Einer der Gründe für die erfolgreiche Lösung des vorgestellten Falls liegt in der sehr detaillierten und präzisen Beschreibung des Problems. Nachfolgend finden Sie eine Kopie des ersten Tickets (bearbeitet, um vertrauliche Informationen zu verbergen):

In dieser Nachricht gibt es viele nützliche Informationen für uns:
- Die spezifische VM ist angegeben
- Das Problem wird beschrieben — DNS funktioniert nicht
- Es wird angegeben, wo sich das Problem zeigt — VM und Container
- Es sind die Schritte aufgeführt, die der Nutzer unternommen hat, um das Problem zu ermitteln
Die Anfrage wurde als «P1: Kritischer Einfluss — Dienst nicht nutzbar in der Produktion» registriert, was bedeutet, dass die Situation rund um die Uhr nach dem Schema «Follow the Sun» überwacht wird (über diesen Link erfahren Sie mehr über ), wobei es von einem Support-Team zum anderen bei jeder Verschiebung der Zeitzonen weitergegeben wurde. Im Grunde genommen hatte das Problem, als es schließlich zu unserem Team in Zürich gelangte, bereits die Welt umrundet. Zu diesem Zeitpunkt hatte der Benutzer Maßnahmen zur Minderung der Auswirkungen ergriffen, war jedoch besorgt über eine mögliche Wiederholung der Situation im Produktionsumfeld, da die Hauptursache weiterhin unbekannt war.
Als das Ticket in Zürich ankam, hatten wir bereits folgende Informationen:
- Inhalt
/etc/hosts - Inhalt
/etc/resolv.conf - Fazit
iptables-save - Von unserem Team gesammelt
ngreppcap-Datei
Mit diesen Daten waren wir bereit, mit der Phase der „Untersuchung“ und Fehlersuche zu beginnen.
Unsere ersten Schritte
Zunächst überprüften wir die Protokolle und den Status des Metadatenservers und stellten sicher, dass er korrekt funktioniert. Der Metadatenserver antwortet auf die IP-Adresse 169.254.169.254 und ist unter anderem für die Kontrolle über die Domainnamen zuständig. Außerdem haben wir überprüft, dass die Firewall ordnungsgemäß mit der VM funktioniert und keine Pakete blockiert.
Es war ein merkwürdiges Problem: Der nmap-Scan widerlegte unsere Hauptannahme über verlorene UDP-Pakete, sodass wir gedanklich weitere Möglichkeiten und Prüfmethoden formulierten:
- Werden die Pakete selektiv verloren? => Überprüfen Sie die iptables-Regeln
- Ist das nicht zu klein? ? => Проверить вывод
ip a show - Betroffen das Problem nur UDP-Pakete oder auch TCP? => Testen
dig +tcp - Kommen die generierten dig-Pakete zurück? => Testen
tcpdump - Funktioniert libdns korrekt? => Testen
straceum die Paketübertragung in beide Richtungen zu überprüfen
Hier entscheiden wir uns, mit dem Benutzer für eine Live-Fehlerbehebung zu telefonieren.
Während des Anrufs gelingt es uns, mehrere Dinge zu überprüfen:
- Nach mehreren Tests schließen wir die iptables-Regeln als mögliche Ursache aus.
- Wir überprüfen die Netzwerkschnittstellen und Routingtabellen und prüfen die MTU genau.
- Wir stellen fest, dass
dig +tcp google.com(TCP) funktioniert einwandfrei, aberdig google.com(UDP) funktioniert nicht - Nach dem Testen
tcpdumpläuft es nochdig, stellen wir fest, dass UDP-Pakete zurückkommen - Wir testen
strace dig google.comund sehen, dass dig korrektsendmsg()undrecvms(), jedoch wird das zweite aufgrund eines Timeouts unterbrochen.
Leider endet die Schicht, und wir müssen das Problem in die nächste Zeitzone übergeben. Das Anliegen hat dennoch das Interesse unseres Teams geweckt, und ein Kollege schlägt vor, das ursprüngliche 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 Codeausschnitt erstellt ein DNS-Paket und sendet eine Anfrage an den Metadatenserver.
Der Benutzer führt den Code aus, der DNS-Antwort wird zurückgegeben und die Anwendung empfängt sie, was das Fehlen von Problemen auf Netzwerkebene bestätigt.
Nach einem weiteren "Weltumrundung" kehrt die Anfrage an unser Team zurück, und ich übernehme sie vollständig, da ich denke, dass es für den Benutzer einfacher ist, wenn die Anfrage nicht weiterhin hin und her geschoben wird.
In der Zwischenzeit ist der Benutzer freundlicherweise bereit, ein Abbild des Systems bereitzustellen. Das sind sehr gute Nachrichten: Die Möglichkeit, das System selbst zu testen, beschleunigt die Fehlersuche erheblich, da ich nicht mehr den Benutzer bitten muss, Befehle auszuführen, mir die Ergebnisse zu schicken und diese zu analysieren; ich kann alles selbst erledigen!
Die Kollegen fangen langsam an, mir ein wenig neidisch zu werden. Während des Mittagessens diskutieren wir den Vorfall, doch niemand hat eine Ahnung, was eigentlich passiert. Glücklicherweise hat der Benutzer bereits Maßnahmen ergriffen, um die Folgen zu mildern, und hat es nicht eilig, also haben wir Zeit, das Problem zu analysieren. Und da wir ein Abbild haben, können wir alle gewünschten Tests durchführen. Ausgezeichnet!
Einen Schritt zurückgehen
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, wie z. B. libdns.
- libdns prüft die Systemkonfiguration, um herauszufinden, welchen DNS-Server sie ansprechen soll (auf dem Diagramm ist das 169.254.169.254, der Metadatenserver).
- libdns verwendet Systemaufrufe, um einen UDP-Socket (SOCK_DGRAM) zu erstellen und UDP-Pakete mit der DNS-Anfrage bidirektional zu übertragen.
- Über die sysctl-Schnittstelle 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 überträgt es beim Kontakt an den Metadaten-Server.
- Der Metadaten-Server bestimmt mit seiner Magie den DNS-Namen und gibt die Antwort auf die gleiche Weise zurück.

Ich erinnere daran, welche Hypothesen wir bereits betrachtet haben:
Hypothese: Bibliotheken sind defekt.
- Test 1: In der Systemumgebung strace ausführen, um zu überprüfen, ob dig die korrekten Systemaufrufe tätigt.
- Ergebnis: Es werden die korrekten Systemaufrufe getätigt.
- Test 2: Über scrapy prüfen, ob wir Namen außerhalb der Systembibliotheken bestimmen können.
- Ergebnis: Das können wir.
- Test 3: rpm -V für das Paket libdns und md5sum für die Bibliotheksdateien ausführen.
- Ergebnis: Der Code der Bibliothek ist vollständig identisch mit dem Code im funktionierenden Betriebssystem.
- Test 4: Das Abbild des Root-Dateisystems des Benutzers auf der VM mounten, die nicht dieses Verhalten zeigt, chroot ausführen und überprüfen, ob DNS funktioniert.
- Ergebnis: DNS funktioniert korrekt.
Schlussfolgerung basierend auf den Tests: Das Problem liegt nicht an den Bibliotheken.
Hypothese: Es gibt einen Fehler in den DNS-Einstellungen.
- Test 1: tcpdump überprüfen und beobachten, ob die DNS-Pakete korrekt gesendet und empfangen werden, nachdem dig ausgeführt wurde.
- Ergebnis: Die Pakete werden korrekt übertragen.
- Test 2: Auf dem Server erneut prüfen.
/etc/nsswitch.confund/etc/resolv.conf - Ergebnis: Alles ist korrekt.
Schlussfolgerung basierend auf den Tests: Das Problem liegt nicht an der DNS-Konfiguration
Hypothese: Kernel beschädigt
- Test: Neues Kernel installieren, Signatur überprüfen, neu starten
- Ergebnis: ähnliches Verhalten
Schlussfolgerung basierend auf den Tests: Kernel ist nicht beschädigt
Hypothese: fehlerhaftes Verhalten des Benutzernetzwerks (oder des Netzwerkinterfaces 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: Verkehr abfangen und die Korrektheit der Übertragung und Rückgabe von DNS-Anfragen verfolgen
- Ergebnis: tcpdump bestätigt den Empfang von Rückpaketen durch den Host
Schlussfolgerung basierend auf den Tests: Das Problem liegt nicht im Netzwerk
Hypothese: Der Metadatenserver funktioniert nicht
- Test 1: Protokolle des Metadatenservers auf Anomalien überprüfen
- Ergebnis: Keine Anomalien in den Protokollen
- Test 2: Metadatenserver umgehen über
dig @8.8.8.8 - Ergebnis: Die Auflösung wird auch ohne Benutzung des Metadatenservers gestört
Schlussfolgerung basierend auf den Tests: Das Problem liegt nicht beim Metadatenserver
Fazit: Wir haben alle Subsysteme getestet außer den Einstellungen der Laufzeitumgebung!
Tauchend in die Einstellungen der Kernel-Laufzeitumgebung
Für die Konfiguration der Kernel-Laufzeitumgebung können Sie die Optionen der Befehlszeile (grub) oder die sysctl-Schnittstelle nutzen. Ich habe einen Blick in /etc/sysctl.conf Und denken Sie nur, ich habe mehrere benutzerdefinierte Einstellungen entdeckt. Als hätte ich etwas Wertvolles gefunden, habe ich alle nicht-netzwerk- oder nicht-TCP-Einstellungen verworfen und bin mit einer Handvoll Netzwerkoptionen geblieben. net.core. Dann habe ich dort nachgesehen, wo die Berechtigungen der VM liegen, und begonnen, nacheinander die Einstellungen der fehlerhaften VM anzuwenden, bis ich den Übeltäter gefunden habe:
net.core.rmem_default = 2147483647Da haben wir die DNS-Konfiguration, die das Problem verursacht! Ich habe das Werkzeug des Verbrechens gefunden. Aber warum passiert das? Ich brauchte immer noch ein Motiv.
Die Einstellung der Basisgröße des DNS-Paketpuffers erfolgt über net.core.rmem_default. Der typische Wert variiert ungefähr zwischen 200 KiB. Wenn Ihr Server jedoch viele DNS-Pakete empfängt, können Sie die Puffergröße erhöhen. Wenn zu einem Zeitpunkt, an dem ein neues Paket eintrifft, der Puffer voll ist – beispielsweise, weil die Anwendung es nicht schnell genug verarbeitet – beginnen Sie, 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 "INVALID ARGUMENT" zurück).
Plötzlich wurde mir klar, warum nmap und scapy korrekt funktioniert haben: Sie verwendeten rohe Sockets! Rohe Sockets unterscheiden sich von normalen: Sie umgehen iptables und sind nicht gepuffert!
Aber warum verursacht ein 'zu großer Puffer' Probleme? Es funktioniert eindeutig nicht wie vorgesehen.
Zu diesem Zeitpunkt konnte ich das Problem auf mehreren Kernels und vielen Distributionen reproduzieren. Das Problem trat bereits bei Kernel 3.x auf und zeigte sich nun auch bei Kernel 5.x.
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 funktionierte. Dieses Zahl war jedoch für mich ein sinnloses Zahlenmuster. 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 Untersuchung fort.
Ich installierte , ein Tool, das ich früher hätte nutzen sollen: Es zeigt, wo genau das Paket im Kernel ankommt. Die Schuld lag bei der Funktion udp_queue_rcv_skb. Ich lud die Kernel-Quellen herunter und fügte einige hinzu um nachzuvollziehen, wohin das Paket genau gelangt. Ich habe schnell die erforderliche Bedingung erkannt. wenn, und eine Zeit lang starrte ich einfach darauf, denn genau dann ergab alles endlich ein klares Bild: 231-1, eine sinnlose 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:
rmemhat den Typ intGrößehat den Typ u16 (nicht signierter 16-Bit-Int) und speichert die Größe des Pakets.sk->sk_rcybufhat den Typ int und speichert die Größe des Puffers, der definitionsgemäß dem Wert innet.core.rmem_default
Wenn sk_rcvbuf nahe 231 kommt; die Summe der Paketgröße kann zu . Und da es sich um ein int handelt, wird sein Wert negativ; damit wird die Bedingung wahr, wenn sie falsch sein sollte (mehr dazu erfahren Sie in ).
Der Fehler wird auf triviale Weise behoben: durch Umwandlung in unsigned int. Ich habe das Update angewendet und das System neu gestartet, daraufhin funktionierte DNS wieder.
Der Geschmack des Sieges
Ich habe meine Entdeckungen an den Kunden weitergeleitet und Kern-Patch. Ich bin zufrieden: Jedes Puzzlestück hat zusammengefunden, ich kann genau erklären, warum wir das beobachtet haben, und das Wichtigste ist, dass wir dank der Teamarbeit eine Lösung für das Problem finden konnten!
Man muss zugeben, dass der Fall selten war, und glücklicherweise erhalten wir von Benutzern selten so komplexe Anfragen.
Quelle: habr.com
