Die Annahme testen, die niemand testet
AWS FIS kann eine Zone ausfallen lassen und Control-Plane-Verlust nur annähern. Wie man den Game Day fährt, was man misst, und eine ehrliche Bilanz.

Beide tragenden Annahmen dieser Serie sind heute testbar, mit einem Managed Service, an einem Nachmittag. Der AWS Fault Injection Service liefert ein Szenario namens "AZ Availability: Power Interruption", das jede getaggte Instance in einer Availability Zone stoppt, den Auto Scaling Groups, die sie zu ersetzen versuchen, InsufficientInstanceCapacity-Fehler zuführt, EBS-Volume-IO pausiert und Subnetz-Traffic kappt, dann die Fehler aufhebt und neu startet, was es gestoppt hat. Die Control-Plane-Annahme aus Teil 3 hat keinen fertigen Fehler von der Stange, aber eine ehrliche Annäherung daran passt in eine JSON-Datei und ein IAM-Deny. Fast niemand fährt eines von beiden, und der Grund ist nicht das Tooling; der Grund ist, dass ein Test fehlschlagen kann und eine Annahme nicht.
Dies ist Teil 4 von Nothing Fails Alone, der letzte. Teil 1 nahm den us-east-1 Incident im Mai und zeigte die Zonengrenze standhalten, während ein Quorum darin starb. Teil 2 nahm das me-central-1 Ereignis im März und zeigte eine Ursache, die zwei Zonen achtzehn Stunden auseinander erreichte, an jeder Isolationsgarantie vorbei. Teil 3 nahm drei Control-Plane-Incidents über zwei Provider hinweg und zeigte Recovery-Pläne, die mit der Schicht sterben, auf der sie laufen. Das Rückgrat der Serie, in zwei Sätzen: Die Verfügbarkeit, die du kaufst, endet an der Grenze dessen, was der Provider versprochen hat, und alles oberhalb dieser Linie ist Architektur, die dir gehört. Was dir gehört und nie geübt wird, ist keine Architektur, es ist eine Annahme mit deinem Namen darauf. Dieser Teil übt sie, dann bewertet er die Serie.
Die AZ-Übung gibt es von der Stange
Die AZ Availability: Power Interruption der FIS-Szenariobibliothek ist ein veröffentlichtes Experiment-Template, das die dokumentierten Symptome des Verlusts einer Zone hervorruft, und ihr veröffentlichtes JSON trägt acht Actions über sieben Fault-Typen (das Stoppen von Instances erscheint zweimal, einmal für eigenständige Instances und einmal für die der Auto Scaling Groups selbst). Getaggte laufende Instances in der Zielzone werden gestoppt und nach der konfigurierten Dauer neu gestartet. Launch-Requests von getaggten Auto Scaling Groups bekommen InsufficientInstanceCapacity für die Dauer, und ebenso die Kapazitätsaufrufe aller IAM-Rollen, die du benennst, denn laut der Szenarioseite "EC2 API calls made by the Auto Scaling control plane to recover lost capacity in the AZ will fail" während einer echten Stromunterbrechung. EBS-Volume-IO wird pausiert. Subnetz-Traffic wird verweigert, indem eine geklonte Network ACL voller Deny-Regeln eingesetzt wird. RDS-Cluster mit einem Writer in der Zone machen ein Failover, und bei ElastiCache-Replikationsgruppen wird der Strom ihrer Zone unterbrochen. Das ist das meiste von dem, was der Mai-Incident use1-az4 antat: Der Ausfall, der Coinbase eine Nacht lang lahmlegte, ist jetzt ein Bibliothekselement.
Sei präzise darin, was es nicht ist. Die Instances werden durch die Vordertür gestoppt, die StopInstances-API: ein geordnetes Herunterfahren, keine Stromunterbrechung, sodass nichts an Crash-Konsistenz getestet wird. Fargate-Tasks sind nicht abgedeckt. RDS-Multi-AZ-Cluster mit zwei lesbaren Standbys werden nicht unterstützt. Eine Drift-Anmerkung: Die Szenarioseite beschreibt eine Recovery-Action, aws:arc:start-zonal-autoshift, die ihr eigener JSON-Snapshot nicht enthält; die Bibliothek der Console trägt die neueste Revision, und das Lab folgt dem veröffentlichten JSON.
Das Lab ist 04-game-day im Serien-Repo, und es bricht die read-only Regel der Serie zum zweiten und letzten Mal: --inject startet echte Experimente gegen den Account, den deine Credentials auch immer erreichen, also gehört es in eine Sandbox, gerichtet auf den Lab-Stack aus Teil 3, und nirgends in die Nähe der Produktion.
Namen, IDs und eine leer ausgelieferte Stop-Condition
Teil 2 machte viel Aufhebens um Zonennamen gegenüber Zonen-IDs, und der Game Day ist, wo sich das Aufhebens auszahlt. Incident-Reports sprechen IDs: Der Mai-Report sagte use1-az4, die März-Updates sagten mec1-az2 und dann mec1-az3. Dein Account spricht Namen, Aliase, die AWS pro Account randomisiert. Und das Tool verlangt größtenteils den Namen. Die Instance- und Subnetz-Targets filtern auf Placement.AvailabilityZone, was zu deinem account-lokalen Namen passt, und das veröffentlichte Szenario füllt auch die EBS-, RDS- und ElastiCache-Target-Parameter mit dem Namen. Genau zwei Actions akzeptieren die physische ID: das Insufficient-Capacity-Paar, aws:ec2:api-insufficient-instance-capacity-error und aws:ec2:asg-insufficient-instance-capacity-error, deren availabilityZoneIdentifiers-Parameter dokumentiert ist, sowohl Zonen-IDs als auch Namen zu nehmen. Selbst dort füllt das veröffentlichte Szenario das Feld mit einem Namen, us-east-1a.
Ein Game Day beginnt daher mit dem Übersetzungsproblem, für das Teil 2 ein Lab lieferte. Der Runner löst es einmal, ganz oben: Das einzige Zonen-Flag, das run.py akzeptiert, ist --az-id, die physische Zone, das, was ein Incident-Report benennt. Er löst den account-lokalen Namen via DescribeAvailabilityZones auf, druckt beide und füllt jeden Platzhalter mit der Form, die sein Konsument verlangt. Du probst den Ausfall unter dem Namen, den ihm ein Incident-Report geben würde, und die Probe konfiguriert sich selbst in dem Dialekt, den dein Account zufällig spricht.
Das zweite, was der Runner sich weigert zu erben, ist leiser. Der Stop-Condition-Block des veröffentlichten Szenarios sieht so aus, wortwörtlich:
"stopConditions": [
{
"source": "aws:cloudwatch:alarm",
"value": ""
}
]Die Form eines Schutzmechanismus, in dem nichts steht, und der Limitations-Abschnitt der Seite sagt, dass die Stop-Conditions von dir hinzuzufügen sind. Ein vertretbarer Default, da AWS deinen Steady State nicht kennen kann; auch ein heikler, weil das JSON sauber einfügt und ein leerer String nicht wie ein fehlendes Sicherheitssystem aussieht. Die Position des Labs: run.py beendet sich, bevor es irgendetwas erstellt, wenn --alarm-arn nicht angegeben ist, denn ein Experiment ohne eine sinnvolle Stop-Condition ist kein Test, es ist eine Wette. Steady State ist eine Zahl, auf die du dich vor dem Fehler festlegst, sonst kann der Game Day nicht fehlschlagen, und ein Game Day, der nicht fehlschlagen kann, kann dir nichts sagen.
Wie ein Pass aussieht, gemessen
Fahre das Experiment gegen den Zwei-Pfad-Stack aus Teil 3, indem du eine seiner beiden Zonen ausfallen lässt:
python3 run.py experiments/az-power-interruption.json \
--az-id euw1-az1 \
--fis-role-arn arn:aws:iam::111111111111:role/fis-game-day \
--alarm-arn arn:aws:cloudwatch:eu-west-1:111111111111:alarm:game-day-abort \
--target-group-arn ... \
--injectOhne --inject ist derselbe Befehl ein Dry Run: Er druckt das aufgelöste Template und startet nichts. Mit ihm erstellt und startet der Runner das Experiment, pollt dann bis zu einem Endzustand, protokolliert den Status jeder Action und tastet die gesunde Anzahl der Target Group als Messung ab. Diese Timeline ist das Ergebnis; was folgt, ist die Form eines Passes, kein Ergebnis, das ich erzielt habe, denn ich habe keinen produktionsförmigen Account, um ihn darin zu verdienen. Pfad B, der statisch stabile, fällt nie unter seine Anforderung von zwei gesunden Targets nach dem Ausfall, da die überlebende Zone die Last bereits trägt. Pfad A fällt auf die Hälfte und bleibt dort für die gesamte Fehlerdauer, weil dasselbe Experiment, das seine Instances stoppte, seinen Ersatz-Launches InsufficientInstanceCapacity zuführt. Das Mess-Skript aus Teil 3 maß die Wiederherstellung von Pfad A gegen eine kooperative Control Plane; der Game Day zeigt denselben Pfad gegen eine unkooperative, und der Unterschied zwischen diesen beiden Zahlen ist die Größe der Annahme, die dein DR-Plan machte.
Vier Zahlen sind es wert, festgehalten zu werden. Zeit bis zur wiederhergestellten Kapazität, aus den Healthy-Target-Samples. Die Anzahl der Runbook-Schritte, die einen Control-Plane-Aufruf brauchten, der fehlgeschlagen wäre, gezählt, indem du dein Runbook während des Fehlerfensters durchgehst. Die Lücke zwischen der kooperativen Zahl aus Teil 3 und der unkooperativen des Game Day. Und das Schicksal jedes Findings aus dem Detektor von Teil 1, denn jede Zeile, die er druckt, ist eine Vorhersage, die der Game Day klären kann: Die Single-AZ Auto Scaling Group strandet ihre Kapazität tatsächlich hinter einem Kapazitätsfehler; das Subnetz in der gesunden Zone, dessen Default-Route in die ausgefallene Zone übergeht, verliert tatsächlich seinen Egress. Ein Detektor-Finding ist eine Hypothese. Ein Game Day ist das Experiment.
Die Control-Plane-Übung, die FIS dir nicht geben kann
Die Annahme aus Teil 3 ist schwerer, und hier kommt der ehrliche Satz zuerst: FIS hat keine Control-Plane-Loss-Action. Die vollständige Actions-Referenz enthält die Phrase nie, und nichts darin nimmt eine Control Plane weg. Am nächsten kommen Fehlerinjektionen, die auf von dir benannte Aufrufer eingegrenzt sind: das Insufficient-Capacity-Paar von oben und ein Trio generischer API-Fehler-Actions (internal, throttle, unavailable), die Fehler in Requests injizieren, die von benannten IAM-Rollen gestellt werden, nur für die Namespaces EC2 und Kinesis. Die Plane bleibt oben; ausgewählten Aufrufern wird nein gesagt. Nichts, was du mieten kannst, reproduziert die Console, die 504s ausliefert, während STS dreimal getrennt fehlschlägt.
Also ist das zweite Template des Labs eine Annäherung und wird als solche gekennzeichnet, in einem Top-Level-approximation-Schlüssel innerhalb der Datei selbst, sodass das Label nicht von dem abdriften kann, was es kennzeichnet. Die FIS-Hälfte injiziert InsufficientInstanceCapacity auf die Launches der Workload-ASGs und auf die Kapazitätsaufrufe der Deploy-Rolle, RunInstances, CreateFleet, StartInstances, CreateCapacityReservation, in der ausgefallenen Zone für zwanzig Minuten. Die andere Hälfte ist überhaupt kein FIS: ein manuelles, eingegrenztes, zeitlich begrenztes IAM-Deny auf ec2:RunInstances, autoscaling:* und cloudformation:*, was den Runbook-Schritt "einfach den Notfall-Stack pushen" in den Fehler verwandelt, der er im Juni 2023 gewesen wäre. run.py druckt die Apply- und Remove-Befehle und führt sie nie aus, die Zeitbegrenzung wird entschieden, bevor die Policy angehängt wird, und das Deny geht nie auf die Rolle, die du brauchen wirst, um es zu entfernen. Diese letzte Regel ist keine Pedanterie; sie ist der Unterschied zwischen einer Übung und einem Incident.
Was die Annäherung nicht reproduziert, aus der eigenen Liste der Datei: Echte Control-Plane-Degradation ist Latenz und Brownout, selten ein sauberer Fehler; Services, die die Datei nie benennt, ELB, Route 53, IAM selbst, die Console, funktionieren hier weiter und tun es in einem echten Ereignis vielleicht nicht; Reads gelingen weiter, sodass deine Dashboards gesünder aussehen, als sie es wären; Nicht-Kapazitäts-Mutationen wie TerminateInstances und Änderungen an der Load-Balancer-Konfiguration gelingen weiter, sodass dies die Kapazitätserstellung verweigert, nicht die Control Plane; und es ist die Kapazität einer Zone, wo ein echtes Ereignis regional sein kann. Miss bei dieser Übung eine zusätzliche Sache: Time to Detect. Wie lange brauchte ein Mensch, um zu bemerken, dass der Fix nicht funktionierte? Wenn dir jemand sagt, er habe Control-Plane-Verlust simuliert, frage, welche dieser fünf Lücken er abgedeckt hat. Meine deckt auch keine davon ab; sie deckt die engere Behauptung ab, dass deine Architektur die verweigerten Aufrufe in der ersten Stunde nicht braucht.
Die Serie bewerten
Vier Incidents, vier Labs, ein Rückgrat. Was die Serie belegte, an ihrem eigenen Maßstab gemessen: Eine Behauptung zählt nur, wenn sie auf den veröffentlichten Worten des Betreibers ruht. Erstens: Zonenisolation ist real gegen die Fehler, für die sie ausgelegt ist: Das thermische Ereignis im Mai blieb innerhalb von use1-az4, und die Architektur, die in dieser Nacht ausfiel, fiel nach dem Eingeständnis ihres eigenen Postmortems aus, nicht dem von AWS. Zweitens: Zonenausfälle sind keine unabhängigen Ereignisse: Eine Ursache erreichte mec1-az2 und mec1-az3 achtzehn Stunden auseinander, und zwei verlorene Zonen trieben regionale Services über ihre angegebene Ein-Zonen-Toleranz hinaus. Drittens: Die Control Plane fällt aus, während die Data Plane weiterläuft, bei mehr als einem Provider, und Recovery-Pläne, die Control-Plane-Operationen sind, fallen mit ihr aus: 2023 und Oktober 2025 nach AWS' eigenen Post-Event-Summaries, Februar 2026 nach der von Azure.
Jetzt die andere Spalte, denn eine Retrospektive, die nur Siege zählt, ist Marketing. Was Annahme bleibt: alles Quantitative über dich. Ich habe diese Labs nie gegen eine Produktionsflotte laufen lassen, also ist jede Zahl in der Serie die Form einer Ausgabe, ehrlich gekennzeichnet, aber trotzdem eine Form. Die Thundering-Herd-Kosten des Massen-Failovers, AWS' "longer than usual provisioning times", erscheinen in zwei Incidents und haben nie eine Zahl bekommen, von AWS oder von mir. Die Static-Stability-Prämie aus Teil 3 war Arithmetik, keine Rechnung von einer echten Flotte. Und ob dein Break-Glass-Pfad funktioniert, ist von hier aus nicht erkennbar.
Was ich anders machen würde: den Game Day zuerst fahren, nicht zuletzt. Die Serie ordnete ihre Labs pädagogisch, detektieren, übersetzen, messen, injizieren, und diese Reihenfolge ist operativ verkehrt herum: Ein Injektionslauf produziert die Finding-Liste, die der Detektor nur vorhersagen kann. Und ich hätte sekundären Quellen früher misstraut. Die Regel, nur von der eigenen Seite des Betreibers zitieren, verdiente sich ihren Platz bis zum Schluss: Sie fing falsche Dauern ab, einen Zeitstempel, der um einen halben Tag daneben lag, und Behauptungen, deren scheinbare Quellen sie, wenn man sie abrief, nicht enthielten. Eine Serie über ungetestete Annahmen hätte fast mehrere ausgeliefert, was ungefähr so eine saubere Demonstration ihrer eigenen These ist, wie ich sie hätte arrangieren können.
Game Days schlagen auch fehl
Das Instrument verdient dieselbe Skepsis wie das, was es misst, also enden wir mit den drei Arten, wie ein Game Day dich belügt.
- Der Test, der nur beweist, dass der Test lief. FIS löst Targets per Tag auf, und ein Tag-Mismatch lässt das Experiment nicht fehlschlagen: Das AZ-Szenario überspringt Actions, deren Targets auf nichts auflösen, und das Lab behält dieses Verhalten (
emptyTargetResolutionMode: skip) bei, damit auch Teilstacks laufen können. Der Preis: Ein Experiment kanncompletederreichen, ohne irgendetwas gestoppt zu haben. Grünes Ergebnis, null injizierte Fehler. Lies die Tabelle pro Action, nicht die Statuszeile; das Control-Plane-Template setzt stattdessenfail, denn dort macht ein leeres Target den Lauf sinnlos. Die Exit-Codes stimmen zu:completedbeendet mit 0, einstopped-Experiment beendet mit 1, obwohl Stoppen bedeutet, dass dein Alarm funktioniert hat. Dass der Schutzmechanismus auslöst, ist der Schutzmechanismus, der funktioniert, und trotzdem kein Pass. - Die Stop-Condition, die zu früh auslöst. Binde den Alarm an eine interne Metrik mit einem engen Schwellenwert, und das Experiment stoppt nach zwei Minuten, vor dem interessanten Ausfall. Du lernst nur, dass der Alarm auslöst, und die Versuchung danach ist, ihn zu lockern oder zu entfernen, und so landest du aus freien Stücken wieder beim leeren String des Szenarios. Der Fix ist nicht weniger Schutzmechanismus; es ist, den Alarm an die nutzerseitige Steady-State-Metrik zu binden, bei dem Schwellenwert, bei dem du einen echten Incident gestoppt haben wolltest.
- Der Account, der nicht Produktion ist. Ein Sandbox-Pass beweist den Mechanismus, nicht das Ergebnis. Kein Produktions-Traffic, keine Datengravitation, keine lauten Nachbarn, kein um 3 Uhr morgens geweckter Operator. Füge die eigene Weichheit der Injektion hinzu, geordnete Shutdowns, saubere Fehler statt Brownouts, und die ehrliche Beweiskette liest sich so: Der Detektor sagt vorher, der Sandbox-Game-Day testet den Mechanismus der Vorhersage, und die Produktion erbt eine gekennzeichnete Extrapolation. Das ist viel besser als nichts und viel weniger als ein Beweis, und so zu tun, als wäre es anders, ist, wie "wir haben Chaos Engineering gemacht" zu einer weiteren ungetesteten Annahme wird.
Hier endet die Serie. Nichts fällt allein aus: Jeder Teil fand den Ausfall verstrickt mit etwas daneben, ein Quorum mit einem Gebäude, eine Zone mit einem Krieg, ein Fix mit einer Control Plane. Nichts besteht allein. Jeder Pass ist mit der Umgebung verstrickt, die ihn hervorbrachte, und das Label, das dies sagt, ist die tragendste Zeile im Report.
Lies das als Nächstes
- Teil 3 dieser Serie: Der Fix teilt das Schicksal des Ausfalls, die Control-Plane-Incidents, die das zweite Experiment dieses Teils annähert.
- Teil 1 dieser Serie: AZ-Isolation hielt. Deine Architektur nicht., wo der Detektor die Vorhersagen erzeugte, die der Game Day dieses Teils zu klären existiert. Der Kreis schließt sich.
- Evals Before Agents: Du kannst nicht ausliefern, was du nicht bewerten kannst auf ercan.ai: dieselbe Disziplin in einer anderen Domäne, definiere die Pass-Bedingung und den Score, bevor das System läuft, denn "es fühlte sich gut an" ist auch dort keine Messung.
- Das begleitende Repo: nothing-fails-alone, alle vier Labs; dieses injiziert echte Fehler und sagt das ganz oben.
Für Consulting zu AWS, Cloud-Architektur, Resilience Reviews und Plattform-Arbeit, starte auf ercanermis.com.
Referenzen
Weiteres von Ercan
Zwei weitere Seiten, gleicher Autor, anderes Terrain.
KI, LLMs, Agents, angewandte ML.
Praxisnotizen zu KI-Workloads. Bedrock-Kostenanalyse, Agent-Patterns, Vektorspeicher-Tradeoffs, Failure-Modes in Produktion.
Besuchen ercan.ai →Die Drehscheibe. Über mich, Beratung, Kontakt.
Persönliche Drehscheibe für beide Schreibspuren. Wer ich bin, wie die Beratung funktioniert, wie Sie mich erreichen.
Besuchen ercanermis.com →