Testare l'assunzione che nessuno testa
AWS FIS può far cadere una zona e solo approssimare la perdita del control plane. Come eseguire il game day, cosa misurare e un bilancio onesto della serie.

Entrambe le assunzioni portanti di questa serie sono testabili oggi, con un servizio gestito, in un pomeriggio. AWS Fault Injection Service offre uno scenario chiamato "AZ Availability: Power Interruption" che fermerà ogni istanza taggata in una availability zone, darà in pasto errori InsufficientInstanceCapacity agli Auto Scaling group che cercano di rimpiazzarle, metterà in pausa l'IO dei volumi EBS e taglierà il traffico della subnet, poi solleverà i guasti e riavvierà ciò che ha fermato. L'assunzione sul control plane della Parte 3 non ha un guasto pronto all'uso, ma una sua approssimazione onesta sta in un file JSON e in un IAM deny. Quasi nessuno esegue né l'uno né l'altro, e la ragione non è il tooling; la ragione è che un test può fallire e un'assunzione no.
Questa è la Parte 4 di Nothing Fails Alone, l'ultima. La Parte 1 ha preso l'incidente di us-east-1 di maggio e ha mostrato il confine di zona reggere mentre un quorum moriva al suo interno. La Parte 2 ha preso l'evento di me-central-1 di marzo e ha mostrato una causa raggiungere due zone a diciotto ore di distanza, oltre ogni garanzia di isolamento. La Parte 3 ha preso tre incidenti di control plane su due provider e ha mostrato piani di ripristino morire con il layer su cui girano. La spina dorsale della serie, in due frasi: la disponibilità che compri finisce al confine di ciò che il provider ha promesso, e tutto ciò che sta sopra quella linea è architettura che possiedi. Ciò che possiedi e non eserciti mai non è architettura, è un'assunzione con il tuo nome sopra. Questa parte la esercita, poi assegna un punteggio alla serie.
Il drill di AZ arriva dallo scaffale
La AZ Availability: Power Interruption della libreria di scenari FIS è un experiment template pubblicato che induce i sintomi documentati della perdita di una zona, e il suo JSON pubblicato porta otto azioni su sette tipi di guasto (l'arresto delle istanze compare due volte, una per le istanze standalone e una per gli Auto Scaling group stessi). Le istanze in esecuzione taggate nella zona target vengono fermate e riavviate dopo la durata configurata. Le richieste di lancio dagli Auto Scaling group taggati ottengono InsufficientInstanceCapacity per la durata, e lo stesso vale per le chiamate di capacità di qualsiasi ruolo IAM che nomini, perché, secondo la pagina dello scenario, "EC2 API calls made by the Auto Scaling control plane to recover lost capacity in the AZ will fail" durante una vera interruzione di alimentazione. L'IO dei volumi EBS viene messo in pausa. Il traffico della subnet viene negato scambiando una network ACL clonata piena di regole di deny. I cluster RDS con un writer nella zona vengono fatti failover, e ai replication group ElastiCache viene interrotta l'alimentazione della loro zona. Questo è gran parte di ciò che l'incidente di maggio ha fatto a use1-az4: il guasto che ha messo giù Coinbase per una notte è ora un elemento di libreria.
Sii preciso su cosa non è. Le istanze vengono fermate attraverso la porta principale, l'API StopInstances: uno spegnimento ordinato, non un taglio di corrente, quindi nulla della crash consistency viene testato. I task Fargate non sono coperti. I cluster RDS Multi-AZ con due standby leggibili non sono supportati. Una nota di drift: la pagina dello scenario descrive un'azione di ripristino, aws:arc:start-zonal-autoshift, che il suo stesso snapshot JSON non contiene; la libreria della console porta la revisione più recente, e il lab segue il JSON pubblicato.
Il lab è 04-game-day nel repo della serie, e infrange la regola read-only della serie per la seconda e ultima volta: --inject avvia esperimenti reali contro qualunque account le tue credenziali raggiungano, quindi appartiene a una sandbox, puntato allo stack del lab della Parte 3, e lontano dalla produzione.
Nomi, ID e una stop condition spedita vuota
La Parte 2 ha fatto una questione dei nomi di zona rispetto agli ID di zona, e il game day è dove la questione ripaga. Gli incident report parlano ID: il report di maggio diceva use1-az4, gli aggiornamenti di marzo dicevano mec1-az2 e poi mec1-az3. Il tuo account parla nomi, alias che AWS randomizza per account. E il tool, per la maggior parte, pretende il nome. I target di istanza e subnet filtrano su Placement.AvailabilityZone, che corrisponde al nome locale del tuo account, e lo scenario pubblicato riempie i parametri di target EBS, RDS ed ElastiCache anch'essi con il nome. Esattamente due azioni accettano l'ID fisico: la coppia di capacità insufficiente, aws:ec2:api-insufficient-instance-capacity-error e aws:ec2:asg-insufficient-instance-capacity-error, il cui parametro availabilityZoneIdentifiers è documentato per accettare sia ID di zona sia nomi. Anche lì, lo scenario pubblicato riempie il campo con un nome, us-east-1a.
Un game day parte quindi con il problema di traduzione per cui la Parte 2 ha spedito un lab. Il runner lo risolve una volta, in cima: l'unico flag di zona che run.py accetta è --az-id, la zona fisica, la cosa che un incident report nomina. Risolve il nome locale dell'account tramite DescribeAvailabilityZones, stampa entrambi e riempie ogni placeholder con la forma che il suo consumatore richiede. Provi il guasto sotto il nome che un incident report gli darebbe, e la prova si configura da sola nel dialetto che il tuo account si trova a parlare.
La seconda cosa che il runner rifiuta di ereditare è più silenziosa. Il blocco stop-condition dello scenario pubblicato ha questo aspetto, alla lettera:
"stopConditions": [
{
"source": "aws:cloudwatch:alarm",
"value": ""
}
]La forma di un guardrail senza nulla dentro, e la sezione limitations della pagina dice che le stop condition sono tue da aggiungere. Un default difendibile, dato che AWS non può conoscere il tuo steady state; anche uno carico, perché il JSON si incolla in modo pulito e una stringa vuota non sembra un sistema di sicurezza mancante. La posizione del lab: run.py esce prima di creare qualsiasi cosa se --alarm-arn non è fornito, perché un esperimento senza una stop condition significativa non è un test, è una scommessa. Lo steady state è un numero a cui ti impegni prima del guasto, o il game day non può fallire, e un game day che non può fallire non può dirti niente.
Che aspetto ha un pass, misurato
Esegui l'esperimento contro lo stack a due percorsi della Parte 3, facendo cadere una delle sue due zone:
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 ... \
--injectSenza --inject lo stesso comando è un dry run: stampa il template risolto e non avvia nulla. Con esso, il runner crea e avvia l'esperimento, poi fa polling fino a uno stato terminale, loggando lo status di ogni azione e campionando il conteggio in salute del target group come misurazione. Quella timeline è il deliverable; ciò che segue è la forma di un pass, non un risultato che ho ottenuto, perché non ho un account a forma di produzione in cui guadagnarlo. Il percorso B, quello staticamente stabile, non scende mai sotto il suo requisito post-guasto di due target in salute, dato che la zona superstite regge già il carico. Il percorso A scende alla metà e ci resta per l'intera durata del guasto, perché lo stesso esperimento che ha fermato le sue istanze sta dando in pasto ai suoi lanci di rimpiazzo InsufficientInstanceCapacity. Lo script di misurazione della Parte 3 ha cronometrato il ripristino del percorso A contro un control plane cooperativo; il game day mostra lo stesso percorso contro uno non cooperativo, e la differenza tra quei due numeri è la dimensione dell'assunzione che il tuo piano di DR stava facendo.
Quattro numeri vale la pena registrare. Il tempo fino alla capacità ripristinata, dai campioni dei target in salute. Il conteggio dei passi del runbook che avevano bisogno di una chiamata al control plane che sarebbe fallita, contati percorrendo il tuo runbook durante la finestra di guasto. Il divario tra il numero cooperativo della Parte 3 e quello non cooperativo del game day. E la sorte di ogni finding del detector della Parte 1, dato che ogni riga che stampa è una previsione che il game day può risolvere: l'Auto Scaling group single-AZ lascia davvero la sua capacità intrappolata dietro un errore di capacità; la subnet della zona in salute la cui rotta di default attraversa nella zona fallita perde davvero l'egress. Un finding del detector è un'ipotesi. Un game day è l'esperimento.
Il drill del control plane che FIS non può darti
L'assunzione della Parte 3 è più dura, e qui la frase onesta viene per prima: FIS non ha un'azione di perdita del control plane. La reference completa delle azioni non contiene mai la frase, e nulla al suo interno porta via un control plane. I più vicini sono iniezioni di errore con scope sui caller che nomini: la coppia di capacità insufficiente sopra, e un trio di azioni di errore API generico (internal, throttle, unavailable) che iniettano guasti nelle richieste fatte da ruoli IAM nominati, solo per i namespace EC2 e Kinesis. Il plane resta su; ai caller scelti viene detto di no. Nulla che tu possa noleggiare riproduce la console che serve 504 mentre STS fallisce tre volte separate.
Quindi il secondo template del lab è un'approssimazione ed è etichettato come tale, in una chiave approximation di primo livello dentro il file stesso, così che l'etichetta non possa allontanarsi dalla cosa che etichetta. La metà FIS inietta InsufficientInstanceCapacity sui lanci degli ASG del workload e sulle chiamate di capacità del ruolo di deploy, RunInstances, CreateFleet, StartInstances, CreateCapacityReservation, nella zona fallita per venti minuti. L'altra metà non è FIS affatto: un IAM deny manuale, con scope e limitato nel tempo, su ec2:RunInstances, autoscaling:* e cloudformation:*, che è ciò che trasforma il passo "basta pushare lo stack d'emergenza" del runbook nel guasto che sarebbe stato a giugno 2023. run.py stampa i comandi di apply e remove e non li esegue mai, il time box è deciso prima che la policy venga attaccata, e il deny non va mai sul ruolo che ti servirà per rimuoverlo. Quest'ultima regola non è pedanteria; è la differenza tra un drill e un incidente.
Cosa l'approssimazione non riproduce, dalla lista del file stesso: la vera degradazione del control plane è latenza e brownout, raramente un errore pulito; servizi che il file non nomina mai, ELB, Route 53, IAM stesso, la console, continuano a funzionare qui e potrebbero non farlo in un evento reale; le letture continuano a riuscire, quindi le tue dashboard sembrano più in salute di quanto sarebbero; le mutazioni non di capacità come TerminateInstances e le modifiche di configurazione del load balancer continuano a riuscire, quindi questo nega la creazione di capacità, non il control plane; ed è la capacità di una zona, dove un evento reale può essere regionale. Misura una cosa in più su questo drill: il tempo di detection. Quanto ci è voluto a un umano per notare che la correzione non stava funzionando? Se qualcuno ti dice di aver simulato la perdita del control plane, chiedi quale di quei cinque divari ha coperto. Il mio non ne copre nessuno neanche lui; copre l'affermazione più stretta che la tua architettura non ha bisogno delle chiamate negate nella prima ora.
Assegnare il punteggio alla serie
Quattro incidenti, quattro lab, una spina dorsale. Ciò che la serie ha stabilito, tenuto al suo stesso standard: un'affermazione conta solo se poggia sulle parole pubblicate dell'operatore. Primo, l'isolamento di zona è reale contro i guasti per cui è progettato: l'evento termico di maggio è rimasto dentro use1-az4, e l'architettura che quella notte è fallita è fallita per ammissione del suo stesso postmortem, non di AWS. Secondo, i guasti di zona non sono eventi indipendenti: una causa ha raggiunto mec1-az2 e mec1-az3 a diciotto ore di distanza, e due zone perse hanno portato i servizi regionali oltre la loro tolleranza dichiarata di una zona. Terzo, il control plane fallisce mentre il data plane continua a girare, su più di un provider, e i piani di ripristino che sono operazioni di control plane falliscono con esso: 2023 e ottobre 2025 sui post-event summary di AWS stessa, febbraio 2026 su quello di Azure.
Ora l'altra colonna, perché una retrospettiva che conta solo le vittorie è marketing. Ciò che resta assunzione: tutto ciò che è quantitativo su di te. Non ho mai eseguito questi lab contro una flotta di produzione, quindi ogni numero della serie è la forma di un output, etichettato onestamente ma comunque una forma. Il costo da thundering herd del failover di massa, i "longer than usual provisioning times" di AWS, compare in due incidenti e non ha mai avuto un numero, né da AWS né da me. Il premio della static stability della Parte 3 era aritmetica, non una fattura da una flotta reale. E se il tuo percorso break-glass funzioni è inconoscibile da qui.
Cosa farei diversamente: eseguire il game day per primo, non per ultimo. La serie ha ordinato i suoi lab in modo pedagogico, rileva, traduci, misura, inietta, e quell'ordine è al contrario operativamente: una singola esecuzione di iniezione produce la lista di finding che il detector può solo prevedere. E avrei diffidato delle fonti secondarie prima. La regola, cita solo dalla pagina dell'operatore stesso, ha continuato a guadagnarsi il posto fino alla fine: ha colto durate sbagliate, un timestamp fuori di mezza giornata, e affermazioni le cui apparenti fonti, una volta recuperate, non le contenevano. Una serie sulle assunzioni non testate ne ha quasi spedite diverse, che è circa la dimostrazione più pulita della sua stessa tesi che avrei potuto organizzare.
Anche i game day falliscono
Lo strumento merita lo stesso scetticismo della cosa che misura, quindi chiudi sui tre modi in cui un game day ti mente.
- Il test che dimostra solo che il test è girato. FIS risolve i target per tag, e un tag mismatch non fa fallire l'esperimento: lo scenario AZ salta le azioni i cui target risolvono a nulla, e il lab mantiene quel comportamento (
emptyTargetResolutionMode: skip) così che stack parziali possano girare. Il prezzo: un esperimento può raggiungerecompletednon avendo fermato nulla. Risultato verde, zero guasti iniettati. Leggi la tabella per azione, non la riga di status; il template del control plane impostafailinvece, dato che lì un target vuoto rende l'esecuzione inutile. Gli exit code concordano:completedesce con 0, un esperimentostoppedesce con 1 anche se essere fermato significa che il tuo allarme ha funzionato. Il guardrail che scatta è il guardrail che funziona, e comunque non è un pass. - La stop condition che scatta troppo presto. Lega l'allarme a una metrica interna con una soglia stretta e l'esperimento si ferma dopo due minuti, prima del guasto interessante. Impari solo che l'allarme scatta, e la tentazione dopo è di allentarlo o rimuoverlo, che è come arrivi di nuovo alla stringa vuota dello scenario per scelta. La correzione non è meno guardrail; è legare l'allarme alla metrica di steady state rivolta all'utente, alla soglia a cui vorresti che un incidente reale venisse fermato.
- L'account che non è produzione. Un pass in sandbox dimostra il meccanismo, non l'esito. Nessun traffico di produzione, nessuna data gravity, nessun vicino rumoroso, nessun operatore svegliato alle 3 del mattino. Aggiungi la morbidezza propria dell'iniezione, spegnimenti ordinati, errori puliti invece di brownout, e la catena onesta di evidenze recita: il detector prevede, il game day in sandbox testa il meccanismo della previsione, e la produzione eredita un'estrapolazione etichettata. È molto meglio di niente e molto meno di una prova, e far finta del contrario è come "abbiamo fatto chaos engineering" diventa un'altra assunzione non testata.
È qui che la serie finisce. Nothing fails alone: ogni parte ha trovato il guasto intrecciato con qualcosa accanto a sé, un quorum con un edificio, una zona con una guerra, una correzione con un control plane. Neanche nulla passa da solo. Ogni pass è intrecciato con l'ambiente che l'ha prodotto, e l'etichetta che lo dice è la riga più portante del report.
Leggi questo dopo
- Parte 3 di questa serie: La correzione condivide la sorte con l'interruzione, gli incidenti di control plane che il secondo esperimento di questa parte approssima.
- Parte 1 di questa serie: L'isolamento delle AZ ha retto. La tua architettura no., dove il detector ha prodotto le previsioni che il game day di questa parte esiste per risolvere. Cerchio chiuso.
- Evals Before Agents: You Can't Ship What You Can't Score su ercan.ai: la stessa disciplina in un altro dominio, definisci la condizione di pass e il punteggio prima che il sistema giri, perché "sembrava a posto" non è una misurazione neanche lì.
- Il repo companion: nothing-fails-alone, tutti e quattro i lab; questo inietta guasti reali e lo dichiara in cima.
Per consulenza su AWS, architettura cloud, resilience review e lavoro di piattaforma, parti da ercanermis.com.
Riferimenti
Altro da Ercan
Altri due siti, stesso autore, terreno diverso.
IA, LLMs, agenti, ML applicato.
Note sul campo su workload IA. Analisi dei costi Bedrock, pattern di agenti, trade-off di storage vettoriale, failure mode in produzione.
Visita ercan.ai →L'hub. Chi sono, consulenza, contatti.
Hub personale per entrambe le tracce di scrittura. Chi sono, come funziona la consulenza, come contattarmi.
Visita ercanermis.com →