Un SLI è una misura di ciò che gli utenti hanno sperimentato, un SLO è l'obiettivo che fissi per quella misura, un SLA è un contratto che dice cosa succede quando l'obiettivo viene mancato, e l'error budget è la differenza tra il 100 percento e lo SLO, contata in richieste fallite che ti è permesso avere. Uno SLO del 99,9 percento su 30 giorni, per un servizio che gestisce 10 milioni di richieste, è un budget di 10.000 fallimenti. Questi quattro numeri sono il modo in cui un SRE risponde alla domanda a cui il resto dell'organizzazione non sa rispondere: quanto affidabile dovrebbe essere questo servizio, ed è sicuro rilasciare oggi?

Questa è la Parte 2 di Budget for Failure. La Parte 1 ha descritto il ruolo dell'SRE e si è chiusa sulla sua dipendenza: niente di tutto quello funziona senza un numero. Questa parte è ancora di livello 101. Definisce i termini, legge gli SLA di AWS per quello che promettono davvero, calcola un primo SLI dalle metriche del load balancer che hai già e trasforma il risultato in un budget con una policy annessa.

SLI: il rapporto tra eventi buoni ed eventi validi

Un service level indicator è un numero tra 0 e 100 percento, calcolato ogni volta nello stesso modo: eventi buoni divisi per eventi validi. Per un servizio guidato dalle richieste i due SLI più utili sono:

  • Disponibilità: richieste servite senza errore del server, divise per tutte le richieste.
  • Latenza: richieste servite più velocemente di una soglia (diciamo 300 ms), divise per tutte le richieste.

In quella definizione si nascondono due decisioni di design. Primo, cosa conta come valido. Un 404 per una URL che non esiste è il servizio che funziona correttamente; un 429 dal tuo stesso rate limiter è discutibile; un 500 no. Decidilo una volta e mettilo per iscritto, perché il denominatore è il punto in cui gli SLI vengono truccati in silenzio. Secondo, dove misuri. Più vicino all'utente, più onesto il numero. Una metrica presa al load balancer vede fallimenti che l'applicazione non logga mai, come un target che ha rifiutato la connessione. Una metrica presa dentro l'applicazione se li perde.

Le medie non si qualificano come SLI. Una latenza media di 120 ms è compatibile con una richiesta su venti che impiega quattro secondi. La forma a rapporto ("il 97,2 percento delle richieste sotto i 300 ms") ti dice quanti utenti hanno avuto una brutta esperienza, che è la cosa che stai cercando di limitare.

SLO: l'obiettivo, e la finestra su cui viene misurato

Un service level objective è un SLI più un obiettivo più una finestra temporale: "il 99,9 percento delle richieste ha successo, misurato su 30 giorni mobili". Tutte e tre le parti sono necessarie. Un obiettivo senza finestra è infalsificabile, dato che qualsiasi ora brutta può essere diluita nella media scegliendo a posteriori un periodo più lungo.

L'obiettivo è dove entrano in gioco i nove, e sui nove è più facile ragionare come downtime consentito. Per una finestra di 30 giorni (43.200 minuti):

SLOTempo di malfunzionamento consentito in 30 giorniRichieste fallite consentite ogni 10 milioni
99%7 ore 12 minuti100.000
99,5%3 ore 36 minuti50.000
99,9%43 minuti 12 secondi10.000
99,95%21 minuti 36 secondi5.000
99,99%4 minuti 19 secondi1.000
99,999%26 secondi100

Leggi la tabella dal basso. Al 99,99 percento hai circa quattro minuti al mese. Un umano chiamato alle 03:00 ha bisogno di più tempo solo per trovare un laptop, il che significa che uno SLO a quattro nove è un impegno a rilevamento automatico e ripristino automatico, con le persone coinvolte solo dopo. Ogni nove in più è più o meno un taglio di dieci volte del fallimento consentito e, in pratica, un grosso scalino di costo. Lo SLO giusto è il numero più basso sopra il quale i tuoi utenti non noterebbero la differenza, non il numero più alto che fa bella figura su una slide.

Il cento percento non è mai l'obiettivo giusto. Vieta ogni deploy, ogni migrazione e ogni dipendenza che non controlli, e i tuoi utenti ti raggiungono attraverso Wi-Fi domestici e reti mobili ben meno affidabili di qualsiasi cosa tu gestisca.

SLA: il contratto, e cosa AWS ti deve davvero

Un service level agreement è uno SLO con delle conseguenze annesse, firmato con un cliente. La conseguenza di solito è denaro. È tutta qui la differenza, ed è il motivo per cui lo SLA è sempre più lasco dello SLO interno: vuoi accorgertene e reagire ben prima che una violazione diventi un rimborso.

AWS è lo SLA che la maggior parte di noi legge per primo, e vale la pena leggerlo con attenzione perché la formulazione insegna il concetto. Quattro che stanno sotto un tipico servizio web:

Servizio e condizioneImpegno di uptime mensileCredito se mancato
EC2, istanze su due o più AZ (Region-Level)99,99%10%, 30% sotto il 99,0%, 100% sotto il 95,0%
EC2, una singola istanza (Instance-Level)99,5%10%, 30% sotto il 99,0%, 100% sotto il 95,0%
Elastic Load Balancing, load balancer su due o più AZ99,99%10%, 30% sotto il 99,0%, 100% sotto il 95,0%
RDS, istanza o cluster DB Multi-AZ99,95%10%, 25% sotto il 99,0%, 100% sotto il 95,0%
S3 Standard99,9%10%, 25% sotto il 99,0%, 100% sotto il 95,0%

In quei documenti tre cose contano più delle percentuali.

Il rimedio è un credito di servizio. Se S3 Standard scende al 99,5 percento per un mese, hai diritto al 10 percento dei costi S3 di quel mese per la Region colpita, applicato a una fattura futura, dopo aver presentato un reclamo con le prove. Il tuo fatturato perso, le penali dei tuoi SLA e il tuo weekend non fanno parte del calcolo. Uno SLA è una clausola di prezzo. Non è una polizza assicurativa e non è una previsione di come si comporterà il servizio.

L'impegno è condizionato alla tua architettura. Il 99,99 percento di EC2 vale solo quando tutte le istanze in esecuzione sono distribuite su due o più Availability Zone nella Region. Gira in una sola zona e sei sull'impegno instance-level del 99,5 percento, che consente circa 3,6 ore di downtime al mese. Lo SLA codifica il design che AWS si aspetta da te.

"Unavailable" ha una definizione legale ristretta. Ogni SLA definisce la propria misurazione, al minuto o per intervalli di cinque minuti, e le proprie esclusioni. Lento non è indisponibile. Tassi di errore elevati in una API da cui dipendi potrebbero non contare affatto.

Il tuo primo SLI, da metriche che hai già

Se il tuo servizio sta dietro un Application Load Balancer, CloudWatch contiene già uno SLI di disponibilità. Bastano tre metriche nel namespace AWS/ApplicationELB: RequestCount, HTTPCode_Target_5XX_Count (errori restituiti dal tuo codice) e HTTPCode_ELB_5XX_Count (errori generati dal load balancer stesso, per esempio quando nessun target in salute ha risposto). Salva questo come sli.json:

[
  { "Id": "req", "ReturnData": false,
    "MetricStat": { "Stat": "Sum", "Period": 86400,
      "Metric": { "Namespace": "AWS/ApplicationELB", "MetricName": "RequestCount",
        "Dimensions": [{ "Name": "LoadBalancer", "Value": "app/checkout/0123456789abcdef" }] } } },
  { "Id": "t5xx", "ReturnData": false,
    "MetricStat": { "Stat": "Sum", "Period": 86400,
      "Metric": { "Namespace": "AWS/ApplicationELB", "MetricName": "HTTPCode_Target_5XX_Count",
        "Dimensions": [{ "Name": "LoadBalancer", "Value": "app/checkout/0123456789abcdef" }] } } },
  { "Id": "e5xx", "ReturnData": false,
    "MetricStat": { "Stat": "Sum", "Period": 86400,
      "Metric": { "Namespace": "AWS/ApplicationELB", "MetricName": "HTTPCode_ELB_5XX_Count",
        "Dimensions": [{ "Name": "LoadBalancer", "Value": "app/checkout/0123456789abcdef" }] } } },
  { "Id": "sli", "Label": "availability_percent",
    "Expression": "100 * (req - FILL(t5xx, 0) - FILL(e5xx, 0)) / req" }
]
aws cloudwatch get-metric-data \
  --start-time "$(date -u -v-30d +%Y-%m-%dT00:00:00Z)" \
  --end-time "$(date -u +%Y-%m-%dT00:00:00Z)" \
  --metric-data-queries file://sli.json \
  --query 'MetricDataResults[0].[Timestamps,Values]'

Questo restituisce un valore di disponibilità al giorno per gli ultimi 30 giorni (la sintassi date -v è di macOS; su Linux usa date -u -d '30 days ago'). FILL(…, 0) è importante: CloudWatch non pubblica alcun datapoint per una metrica di errore in un periodo con zero errori, e senza il fill un giorno perfetto torna come dato mancante invece che come 100 percento.

Per la latenza, lo stesso namespace ha TargetResponseTime, e la statistica percentile rank di CloudWatch la trasforma direttamente nella forma a rapporto: PR(:0.3) restituisce la percentuale di richieste completate in 300 ms o meno.

L'error budget: quello che lo SLO ti lascia spendere

Una volta che il business ha concordato che il 99,9 percento è abbastanza, il restante 0,1 percento smette di essere un difetto da eliminare e diventa un margine da usare. Se il servizio sta al 99,97 percento con dieci giorni ancora nella finestra, c'è spazio per prendersi dei rischi: rilascia la migrazione, aggiorna il cluster, esegui il test di failover. Se sta al 99,88 percento, non c'è, e nessuno deve vincere una discussione per stabilirlo. Nemmeno un servizio che chiude ogni finestra con il budget intatto è un trionfo: lo SLO è più lasco di quello che serve agli utenti, oppure il team sta pagando per un'affidabilità che nessuno ha chiesto.

Il budget si può contare in eventi o in tempo, e le due forme si comportano in modo diverso.

SLO                      99.9% over 30 days
requests in window       10,000,000
error budget             (1 - 0.999) x 10,000,000 = 10,000 failed requests

bad requests so far      6,400
budget consumed          6,400 / 10,000 = 64%
budget remaining         3,600 requests = 36%

I budget basati sugli eventi pesano il fallimento in base al traffico. Dieci minuti di interruzione totale al picco del martedì costano molto di più di dieci minuti alle 04:00 di domenica, il che corrisponde a quello che gli utenti hanno sperimentato. È la forma da preferire per qualsiasi cosa guidata dalle richieste.

I budget basati sul tempo contano i minuti cattivi: il 99,9 percento su 30 giorni sono 43 minuti e 12 secondi. Sono più facili da spiegare a un non ingegnere e si adattano a servizi senza un flusso costante di richieste, come una pipeline batch o uno strumento interno a basso traffico. Il costo è che un minuto conta come del tutto cattivo o del tutto buono, quindi serve una regola per cosa rende cattivo un minuto (per esempio "tasso di errore sopra l'1 percento"), e un'interruzione parziale è invisibile oppure viene contata per intero.

La finestra conta quanto l'obiettivo. Una finestra mobile di 30 giorni riflette sempre il passato recente, e una brutta giornata esce dal calcolo esattamente 30 giorni dopo; è la scelta migliore per seguire l'esperienza utente. Una finestra di calendario si azzera il primo del mese, il che si allinea con il reporting e con la maggior parte degli SLA, e ha una brutta proprietà: un incidente il giorno 2 spende il budget per quattro settimane, mentre lo stesso incidente il giorno 30 viene perdonato due giorni dopo.

Cosa spende il budget

Tutto ciò che fa fallire la richiesta di un utente attinge dallo stesso conto, chiunque l'abbia causato. È proprio questo il punto. In ordine più o meno decrescente di quanto spende un team tipico:

  • Cambiamenti. Deploy, push di configurazione, feature flag, migrazioni di schema. Nella maggior parte delle raccolte di postmortem, il cambiamento è la singola causa più grande di incidenti. Se tieni traccia del change failure rate di DORA, stai già misurando il principale consumatore.
  • Dipendenze. Un rallentamento di una API regionale, un provider di pagamento terzo, un problema DNS. È il tuo budget anche quando non è colpa tua, perché all'utente non importa di chi sia la colpa.
  • Capacità e carico. Throttling, cache fredde, una policy di scaling che reagisce con due minuti di ritardo.
  • Lavoro pianificato. Finestre di manutenzione, test di failover, game day. Un'esercitazione deliberata di evacuazione di una zona che costa 300 richieste fallite è budget ben speso, e avere un budget è ciò che rende l'esercitazione approvabile.
  • Hardware e piattaforma. Ritiri di istanze, eventi di zona. Più piccoli di quanto ci si aspetti, se l'architettura è multi-AZ.

Tieni un registro. Dopo ogni incidente o cambiamento rischioso, annota quanto budget ha consumato e la sua causa. Tre mesi di quel registro ti dicono dove dovrebbe andare il tempo di reliability engineering meglio di qualsiasi discussione di roadmap.

DataEventoRichieste falliteQuota del budget
3 agoDeploy con un'impostazione sbagliata del connection pool, rollback in 9 minuti3.10031%
11 agoLatenza del provider di pagamento, timeout sul checkout2.20022%
14 agoTest di failover RDS pianificato3503,5%
Intera finestraErrori di fondo, nessuna causa singola7507,5%

In questo esempio un singolo deploy è costato più di un'interruzione di terzi e di un test pianificato messi insieme. Il caso di investimento si scrive da solo: canary analysis e rollback automatico prima di qualsiasi altra cosa.

La policy è il prodotto

Un budget senza conseguenze è una dashboard. Ciò che lo fa funzionare è una breve policy scritta, concordata in anticipo dalla leadership di engineering e di prodotto, che dice cosa cambia quando il budget si esaurisce. Il Google SRE Workbook pubblica una policy di esempio di cui vale la pena copiare la struttura. Una prima versione praticabile sta in mezza pagina:

  • Budget sopra il 50 percento: processo di rilascio normale. Cambiamenti rischiosi ed esperimenti sono incoraggiati.
  • Budget sotto il 50 percento: i cambiamenti richiedono una fase canary e un rollback testato. Niente esperimenti in produzione.
  • Budget esaurito: le release di feature si fermano finché il servizio non rientra nello SLO. Escono solo correzioni di affidabilità, patch di sicurezza e cambiamenti necessari a ripristinare il budget. La priorità del team sono le voci principali dei postmortem recenti.
  • Un singolo incidente consuma più del 20 percento del budget: il postmortem è obbligatorio, con almeno una action item ad alta priorità.
  • Disaccordo: decide un dirigente designato. Il percorso di escalation è scritto prima che serva.

Due proprietà fanno reggere tutto questo. È simmetrica: lo stesso documento che congela le release quando il budget è finito dice alle operations di smettere di bloccarle quando è in salute. Ed è concordata in anticipo: il product owner l'ha firmata in una settimana tranquilla, quindi invocarla in una brutta settimana è esecuzione, non scontro. Se la leadership non firma una policy capace di fermare il lavoro sulle feature, non hai ancora uno SLO. Hai una metrica.

Lascia che CloudWatch tenga il registro

Lo SLI qui sopra è stato calcolato a mano con get-metric-data. Per il budget corrente, CloudWatch ha una funzione SLO gestita dentro Application Signals. Definisci l'indicatore, l'obiettivo di raggiungimento e l'intervallo, e lei tiene traccia in continuo del raggiungimento e dell'error budget residuo. Tre scelte nel suo modello corrispondono direttamente a questo post:

  • Basato sui periodi o sulle richieste. Uno SLO basato sui periodi divide l'intervallo in periodi (un minuto, per esempio), giudica ciascuno buono o cattivo e riporta i periodi buoni sul totale dei periodi. Uno SLO basato sulle richieste conta le richieste buone sul totale delle richieste. Sono i budget basati sul tempo e sugli eventi descritti sopra.
  • Intervallo mobile o di calendario. Sono supportati entrambi, con il trade-off già descritto.
  • Su cosa è costruito lo SLI. I servizi strumentati con Application Signals ottengono metriche standard di latenza e disponibilità, con le risposte 5xx contate come fault e le 4xx trattate come successi. Se non sei strumentato, uno SLO può essere impostato su qualsiasi metrica CloudWatch o espressione metric math che produca una singola serie temporale, inclusa l'espressione del load balancer qui sopra.

La console mostra il risultato come due cifre: raggiungimento rispetto all'obiettivo, e budget residuo. Per uno SLO basato sulle richieste il budget residuo può salire oltre che scendere durante l'intervallo, perché dipende dal rapporto tra richieste buone e totali fino a quel momento, e una settimana tranquilla di traffico pulito diluisce un incidente precedente.

Modalità di guasto e cosa tenere d'occhio

Fare la media delle percentuali. Un giorno con 100 richieste al 90 percento e un giorno con un milione di richieste al 99,99 percento non fanno una media di 95. Lo SLI a 30 giorni è il totale delle richieste buone sul totale delle richieste valide. Somma i numeratori e i denominatori, poi dividi.

Un denominatore con dei buchi. RequestCount conta solo le richieste per cui il load balancer è riuscito a scegliere un target, quindi l'espressione sopra sovrastima leggermente il danno in un'interruzione totale. È la direzione sicura in cui sbagliare. Lo SLI inoltre non può vedere le richieste che non hanno mai raggiunto il load balancer: un fallimento DNS, un blocco del WAF, un client su una rete rotta.

Lo SLO che nessuno ha concordato. Un obiettivo scelto dal solo team di piattaforma non ha alcuna autorità quando si scontra con una data di lancio. Il numero deve essere di proprietà di chi possiede la roadmap.

Troppi SLO. Parti con uno o due per servizio rivolto all'utente: disponibilità e latenza sul percorso di richiesta che porta i soldi. Venti SLO producono venti budget, uno dei quali è sempre esaurito, e la policy diventa rumore.

Fissare l'obiettivo prima di vedere i dati. Esegui prima la query sugli ultimi 30 giorni. Uno SLO scelto prima di aver visto lo SLI è un desiderio. Uno fissato leggermente sotto la storia misurata, e poi stretto di proposito, è un obiettivo che puoi difendere.

Scoprirlo a fine mese. Un budget letto su un report mensile può solo spiegare il passato. Agire mentre c'è ancora budget richiede un alert sul ritmo di spesa, e far spendere meno al servizio richiede cambiamenti nel suo codice. Entrambi sono la Parte 3.

Leggi questo dopo

Per consulenza su AWS, architettura cloud, pratica SRE, pipeline CI/CD, Linux e altro, parti da ercanermis.com.

Riferimenti