Die drei Fehlbegriffe, die man kennen sollte
Willkommen
Verteilte Systeme versagen in Mustern. Sobald man die Muster kennt, wird jeder Postmortem zu einer Übung im Erkennen statt zu einem Rätsel.
Drei Konzepte decken den Großteil dessen ab, was bei der Analyse von Produktionsfehlern wichtig ist:
Einzelpunktversagen (SPOF): eine Komponente, deren Ausfall ein größeres System mitreißt. Oft unsichtbar: der DNS-Server, von dem alle abhängen; das Zertifikat, gegen das alles erneuert wird; der einzelne Datenbank-Master.
Kaskadierende Ausfälle: Der Ausfall einer Komponente löst den Ausfall einer anderen aus, die wiederum eine weitere auslöst. Eine langsame Datenbank verursacht Timeouts in der API-Ebene, was zu Wiederholungsversuchen führt, die die Datenbank weiter belasten, was zu weiteren Timeouts führt. Die Welle der Ausfälle breitet sich aus.
Auswirkungsgrad (Blast Radius): Wie viel vom System ausfällt, wenn ein Teil versagt. Architektonische Entscheidungen begrenzen oder lassen den Auswirkungsgrad unbeschränkt. Eine SPOF (Single Point of Failure) hat einen unbeschränkten Auswirkungsgrad. Ein Service mit Bulkhead-Isolierung hat einen begrenzten Auswirkungsgrad.
Bis zum Ende dieser Lektion werden Sie:
- SPOFs in einer Architektur durch Inspektion identifizieren
- Muster kaskadierender Ausfälle erkennen: Thundering Herd, Retry Storm, Queue of Death
- Eine echte Timeline lesen und den Auslöser vom latenten Defekt trennen, den der Auslöser aufgedeckt hat
- Schuldzuweisungslose Maßnahmenpunkte formulieren, die Systeme statt Personen ansprechen und Prävention / Erkennung / Wiederherstellung abdecken
- Über Bulkheads und Circuit Breaker als Werkzeuge zur Begrenzung des Auswirkungsgrades nachdenken
Einzelpunktfehler erkennen
Inspektion der geschichteten Architektur
Betrachten Sie eine kleine Web-Architektur:
- DNS: api.example.com -> einzelne Nameserver-IP 203.0.113.10, gehostet von einem einzigen DNS-Anbieter
- CDN: ein einzelner CDN-Anbieter vor api.example.com
- Ingress: zwei Reverse-Proxy-Maschinen hinter einem Lastausgleich (Load Balancer)
- Backend: sechs API-Replikate in zwei Verfügbarkeizonen (drei pro Zone)
- Datenbank: eine Primärinstanz + eine Lese-Replikate, in derselben Verfügbarkeizone
- Cache: Redis-Cluster, drei Knoten, verteilt über dieselben zwei Verfügbarkeitszonen
Frage: Welche Komponenten sind SPOFs? Hinweis: SPOFs sind nicht immer die offensichtliche Art „einzelnes Gerät“. Ein Cluster aus drei Maschinen in einer einzigen Verfügbarkeitszone ist ein SPOF für den Ausfall dieser Zone.
Drei klassische Kaskadenmuster
Fehler breiten sich über Abhängigkeiten aus
Muster 1: Donnerschlag-Schwarm (Thundering Herd). Eine gemeinsam genutzte Ressource (Cache, Sperre, Datenbank) schlägt aus oder wird neu gestartet. Jeder Client, der von ihr abhängig war, führt gleichzeitig neue Anfragen aus. Die Flut überfordert das wiederhergestellte System; die Wiederholungsanfragen häufen sich schneller an, als die Wiederherstellung sie bewältigen kann; die Wiederherstellung wird nie abgeschlossen.
Muster 2: Wiederholungssturm (Retry Storm). Ein nachgelagerter Dienst wird langsamer. Die aufrufenden Dienste im vorgelagerten Bereich führen stattdessen neue Anfragen aus. Die Wiederholungen multiplizieren die ursprüngliche Last. Der langsame Dienst wird noch langsamer, was weitere Wiederholungen auslöst. Schließlich übersteigt die Last sogar die Kapazität einer gesunden Version des Dienstes.
Muster 3: Todeswarte (Queue of Death). Eine Verarbeitungswarteschlange ohne Rückdruck (Backpressure) empfängt Daten schneller, als sie verarbeitet werden können. Die Warteschlange wächst unbegrenzt. Der Speicher wird erschöpft; der Verbraucher (Consumer) stürzt ab; startet neu; findet eine noch größere Warteschlange vor; stürzt erneut ab.
Gemeinsamer Nenner: Eine kleine anfängliche Störung löst eine positive Rückkopplungsschleife aus. Die eigene Reaktion des Systems verstärkt den Fehler, anstatt ihn zu dämpfen.
Dämpfungsmechanismen
Exponentielles Backoff mit Jitter. Clients, die erneut versuchen, warten jedes Mal länger, mit einem zufälligen Offset. Verhindert synchronisierte Wellen von Wiederholungsversuchen.
Circuit Breaker. Ein Aufrufer überwacht die Fehlerquote der nachgelagerten Komponente. Überschreitet diese einen Schwellenwert, stoppt der Aufrufer die Aufrufe für eine Abklingphase und schlägt seine eigenen Anfragen sofort fehl. Verhindert unnütze Arbeit und ermöglicht die Erholung der nachgelagerten Komponente.
Bulkhead. Ressourcen pro Abhängigkeit isolieren. Verbindungspool A für die Datenbank, separater Verbindungspool B für den Cache. Eine langsame Datenbank kann nicht alle Verbindungen auslasten; Cache-Aufrufe setzen sich fort.
Load Shedding. Bei Überlastung Anfragen am Rand verwerfen, anstatt sie anzunehmen und langsam zu fehlschlagen. Ein 429 in 1 ms ist besser als ein 500 in 30 Sekunden.
Backpressure. Produzenten verlangsamen, wenn Konsumenten nicht mithalten können. Warteschlangen werden begrenzt; Sender blockieren; die ursprüngliche Arbeitsquelle spürt den Widerstand.
Kaskadierende Ausfälle diagnostizieren
Die API-Ebene eines Teams bricht während eines routinemäßigen Datenbank-Failovers zusammen. Zeitleiste:
- 14:00:00 — Der Operator befördert die Standby-Datenbank. Erwartete Ausfallzeit: ca. 10 Sekunden.
- 14:00:08 — Primärknoten nicht verfügbar. API-Ebenen-Anfragen beginnen, mit Datenbankverbindungsfehlern zu fehlschlagen.
- 14:00:08 — Die API-Ebene führt Wiederholungen durch (Standardkonfiguration: 5 Wiederholungen, kein Backoff, 100 ms Abstand).
- 14:00:11 — Standby-Knoten befördert, akzeptiert neue Verbindungen.
- 14:00:11 — Die API-Ebene öffnet gleichzeitig Tausende neue Datenbankverbindungen (jeder Replikate × jede gleichzeitige Anfrage × jede Wiederholung).
- 14:00:13 — Der Verbindungs-Pool des neuen Primärknotens ist erschöpft; neue Verbindungen werden abgelehnt.
- 14:00:13-14:05:00 — Die Replikate der API-Ebene erschöpfen ihre Verbindungspools, werfen Ausnahmen, stürzen ab, starten neu und wiederholen den Vorgang.
- 14:05:00 — Der Operator stoppt den API-Ebenen-Verkehr manuell; die Datenbank stabilisiert sich.
- 14:10:00 — Die schrittweise Wiederherstellung des Datenverkehrs ist abgeschlossen. Gesamtausfallzeit: ~10 Minuten (im Vergleich zur erwarteten Zeit von ~10 Sekunden).
DNS SERVFAIL: Zwei sich überlagernde Fehler
Ein Postmortem in realistischer Form
Im Folgenden finden Sie eine anonymisierte Version eines echten Vorfalls. Die Namen der Anbieter wurden geändert, die IP-Adressen anonymisiert; der Verlauf, die Chronologie und die Lehren sind jedoch real.
Zusammenfassung
Die Website example.com lieferte von allen öffentlichen DNS-Resolvern SERVFAIL für etwa 3–4 Stunden. Alle anderen 46 Zonen auf demselben DNS-Master waren nicht betroffen. Grundursache: zwei sich überlagernde Fehler.
1. Anbieter A (ein sekundärer DNS-Dienstleister) fügte eine neue interne Synchronisations-IP hinzu, die nicht in der allow-axfr-ips-Erlaubnisliste des Primärs enthalten war.
2. Die Zone example.com wies einen jahrelang bestehenden, gegen die RFC verstoßenden CNAME-Konflikt auf (demo.example.com hatte sowohl CNAME- als auch MX-/TXT-Einträge auf derselben Ebene), was dazu führte, dass Anbieter A die Zone bei einem neuen AXFR ablehnte.
Zeitplan (UTC)
- ~15:00 — Anbieter A fügt die neue Synchronisations-IP 198.51.100.42 in seine Infrastruktur ein
- 15:02 — der erste AXFR-out denied-Eintrag für 198.51.100.42 erscheint in den Primär-DNS-Protokollen (keine Alarmierung für dieses Signal)
- ~18:00 — SOA-Ablaufzeitfenster erreicht; Anbieter A entfernt die example.com-Zone aus dem Cache
- ~18:30 — SERVFAIL wird extern festgestellt
- ~19:45 — Ursache identifiziert
- 20:00 — 198.51.100.42 wird zu allow-axfr-ips hinzugefügt; Primärsystem neu gestartet
- 20:05 — NOTIFY gesendet; AXFR gestartet; Zone weiterhin SERVFAIL (CNAME-Konflikt)
- 20:07 — check-zone zeigt 1 Fehler: CNAME-Konflikt bei demo.example.com
- 20:09 — CNAME durch A-Record ersetzt; Zonenprüfung fehlerfrei (0 Fehler)
- 20:10 — NOTIFY gesendet; AXFR abgeschlossen; Anbieter A beginnt mit der Bereitstellung der Zone
- 20:11 — dig @8.8.8.8 example.com A liefert die korrekte IP-Adresse — BEHOBEN
Warum nur example.com?
Alle 47 Zonen teilen sich denselben DNS-Primärserver. Die IP-Blockade für AXFR betraf alle Zonen. Aber nur example.com hatte den CNAME-Konflikt, & nur example.com benötigte zum Zeitpunkt der Durchsetzung der Sperrung einen frischen AXFR. Andere Zonen hatten sich bereits vor der Sperrung aktualisiert oder brauchten es noch nicht.
Latenter Defekt
Der CNAME-Konflikt bei demo.example.com bestand seit Jahren. Er funktionierte, weil der Primärserver die Zone aus seiner Datenbank bereitstellte (nachsichtig gegenüber RFC-Verstößen) & Anbieter A von veralteten CACHEDATEN aus der Zeit vor der Einführung des Verstoßes lieferte. Als Anbieter A seinen Cache verworfen und frische Daten benötigte, trat der Verstoß zutage.
Auslöser
Anbieter A fügte stillschweigend eine neue Synchronisations-IP-Adresse hinzu. Die Allowlist des Primärservers enthielt diese nicht. Die AXFR-Anfrage wurde abgelehnt. Drei Stunden später (SOA expire) entfernte Anbieter A die Zone. Der latente Fehler wurde sichtbar, als das System versuchte, sich zu erholen.
Schuldzuweisungslose Maßnahmen (Blameless Action Items) formulieren
Schuldzuweisungslos: Systeme ansprechen, keine Personen
Ein schuldzuweisungsloses Maßnahmenziel benennt etwas, das das System anders tun sollte, nicht etwas, das eine Person anders tun sollte. „Den Operator schulen“ ist eine Schuldzuweisung. „Einen automatisierten Check hinzufügen, der dies vor der Bereitstellung erkennt“, ist schuldzuweisungslos.
Gute schuldzuweisungslose Maßnahmen gruppieren sich in drei Dimensionen:
- Prävention: das Ungewollte erschweren oder unmöglich machen
- Erkennung: es früher bemerken, wenn es passiert
- Wiederherstellung: den Schaden begrenzen, wenn es passiert
Jeder Punkt sollte (1) die spezifische Systemänderung, (2) ein zuständiges Team und (3) die Dimension benennen, der er dient.
Abteilungen, die ohne das Schiff sinken
Entlehnt der Schiffsbautechnik
Schiffe verfügen über wasserdichte Schotten: senkrechte Wände, die den Rumpf in Abteilungen unterteilen. Eine Abteilung kann überfluten, ohne dass das Schiff sinkt; eine andere kann ausfallen, ohne dass der Rest betroffen ist.
Verteilte Systeme übernehmen denselben Begriff und dieselbe Idee.
Schotten-Muster: Ressourcen pro Abhängigkeit isolieren. Ein Dienst, der drei nachgelagerte APIs aufruft, verwendet drei separate Verbindungspools, drei separate Thread-Pools und drei separate Wiederholungs-Budgets. Eine langsame oder fehlerhafte nachgelagerte Komponente kann die für die anderen beiden reservierten Ressourcen nicht aufbrauchen.
Ohne Schotten: Eine langsame Abhängigkeit erschöpft den gemeinsamen Thread-Pool; Aufrufe an andere Abhängigkeiten blockieren, während sie auf Threads warten; der gesamte Dienst wird unempfindlich.
Mit Schotten: Eine langsame Abhängigkeit erschöpft ihren eigenen Pool; Aufrufe an sie schlagen schnell fehl; Aufrufe an andere Abhängigkeiten laufen normal weiter; der Schadensumfang bleibt auf die fehlerhafte Abhängigkeit beschränkt.
Sicherungsschalter
Circuit-Breaker-Muster: ein zustandsbehaftetes Wrapper um eine nachgelagerte Abhängigkeit, das die Fehlerquote nachverfolgt. Drei Zustände:
- Geschlossen (normal): Aufrufe werden durchgelassen. Fehler werden gezählt.
- Offen (ausgelöst): Wird eine Fehler-Schwelle überschritten (z. B. 50 % Fehler in den letzten 30 Sekunden), wird der Schalter geöffnet. Aufrufe schlagen sofort fehl, ohne die Abhängigkeit zu versuchen. Das spart dem Aufrufer unnötige Arbeit und schützt die Abhängigkeit davor, Last zu erhalten, während sie ungesund ist.
- Halb offen (Test): Nach einer Abklingzeit lässt der Schalter einen kleinen Anteil der Aufrufe durch. Wenn diese erfolgreich sind, schließt er wieder in den Normalzustand. Wenn sie fehlschlagen, öffnet er sich erneut für eine weitere Abklingzeit.
Die zentrale Erkenntnis: Der Circuit Breaker verhindert unnötige Aufwendungen während bekannt ungesunder Zeiträume und gibt der nachgelagerten Komponente die Chance, sich zu erholen, ohne weiterhin belastet zu werden.
Bulkheads begrenzen die Ausbreitungsgeschwindigkeit. Circuit Breakers verhindern, dass die Ausbreitung sich selbst aufrechterhält.
Die Ausbreitungsgeschwindigkeit begrenzen
Ihr API-Dienst ruft vier nachgelagerte Dienste auf: User Service, Recommendation Service, Notification Service und eine Payment-API eines Drittanbieters. Das Team hat gehört, dass „der Recommendation Service etwas unzuverlässig ist“, und möchte sicherstellen, dass das restliche System gesund bleibt, wenn dieser ausfällt.
Heute verwendet der Dienst einen einzelnen gemeinsamen Thread-Pool mit 200 Threads & einen einzelnen gemeinsamen HTTP-Verbindungspool. Alle vier Abhängigkeiten konkurrieren um diese Ressourcen. Es gibt keine Circuit Breaker.
Eine Fehlermodus-Prüfung gestalten
Synthese
Du hast gelernt, SPOFs (Single Points of Failure) durch Inspektion zu erkennen, Kaskadenschadensmuster zu identifizieren, bei der Lektüre eines Postmortems zwischen Auslöser und latentem Defekt zu unterscheiden, schuldzuweisungsfreie Maßnahmenpunkte über Prävention / Erkennung / Wiederherstellung zu formulieren & den Schadensumfang (Blast Radius) mit Bulkheads + Circuit Breakern + sanfter Degradierung zu begrenzen.
Wende alle fünf Punkte an.
Dein Team bringt einen neuen Dienst search.example.com auf den Markt, der von drei nachgelagerten Diensten abhängt: einem primären Suchindex (index.example.com), einem Analytik-Dienst (analytics.example.com) & einem Empfehlungsdienst (recs.example.com). Das Team möchte, dass du vor dem Launch eine „Fehlermodus-Prüfung“ (failure-mode review) leitest.
Wie dieser Kurs weitergeht
Wie dieser Kurs weitergeht
Du kannst nun einen SPOF erkennen, eine Kaskade identifizieren, einen Postmortem-Bericht produktiv lesen, schuldzuweisungsfreie Maßnahmen formulieren und den Ausbreitungsradius durch Design begrenzen.
Die letzte Lektion dieses Kurses (cs_distsys_observability_and_capacity) lehrt, was zu messen ist, damit man ein Problem erkennt, bevor es die Nutzer tun. Health Checks, Versions-Endpunkte, die vier Goldenen Signale auf Proxy-Ebene und wie Entscheidungen zur Spitzenlastkapazität auf beobachtete Daten zurückwirken.
Begleitende Lektion: geometry_of_failure_modes_and_blast_radius leitet die Betweenness-Zentralität (welcher Graphen-Knoten die Engstelle ist) und den Minimal-Schnitt (die Grenze des Ausbreitungsradius) ab.
Gut gemacht. Weiter geht's.