Si preguntes a la direcció d'AlpinaShop quin nivell de servei espera de la botiga, la resposta serà «que no caigui mai». És una resposta comprensible i completament inútil, per tres motius: no es pot complir, no es pot mesurar, i no permet decidir res.

No es pot complir perquè el 100 % de disponibilitat no existeix: falla el maquinari, falla la xarxa del client, falla el proveïdor de DNS, falla un desplegament, s'equivoca una persona. No es pot mesurar perquè ningú no ha definit què compta com a «caiguda». I no permet decidir res perquè si l'objectiu és la perfecció, qualsevol inversió en fiabilitat està justificada i cap no és suficient, cosa que a la pràctica significa que les decisions es prenen per intuïció.

La lliçó anterior va acabar assenyalant exactament això: AlpinaShop paga l'alta disponibilitat de Cloud SQL, ha duplicat els túnels VPN i manté el balancejador global sense cap objectiu declarat que justifiqui aquestes tres decisions amb números. I arrossega des de 07-04 un deute qualificat de crític: ningú no ha comprovat mai que les còpies de seguretat es puguin restaurar.

Aquesta lliçó converteix la fiabilitat en enginyeria. En acabar sabràs definir SLI, SLO i SLA amb precisió, calcular i fer servir un pressupost d'error per decidir quan es desplega i quan es para, crear SLO i alertes per consum de pressupost a Cloud Monitoring, triar el nivell d'alta disponibilitat de cada component sabent què costa i què aporta, dissenyar contra els modes de fallada reals, i tenir un pla de recuperació de desastres amb RTO i RPO — assajat, no escrit.

Contingut

  1. Què significa fiabilitat i per què la perfecció no és un objectiu
  2. SLI, SLO i SLA: les tres sigles que tothom confon
  3. Triar bons SLI per a AlpinaShop
  4. Els SLO d'AlpinaShop, amb les seves consultes reals
  5. Pressupost d'error: què és i per a què serveix de debò
  6. SLO a Cloud Monitoring i alertes per consum de pressupost
  7. Arquitectures d'alta disponibilitat per nivells
  8. Modes de fallada i la seva mitigació
  9. Recuperació de desastres: RTO, RPO i estratègies
  10. El pla de recuperació d'AlpinaShop
  11. Còpies que no s'han provat no existeixen
  12. Pràctica operativa: guàrdies, runbooks i post mortem
  13. Proves de caos a escala raonable

  1. Què significa fiabilitat i per què la perfecció no és un objectiu

Fiabilitat és la probabilitat que un sistema faci el que s'espera d'ell durant un període determinat. No és una propietat binària: és una magnitud contínua que es tria, es paga i es gestiona.

La corba que cal tenir al cap:

Objectiu Cost relatiu Què exigeix
99 % 3 Un servidor ben configurat
99,9 % 8 Multizona, desplegament canari, reversió ràpida
99,95 % 15 L'anterior + guàrdies en horari ampliat
99,99 % 35 Multiregió, guàrdies 24×7, automatització de commutació
99,999 % 95 Actiu-actiu, enginyeria dedicada, desplegaments molt més lents

Cada nou addicional multiplica el cost, i no de manera lineal. Passar de 99 % a 99,9 % sol ser qüestió de bones pràctiques. Passar de 99,99 % a 99,999 % requereix multiregió actiu-actiu, guàrdies 24×7, enginyeria dedicada i acceptar que desplegues molt més a poc a poc.

I l'altra meitat de l'equació, la que gairebé mai no es calcula:

Disponibilitat Temps caigut al mes Comandes perdudes (AlpinaShop) Cost aproximat del tall
99 % 7 h 18 min ~12 ~780 €
99,5 % 3 h 39 min ~6 ~390 €
99,9 % 43 min 50 s ~1,2 ~78 €
99,95 % 21 min 55 s ~0,6 ~39 €
99,99 % 4 min 23 s ~0,1 ~8 €
99,999 % 26 s ~0 ~0 €

Amb 1.200 comandes al mes i un tiquet mitjà de 65 €, passar de 99,9 % a 99,99 % estalviaria a AlpinaShop uns 70 € al mes en comandes no perdudes — i li costaria multiregió actiu-actiu, és a dir, diversos centenars d'euros mensuals més la feina d'enginyeria.

La conclusió incòmoda i correcta: per a AlpinaShop, 99,99 % seria una mala inversió. I saber dir això amb números a la mà, en lloc de perseguir nous per inèrcia, és de les coses més valuoses que pot aportar un enginyer.

Amb dos matisos que cal fer explícits:

  1. No tot el temps val igual. Quaranta minuts de caiguda de matinada al febrer no costen gairebé res; quaranta minuts el divendres de la campanya de tardor poden costar vint vegades més. La disponibilitat importa ponderada per trànsit.
  2. Hi ha costos que no són comandes perdudes: reputació, clients que no tornen, suport, i —en un incident llarg— el desgast de l'equip.

  1. SLI, SLO i SLA: les tres sigles que tothom confon

Sigla Què és Naturalesa Exemple a AlpinaShop
SLI (Service Level Indicator) El que mesures Un número, una proporció % de peticions al catàleg amb codi < 500
SLO (Service Level Objective) L'objectiu que et poses Intern, un llindar sobre el SLI 99,9 % en 28 dies
SLA (Service Level Agreement) El que promets per contracte Extern, amb penalització 99,5 %, o es retorna el 10 %

La relació entre els tres, que és el que cal interioritzar:

flowchart LR
    M["Mètriques crues<br/>Cloud Monitoring"] --> SLI["SLI<br/>bones / vàlides"]
    SLI --> SLO["SLO<br/>llindar intern"]
    SLO --> EB["Pressupost<br/>d error"]
    EB --> DEC["Decisions:<br/>desplegar o estabilitzar"]
    SLO -.->|"sempre més exigent"| SLA["SLA<br/>compromís extern"]

Tres regles que eviten gairebé tots els errors:

  1. El SLO sempre és més exigent que el SLA. Si promets 99,5 % i el teu objectiu intern és 99,5 %, incompliràs el contracte la meitat de les vegades. La pràctica habitual és un marge folgat: SLA 99,5 %, SLO 99,9 %.
  2. El SLI es mesura des d'on és l'usuari, no des d'on és còmode. Un servidor que respon perfectament mentre el balancejador retorna 502 té un SLI del 100 % i un usuari furiós.
  3. Un SLO sense conseqüència és un adorn. Si en incomplir-lo no canvia res, no és un objectiu: és un número en un tauler.

I una definició precisa de SLI que resol la majoria dels dubtes:

SLI = esdeveniments bons / esdeveniments vàlids

Tot el que és difícil és definir aquests dos termes. Compta com a «dolent» un 404? (No: l'usuari va demanar una cosa que no existeix.) I un 429 del limitador de taxa? (Depèn: si és un atac, no; si és un client legítim, sí.) I una petició d'un robot rastrejador? (Probablement no hauria de ser un esdeveniment vàlid.) Aquestes decisions són el 80 % de la feina de definir un SLI, i cal prendre-les per escrit.

  1. Triar bons SLI per a AlpinaShop

Un bon SLI compleix quatre condicions:

Condició Significa Contraexemple
Reflecteix l'experiència de l'usuari Si el SLI està bé, l'usuari està content CPU del servidor
És mesurable amb el que ja tens Surt de mètriques existents «Satisfacció del client»
És predictiu Quan es degrada, el negoci se'n ressent Nombre de registres
És accionable Saps què fer si baixa «Salut general del sistema»

L'error més comú és mesurar el fàcil en lloc de l'important. La CPU és facilíssima de mesurar i no li importa a cap client. Els «quatre senyals d'or» —latència, trànsit, errors i saturació— són un bon punt de partida, però només els dos primers i el tercer són candidats a SLI; la saturació és una causa, no una experiència.

Els quatre SLI d'AlpinaShop, triats recorrent el viatge del client:

SLI 1 — Disponibilitat del catàleg

Esdeveniments vàlids: peticions HTTP al balancejador amb rutes de la botiga, excloent-hi rastrejadors coneguts. Esdeveniments bons: les que retornen codi < 500.

Decisions preses i anotades:

Cas Vàlid? Bo? Raó
200 OK
404 producte inexistent El sistema va funcionar correctament
429 de Cloud Armor a un atacant No No és un usuari a qui servir
500, 502, 503 No Fallada nostra
499 el client va tallar No Se'n va anar el client, no va fallar el sistema
Petició d'un rastrejador No No és experiència d'usuari

SLI 2 — Latència del catàleg

Esdeveniments vàlids: les mateixes peticions que al SLI 1. Esdeveniments bons: les que se serveixen en menys de 800 ms.

Compte amb l'error clàssic: un SLI de latència no és «el p95 és menor que X». És la proporció de peticions més ràpides que X. La diferència importa: un percentil no es pot combinar amb un pressupost d'error, i una proporció sí.

El llindar de 800 ms no es tria per intuïció: es mesura la distribució actual, es comprova on és el punt a partir del qual els usuaris abandonen, i es tria un valor que avui es compleixi folgadament però que detecti una degradació real.

SLI 3 — Èxit del procés de pagament

El més important dels quatre, perquè és el que toca els diners.

Esdeveniments vàlids: intents de pagament iniciats. Esdeveniments bons: els que acaben en confirmació o en un rebuig legítim de la passarel·la (fons insuficients, targeta caducada).

El matís que el fa correcte: un rebuig de la passarel·la per fons insuficients no és una fallada nostra. Comptar-lo com a error faria que el SLI depengués de la solvència dels clients, no de la qualitat del sistema. El que sí que compta com a dolent: temps d'espera esgotats, errors 5xx, excepcions i transaccions que queden en estat indeterminat.

SLI 4 — Frescor de les dades analítiques

Diferent dels anteriors perquè no és una petició: és un procés.

Esdeveniments vàlids: execucions programades del pipeline de comandes. Esdeveniments bons: les que deixen les dades a BigQuery amb menys de 2 hores de retard.

La taula de SLI

SLI Què mesura Font Criticitat
Disponibilitat del catàleg Es pot comprar? Registres del balancejador Crítica
Latència del catàleg És usable? Registres del balancejador Alta
Èxit del pagament Es cobra? Mètrica personalitzada (06-04) Crítica
Frescor analítica Decideix bé la Lucía? Mètrica de Dataflow / Workflows Mitjana

Fixa't que no hi ha SLI de CPU, memòria, nombre d'instàncies ni mida de la cua. Tot això es monitora —cal per diagnosticar— però no és un objectiu de nivell de servei, perquè a cap client no li importa.

  1. Els SLO d'AlpinaShop, amb les seves consultes reals

SLI SLO Finestra Pressupost d'error Justificació
Disponibilitat del catàleg 99,9 % 28 dies mòbils 40 min 19 s Equilibri cost/valor de l'apartat 1
Latència < 800 ms 99 % 28 dies mòbils 6 h 43 min Una petició lenta molesta; no impedeix comprar
Èxit del pagament 99,95 % 28 dies mòbils 20 min 10 s Més exigent: aquí es perden diners directes
Frescor analítica < 2 h 99 % 28 dies mòbils 6 h 43 min Un retard puntual no afecta les decisions

Per què 28 dies i no un mes natural: una finestra mòbil de 28 dies conté exactament quatre setmanes, així que sempre inclou el mateix nombre de caps de setmana i de dilluns. Amb mesos naturals, febrer i març no són comparables, i el pressupost es «reinicia» el dia 1, cosa que crea l'incentiu pervers d'esperar al canvi de mes per desplegar el que és arriscat.

Les consultes reals, sobre les mètriques de 06-04, en PromQL —que és el llenguatge que Cloud Monitoring admet avui juntament amb MQL:

# SLI 1 - Disponibilitat del cataleg
sum(rate(loadbalancing_googleapis_com:https_request_count{
    monitored_resource="https_lb_rule",
    url_map_name="alpinashop-url-map",
    response_code_class!="500"
}[5m]))
/
sum(rate(loadbalancing_googleapis_com:https_request_count{
    monitored_resource="https_lb_rule",
    url_map_name="alpinashop-url-map"
}[5m]))
# SLI 2 - Latencia: proporcio de peticions per sota de 800 ms
sum(rate(loadbalancing_googleapis_com:https_total_latencies_bucket{
    url_map_name="alpinashop-url-map", le="800"
}[5m]))
/
sum(rate(loadbalancing_googleapis_com:https_total_latencies_count{
    url_map_name="alpinashop-url-map"
}[5m]))

Per al pagament, es fa servir la mètrica personalitzada que l'aplicació ja emet des de 06-04:

# Al codi del cataleg, despres de cada intent de pagament
from opentelemetry import metrics

mesurador = metrics.get_meter("alpinashop.pagos")
comptador = mesurador.create_counter(
    "pagos_intentos",
    description="Intents de pagament per resultat",
)

def registrar_pagament(resultat: str, passarela: str):
    # resultat: "confirmado" | "rechazado_legitimo" | "error_tecnico"
    comptador.add(1, {"resultado": resultat, "pasarela": passarela})
# SLI 3 - Exit del pagament: confirmats + rebuigs legitims / total
sum(rate(custom_googleapis_com:pagos_intentos{
    resultado=~"confirmado|rechazado_legitimo"
}[5m]))
/
sum(rate(custom_googleapis_com:pagos_intentos[5m]))

La separació entre rechazado_legitimo i error_tecnico és una decisió d'instrumentació que cal prendre al codi, i és exactament on es veu si algú ha pensat el SLI o l'ha copiat. Si l'aplicació només registra «èxit» i «fallada», el SLI de pagament és irreparablement ambigu.

  1. Pressupost d'error: què és i per a què serveix de debò

Pressupost d'error = 100 % − SLO

Amb un SLO del 99,9 % en 28 dies, el pressupost és del 0,1 %: 40 minuts i 19 segons d'indisponibilitat, o el seu equivalent en peticions fallides.

La taula de «nous» traduïda a temps, que convé tenir a mà:

SLO Al dia A la setmana Als 28 dies A l'any
99 % 14 min 24 s 1 h 40 min 6 h 43 min 3 d 15 h
99,5 % 7 min 12 s 50 min 3 h 22 min 1 d 19 h
99,9 % 1 min 26 s 10 min 5 s 40 min 19 s 8 h 46 min
99,95 % 43 s 5 min 2 s 20 min 10 s 4 h 23 min
99,99 % 8,6 s 1 min 4 min 2 s 52 min 36 s
99,999 % 0,9 s 6 s 24 s 5 min 15 s

Per a què serveix de debò

Aquí hi ha la part que transforma el concepte de curiositat en eina de gestió. El pressupost d'error resol la tensió permanent entre desenvolupament i operacions:

Desenvolupament vol Operacions vol
Ritme Desplegar ràpid i sovint Canviar poc
Risc Provar coses noves Estabilitat
Incentiu Funcionalitats Que no soni el telèfon

Sense un criteri objectiu, aquesta tensió es resol per autoritat, per cansament o per qui crida més. Amb pressupost d'error, es resol amb un número:

Mentre quedi pressupost, es desplega. Quan s'esgota, es para i s'estabilitza.

I això converteix la fiabilitat en una cosa que desenvolupament també vol, perquè un sistema fràgil consumeix el pressupost i li impedeix lliurar funcionalitats. L'incentiu deixa d'estar enfrontat.

La política de pressupost d'error d'AlpinaShop

Escrita, acordada i al README d'alpinashop-infra:

Pressupost consumit Estat Què es pot fer
< 50 % 🟢 Verd Tot normal. Es desplega diàriament. Es pot experimentar
50-75 % 🟡 Groc Es desplega, però amb canari més llarg i sense canvis d'esquema els divendres
75-100 % 🟠 Taronja Només correccions i millores de fiabilitat. Res de funcionalitats noves
> 100 % 🔴 Vermell Congelació de funcionalitats. Tot l'equip a estabilitzar. Post mortem obligatori

I la clàusula que fa que la política sigui creïble:

Si el pressupost s'esgota per una fallada de la plataforma de Google i no pels nostres canvis, es documenta i no es congela. L'objectiu és corregir el que està a la nostra mà, no castigar l'equip pel que no controla.

Sense aquesta clàusula, la primera caiguda regional de Google convertiria la política en una cosa que l'equip deixaria de respectar — i una política que s'ignora és pitjor que no tenir-ne.

L'ús que gairebé ningú no fa: gastar el pressupost a propòsit

El pressupost d'error no és un objectiu que calgui minimitzar. Si al final dels 28 dies n'has consumit el 5 %, no has guanyat: probablement eres massa conservador i podies haver lliurat més ràpid, o el teu SLO és massa lax i no reflecteix el que els usuaris noten.

Un pressupost que no es toca mai és tan mal senyal com un que sempre s'esgota. El consum sa ronda el 50-80 %.

  1. SLO a Cloud Monitoring i alertes per consum de pressupost

Cloud Monitoring té suport natiu per a SLO, i val la pena fer-lo servir en lloc de construir-lo a mà.

Crear el SLO

gcloud monitoring slos create \
  --service=alpinashop-web-service \
  --slo-id=disponibilidad-catalogo \
  --display-name="Disponibilitat del cataleg 99.9%" \
  --goal=0.999 \
  --rolling-period=28d \
  --request-based-good-total-ratio \
  --project=alpinashop-prod

I en Terraform, que és on ha de viure:

resource "google_monitoring_slo" "disponibilidad_catalogo" {
  project      = var.proyecto_prod
  service      = google_monitoring_custom_service.tienda.service_id
  slo_id       = "disponibilidad-catalogo"
  display_name = "Disponibilitat del cataleg 99.9%"

  goal                = 0.999
  rolling_period_days = 28

  request_based_sli {
    good_total_ratio {
      good_service_filter = join(" AND ", [
        "metric.type=\"loadbalancing.googleapis.com/https/request_count\"",
        "resource.type=\"https_lb_rule\"",
        "resource.label.\"url_map_name\"=\"alpinashop-url-map\"",
        "metric.label.\"response_code_class\"!=\"500\"",
      ])
      total_service_filter = join(" AND ", [
        "metric.type=\"loadbalancing.googleapis.com/https/request_count\"",
        "resource.type=\"https_lb_rule\"",
        "resource.label.\"url_map_name\"=\"alpinashop-url-map\"",
      ])
    }
  }
}

Dos tipus de SLI que cal distingir:

Tipus Com compta Quan fer-lo servir
Basat en peticions (request_based) Proporció de peticions bones L'habitual. Serveis amb trànsit
Basat en finestres (windows_based) Proporció de minuts «bons» Serveis de baix trànsit, o quan importa la durada del tall més que el nombre d'afectats

Per a AlpinaShop, basat en peticions. Amb un matís honest: de matinada, amb tres peticions per minut, un sol error és el 33 % d'aquell minut. Els SLI basats en peticions són sorollosos amb trànsit baix, i per això les alertes de l'apartat següent exigeixen finestres llargues per als llindars suaus.

Alertes per consum de pressupost (burn rate)

Aquesta és la part més valuosa de l'apartat, i substitueix les alertes de llindar fix de 06-04.

La taxa de consum (burn rate) és quantes vegades més ràpid del que és sostenible s'està gastant el pressupost:

  • Taxa 1 = a aquest ritme, el pressupost s'esgota exactament al final dels 28 dies. És el ritme sostenible.
  • Taxa 14,4 = s'esgota en 2 dies.
  • Taxa 6 = s'esgota en poc menys de 5 dies.

El problema d'alertar sobre un llindar fix d'errors és conegut: si el llindar és sensible, hi ha falses alarmes; si és lax, no detecta degradacions lentes. Les alertes de taxa de consum resolen les dues coses amb dues alertes complementàries.

Alerta Taxa Finestres Consum Acció
Ràpida 14,4× 1 h i 5 min 2 % del pressupost en 1 h 📟 Despertar algú
Lenta 6 h i 30 min 5 % del pressupost en 6 h 🎫 Crear un tiquet, horari laboral

Per què dues finestres per alerta. La finestra llarga (1 h) evita falses alarmes per un pic de trenta segons. La finestra curta (5 min) fa que l'alerta s'apagui ràpid quan el problema es resol — sense ella, una alerta amb finestra d'una hora continuaria sonant cinquanta minuts després d'arreglada la fallada. És un detall petit amb un impacte enorme en la vida de qui està de guàrdia.

resource "google_monitoring_alert_policy" "consumo_rapido" {
  project      = var.proyecto_prod
  display_name = "SLO cataleg: consum rapid del pressupost (14.4x)"
  combiner     = "AND"

  conditions {
    display_name = "Taxa de consum > 14.4 en 1h i en 5min"
    condition_threshold {
      filter = join(" AND ", [
        "select_slo_burn_rate(\"${google_monitoring_slo.disponibilidad_catalogo.name}\", \"3600s\")",
      ])
      threshold_value = 14.4
      comparison      = "COMPARISON_GT"
      duration        = "300s"
    }
  }

  notification_channels = [var.canal_movil_marta, var.canal_slack_incidentes]

  documentation {
    content = <<-EOT
      # Consum rapid del pressupost d error

      A aquest ritme, el pressupost de 28 dies s esgota en menys de 2 dies.

      ## Primers passos
      1. Tauler de campanya: https://console.cloud.google.com/monitoring/dashboards/...
      2. Ultim desplegament: `gcloud run revisions list --service=alpinashop-web --region=europe-west1`
      3. Si coincideix amb un desplegament recent, REVERTIR PRIMER:
         `gcloud run services update-traffic alpinashop-web --region=europe-west1 --to-revisions=REVISION_ANTERIOR=100`
      4. Errors per tipus: Error Reporting
      5. Runbook complet: alpinashop-infra/runbooks/catalogo-5xx.md
    EOT
    mime_type = "text/markdown"
  }
}

El bloc documentation és el més important de la política, i el que gairebé tothom deixa buit. Una alerta que arriba al mòbil a les 3 de la matinada amb el text «Llindar superat» obliga la persona a reconstruir el context mig adormida. Una que arriba amb els tres primers passos i l'enllaç al runbook converteix quinze minuts de desorientació en dos d'acció. Es desenvolupa a l'apartat 12.

  1. Arquitectures d'alta disponibilitat per nivells

L'alta disponibilitat es construeix eliminant punts únics de fallada, i es fa per nivells, cadascun amb el seu preu.

Nivell Què resisteix Què NO resisteix Cost relatiu
Una zona Fallada d'una màquina Caiguda de la zona (manteniment, tall elèctric)
Multizona (regional) Caiguda d'una zona completa Caiguda de la regió sencera 1,5-2×
Multiregió Caiguda d'una regió Fallada global de la plataforma o error propi replicat 2,5-4×
Multinúvol Fallada global d'un proveïdor Errors propis 4-10×

La freqüència relativa de cada esdeveniment és el que hauria de guiar la decisió, i poques vegades es mira:

Esdeveniment Freqüència aproximada
Fallada d'una VM concreta Setmanal en flotes grans
Degradació d'una zona Unes quantes vegades l'any
Caiguda d'una regió completa Molt infreqüent, però passa
Fallada global d'un proveïdor Extraordinari
Error humà o desplegament defectuós La causa més comuna de totes

L'última fila és la que canvia les prioritats: la majoria de les caigudes no les provoca la infraestructura, sinó un canvi. Invertir en multiregió mentre no tens desplegament canari ni reversió ràpida és blindar la porta i deixar la finestra oberta. AlpinaShop va fer el correcte: primer pipeline i reversió (mòdul 6 i 07-02), després arquitectura.

Component a component

Component Nivell actual Com pujar Cost extra Val la pena?
Balancejador global Multiregió per disseny ✅ Ja hi és
Cloud DNS Anycast global ✅ Ja hi és
Cloud Armor Global ✅ Ja hi és
Cloud CDN Global ✅ Ja hi és
Cloud Run Regional: reparteix entre zones només Segon servei a europe-southwest1 + NEG ~+7 €/mes ⏸️ Documentat, no fet
Cloud SQL HA regional (rèplica en una altra zona) Rèplica de lectura en una altra regió ~+55 €/mes ⏸️ Avaluat, no fet
Cloud Storage Regional Bucket biregional o multiregió ~+30 % ✅ Fet al bucket crític
Firestore Multiregió ✅ Ja hi és
BigQuery Multiregió EU ✅ Ja hi és
Artifact Registry Regional Rèplica Baix ⏸️ No crític
Secret Manager Replicació automàtica ✅ Ja hi és
VPN a Sabadell Dos túnels Segona passarel·la a Sabadell ~+30 €/mes ❌ No: la botiga no és crítica

Observació important: una part gran de l'arquitectura d'AlpinaShop ja és multiregió sense esforç, perquè els serveis globals de Google ho són per disseny. El balancejador, DNS, CDN, Armor, Firestore, BigQuery i Secret Manager no requereixen cap decisió. Els dos components que que són regionals i crítics són Cloud Run i Cloud SQL.

Cloud SQL: què és exactament HA regional

Mereix detall perquè es paga el doble i molta gent no sap què compra.

flowchart LR
    APP["Cloud Run"] --> IP["IP privada única"]
    IP --> P["Instància primària<br/>europe-west1-b"]
    P <-->|"replicació síncrona<br/>de disc"| S["Instància en espera<br/>europe-west1-c"]
    P --> R["Rèplica de lectura<br/>alpinashop-pedidos-replica-informes"]
    R -.->|"només lectura"| LU["Consultes de la Lucía"]

    style S fill:#e8f0fe,stroke:#4285f4
Aspecte Detall
Què replica El disc, de manera síncrona. Zero pèrdua de dades
On Una altra zona de la mateixa regió
Commutació Automàtica, típicament 1-2 minuts
Què veu l'aplicació La mateixa IP. Reconnecta sola si té reintents
Cost El doble de la instància
Què no cobreix Caiguda de la regió completa; esborrat accidental de dades; corrupció lògica

L'última fila és la que cal subratllar. HA regional protegeix contra fallades d'infraestructura. No protegeix contra un DELETE FROM pedidos sense WHERE, que es replica a l'instant a la instància en espera. Per a això hi ha les còpies de seguretat i la recuperació a un punt en el temps — apartat 11.

Com seria Cloud Run multiregió

Documentat a alpinashop-infra com a disseny llest per activar:

# 1. Desplegar el mateix servei a la segona regio
gcloud run deploy alpinashop-web \
  --image=europe-west1-docker.pkg.dev/alpinashop-prod/alpinashop/catalogo:a3f91c2 \
  --region=europe-southwest1 \
  [... mateixes banderes ...]

# 2. NEG sense servidor en aquesta regio
gcloud compute network-endpoint-groups create neg-catalogo-run-esw1 \
  --region=europe-southwest1 \
  --network-endpoint-type=serverless \
  --cloud-run-service=alpinashop-web

# 3. Afegir-lo al MATEIX servei de backend
gcloud compute backend-services add-backend bs-catalogo-web \
  --global \
  --network-endpoint-group=neg-catalogo-run-esw1 \
  --network-endpoint-group-region=europe-southwest1

Tres comandes. El balancejador global envia cada petició a la regió més propera amb capacitat, i si una regió falla, deixa d'enviar-li trànsit automàticament.

El que impedeix activar-ho avui no és Cloud Run: és Cloud SQL. Amb la base de dades només a europe-west1, les instàncies de Madrid haurien de creuar regió a cada consulta —latència i cost de trànsit— i si cau europe-west1, la base de dades cau igual. L'alta disponibilitat la determina el component menys disponible, i a AlpinaShop aquest és Cloud SQL.

  1. Modes de fallada i la seva mitigació

Els sistemes distribuïts fallen de maneres concretes i conegudes. Dissenyar contra elles és més rendible que afegir redundància.

Dependències i la seva classificació

El primer és distingir dos tipus, perquè el tractament és oposat:

Tipus Definició Exemple Si falla
Dura Sense ella el servei no pot funcionar Cloud SQL per al catàleg Fallada del servei
Tova Aporta valor però no és imprescindible El recomanador, l'analítica El servei ha de continuar

La fallada de disseny més comuna és tractar una dependència tova com si fos dura. Un recomanador que triga 5 segons no ha de fer que la pàgina de producte trigui 5 segons: ha de desaparèixer de la pàgina.

Temps d'espera

Tota crida de xarxa ha de tenir un temps d'espera. Sense excepcions.

Un temps d'espera absent o massa llarg és el mecanisme pel qual una fallada petita es converteix en una caiguda total: les peticions s'acumulen esperant, s'esgoten els fils, i el servei deixa de respondre a tot, inclòs el que no depenia del sistema lent.

Crida Temps d'espera Raó
Cloud SQL, consulta normal 2 s Si triga més, alguna cosa va malament
Cloud SQL, connexió 5 s
Passarel·la de pagament 10 s És lenta per naturalesa
Recomanador (tova) 300 ms Si no respon ràpid, no es mostra
ERP de Sabadell per VPN 5 s L'enllaç pot estar regular
Secret Manager 3 s Es desa a la memòria cau després de la primera lectura

Regla d'or: el temps d'espera d'una crida sempre ha de ser menor que el temps d'espera de qui la crida. Si Cloud Run talla als 60 s i la teva consulta espera 90 s, aquest codi no s'executa mai.

Reintents amb retrocés exponencial i jitter

Reintentar està bé. Reintentar malament empitjora la fallada:

# MALAMENT: reintent immediat. Davant d una fallada, multiplica la carrega sobre
# un servei que ja esta patint. Es com colpejar algu que ha caigut.
for intent in range(5):
    try:
        return cridar()
    except Exception:
        continue

# BE: retroces exponencial + jitter + limit + nomes errors transitoris
import random, time

TRANSITORIS = (503, 502, 504, 429)

def cridar_amb_reintents(fn, max_intents=4, base=0.2, sostre=8.0):
    for intent in range(max_intents):
        try:
            return fn()
        except ErrorHTTP as e:
            if e.codi not in TRANSITORIS or intent == max_intents - 1:
                raise                                   # no es reintenta el permanent
            espera = min(sostre, base * (2 ** intent))  # 0.2, 0.4, 0.8, 1.6...
            espera = espera * (0.5 + random.random())   # jitter: +/- 50%
            time.sleep(espera)

Els quatre elements, i per què cadascun:

  1. Retrocés exponencial: donar temps que l'altre es recuperi.
  2. Jitter: sense ell, mil clients que van fallar alhora reintenten alhora, creant una ona sincronitzada que torna a tombar el servei just quan s'estava aixecant. És una fallada real i freqüent, i el jitter costa una línia.
  3. Límit d'intents: reintentar indefinidament converteix una fallada en un atac al teu propi sistema.
  4. Només errors transitoris: reintentar un 400 o un 403 no ho arreglarà mai; només gasta temps i recursos.

Interruptor de circuit

Quan un servei porta una estona fallant, deixa de cridar-lo:

class Interruptor:
    """Tancat = passa. Obert = falla rapid. Semiobert = prova amb una."""
    def __init__(self, llindar=5, espera=30):
        self.llindar, self.espera = llindar, espera
        self.fallades, self.obert_des_de = 0, None

    def cridar(self, fn, alternativa=None):
        import time
        if self.obert_des_de:
            if time.time() - self.obert_des_de < self.espera:
                return alternativa() if alternativa else None    # falla rapid
            self.obert_des_de = None                             # semiobert: es prova
        try:
            r = fn()
            self.fallades = 0
            return r
        except Exception:
            self.fallades += 1
            if self.fallades >= self.llindar:
                self.obert_des_de = time.time()
            if alternativa:
                return alternativa()
            raise

El benefici doble: qui crida deixa d'esperar (falla en microsegons en lloc de en segons) i qui falla deixa de rebre càrrega, cosa que li permet recuperar-se.

Degradació elegant

El principi que fa que una fallada parcial sigui invisible:

La botiga continua venent encara que el recomanador falli.

@app.route("/producto/<sku>")
def producte(sku):
    p = catalogo.obtener(sku)          # dependencia DURA: si falla, error 500

    # Dependencia TOVA: si falla, la pagina es serveix sense recomanacions
    try:
        recomanats = interruptor_reco.cridar(
            lambda: recomanador.per_a(sku, timeout=0.3),
            alternativa=lambda: catalogo.mas_vendidos_de(p.categoria),   # pla B
        )
    except Exception:
        logging.warning(json.dumps({
            "severity": "WARNING", "message": "Recomanador no disponible",
            "sku": sku, "degradado": True,
        }))
        recomanats = []                                                  # pla C

    return render("producto.html", producto=p, recomendados=recomanats)

Els tres nivells són la clau del patró: pla A el recomanador; pla B alguna cosa simple i local —els més venuts de la categoria, que gairebé ningú no distingirà—; pla C res, i la pàgina se serveix igual. Un client pot comprar en els tres casos, que és l'única cosa que importa.

I el detall que fa possible mesurar-ho: el registre porta "degradado": true. Sense aquesta marca, el servei funciona degradat durant setmanes i ningú no se n'assabenta, perquè el SLI de disponibilitat continua en verd. Una alerta sobre el percentatge de peticions degradades és el que evita que la degradació elegant es converteixi en degradació silenciosa.

Limitació de càrrega

Quan la demanda supera la capacitat, hi ha dues opcions: degradar per a tothom o rebutjar-ne alguns i servir bé la resta. La segona és gairebé sempre millor.

Mecanisme On Què fa
Cloud Armor rate-based-ban Vora Talla l'abús abans d'arribar (03-05)
--max-instances de Cloud Run Servei Tallafoc de cost i de connexions (07-02)
Cua amb longitud màxima Aplicació Rebutja amb 503 en lloc d'acumular
Descàrrega de càrrega per prioritat Aplicació Rebutja rastrejadors abans que compradors

L'últim és el més sofisticat i el més útil: quan hi ha saturació, no totes les peticions valen el mateix. Un usuari en el procés de pagament val molt més que un rastrejador indexant la pàgina 47 del catàleg.

  1. Recuperació de desastres: RTO, RPO i estratègies

Alta disponibilitat i recuperació de desastres no són el mateix, i confondre-les porta a arquitectures cares que no protegeixen del que de debò passa:

Alta disponibilitat Recuperació de desastres
Protegeix de Fallades d'infraestructura Desastres i errors humans
Actuació Automàtica Habitualment manual
Escala temporal Segons o minuts Hores o dies
Exemple Commutació de zona a Cloud SQL Restaurar després d'un esborrat massiu

Una dada que reordena prioritats: la causa més freqüent de pèrdua de dades no és un desastre natural ni un atac. És algú executant alguna cosa a l'entorn equivocat. I contra això, l'alta disponibilitat no serveix de res: replica l'error fidelment.

Les dues mètriques

Mètrica Pregunta Determina
RTO (Recovery Time Objective) Quant temps pot estar caigut? L'estratègia
RPO (Recovery Point Objective) Quantes dades puc perdre? La freqüència de còpia
flowchart LR
    C1["Última còpia<br/>03:00"] -.->|"RPO = dades perdudes"| D["DESASTRE<br/>10:15"]
    D -.->|"RTO = temps caigut"| R["Servei restaurat<br/>14:15"]

Amb còpies diàries a les 3:00 i un desastre a les 10:15, el RPO real són 7 hores i 15 minuts de dades perdudes. Per a AlpinaShop, això serien les comandes de tot un matí: inacceptable.

Les quatre estratègies

Estratègia RTO RPO Cost Com
Còpia i restauració Hores a dies Hores Còpies en una altra regió; es reconstrueix
Llum pilot Desenes de min a hores Minuts €€ Nucli mínim encès (BD replicada); la resta s'aixeca
Espera temperada Minuts Segons a min €€€ Tot desplegat a escala reduïda
Actiu-actiu Segons ~0 €€€€ Les dues regions servint
flowchart TB
    subgraph A["Còpia i restauració"]
      A1["Còpies en una altra regió"] -.->|"hores"| A2["Es reconstrueix tot"]
    end
    subgraph B["Llum pilot"]
      B1["Rèplica de BD encesa"] -.->|"minuts"| B2["S aixeca la resta"]
    end
    subgraph C["Espera temperada"]
      C1["Tot desplegat, escala mínima"] -.->|"minuts"| C2["S escala"]
    end
    subgraph D["Actiu-actiu"]
      D1["Les dues regions servint"] -.->|"segons"| D2["El balancejador redirigeix"]
    end

Com es tria, i no és per la tecnologia: es calcula el cost d'una hora de caiguda i es compara amb el cost mensual de cada estratègia. Per a AlpinaShop, una hora de caiguda en un dia normal són uns 100 € de comandes; en campanya, potser 800 €. Amb aquests números, actiu-actiu —diversos centenars d'euros al mes, tots els mesos— no es justifica.

  1. El pla de recuperació d'AlpinaShop

Escrit, amb responsables, i desat també fora de Google Cloud (perquè si el desastre és d'accés a Google Cloud, el pla ha de ser accessible).

Escenaris i resposta

# Escenari Probabilitat RTO objectiu RPO objectiu Estratègia
1 Fallada d'una zona Mitjana 2 min 0 Automàtic (HA de Cloud SQL, Cloud Run multizona)
2 Desplegament defectuós Alta 5 min 0 Reversió de revisió (07-02)
3 Esborrat accidental de dades Mitjana 1 h 5 min Recuperació a un punt en el temps
4 Corrupció lògica detectada tard Baixa 4 h Fins a 24 h Restaurar còpia + reprocessar des de Pub/Sub
5 Caiguda de la regió europe-west1 Baixa 4 h 5 min Llum pilot a europe-southwest1
6 Ransomware / compte compromès Baixa 8 h 1 h Còpies immutables + reconstruir amb Terraform
7 Pèrdua de l'enllaç amb Sabadell Mitjana 1 h 0 Pub/Sub reté; la botiga continua (DA-004)

L'escenari 2 és el més probable amb diferència, i el seu RTO de 5 minuts ja està resolt des de 07-02. És un bon recordatori que la feina de fiabilitat més rendible ja estava feta abans d'aquesta lliçó.

Configuració de còpies de seguretat

resource "google_sql_database_instance" "pedidos" {
  name             = "alpinashop-pedidos"
  database_version = "POSTGRES_15"
  region           = "europe-west1"

  settings {
    tier              = "db-custom-2-7680"
    availability_type = "REGIONAL"          # HA: replica sincrona en una altra zona

    backup_configuration {
      enabled                        = true
      start_time                     = "02:00"
      location                       = "eu"          # MULTIREGIO: sobreviu a la regio
      point_in_time_recovery_enabled = true          # <<< el mes important
      transaction_log_retention_days = 7             # finestra de recuperacio puntual

      backup_retention_settings {
        retained_backups = 30
        retention_unit   = "COUNT"
      }
    }

    maintenance_window {
      day          = 2      # dimarts
      hour         = 4
      update_track = "stable"
    }
  }

  deletion_protection = true                 # 06-07
}

Les quatre línies que fan la feina:

  • point_in_time_recovery_enabled = true és el que converteix un RPO de 24 hores en un RPO de minuts. Sense ella, només es pot restaurar al moment de l'última còpia; amb ella, a qualsevol instant dins de la finestra de registres de transaccions.
  • location = "eu" desa les còpies fora de la regió. Còpies a la mateixa regió que mor amb la regió no són còpies: són una il·lusió.
  • transaction_log_retention_days = 7 defineix la finestra de recuperació puntual. Set dies cobreixen el cas «algú va esborrar una cosa el dilluns i ens en vam adonar el dijous».
  • deletion_protection impedeix l'esborrat accidental de la instància sencera.

Exportació de configuració

Les dades no són l'única cosa que cal poder reconstruir:

Què Com On
Infraestructura Terraform a alpinashop-infra GitHub + backend a alpinashop-terraform-estado
Secrets Secret Manager amb replicació automàtica Gestionat per Google
Imatges de contenidor Artifact Registry Amb rèplica
Polítiques d'IAM Exportació periòdica de Cloud Asset Inventory (07-07) Bucket en una altra regió
Taulers i alertes JSON versionat (06-04) Git
Regles de Cloud Armor Exportació a YAML (03-05) Git
El pla de DR mateix Document Fora de GCP: Drive, paper, gestor de contrasenyes

  1. Còpies que no s'han provat no existeixen

Aquí es paga el deute que 07-04 va marcar com a crític.

Una còpia de seguretat no provada no és una còpia de seguretat: és una esperança amb nom tècnic.

Els motius pels quals una restauració falla el dia que cal, tots reals i tots freqüents:

Motiu Com es descobreix
La còpia era buida o incompleta Només restaurant
Faltaven permisos per restaurar Només intentant-ho
El procés triga 6 h i el RTO era 1 h Només cronometrant-lo
L'esquema restaurat no és compatible amb l'aplicació actual Només arrencant l'aplicació contra ell
Ningú no sap fer-ho i la persona que en sabia se'n va anar Només el dia del desastre
La còpia era a la mateixa regió que va morir El dia del desastre

L'assaig de restauració d'alpinashop-pedidos

Procediment complet, executat trimestralment per la Marta, i cronometrat.

Pas 1 — Preparar i anotar l'hora d'inici.

INICIO=$(date +%s)
echo "Assaig de restauracio iniciat: $(date)"

gcloud sql backups list --instance=alpinashop-pedidos --project=alpinashop-prod \
  --format="table(id, windowStartTime, status, type, location)" --limit=5

Pas 2 — Restaurar a una instància NOVA.

Mai sobre la de producció. La restauració crea una instància a part, cosa que fa l'assaig totalment segur:

# Recuperacio a un punt en el temps: fa 2 hores
MOMENTO=$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%S.000Z)

gcloud sql instances clone alpinashop-pedidos alpinashop-pedidos-ensayo \
  --point-in-time="$MOMENTO" \
  --project=alpinashop-prod

Pas 3 — Verificar la integritat, que és el que gairebé ningú no fa.

Restaurar sense verificar només demostra que la comanda funciona:

gcloud sql connect alpinashop-pedidos-ensayo --user=postgres --database=tienda
-- 1. Les taules existeixen i tenen files
SELECT schemaname, relname, n_live_tup
FROM pg_stat_user_tables ORDER BY n_live_tup DESC LIMIT 10;

-- 2. L ultima comanda es coherent amb el moment restaurat
SELECT MAX(fecha_pedido) AS ultima_comanda, COUNT(*) AS total FROM pedidos;

-- 3. Integritat referencial: no hi ha linies orfes
SELECT COUNT(*) AS linies_orfes
FROM lineas_pedido lp
LEFT JOIN pedidos p ON p.id = lp.pedido_id
WHERE p.id IS NULL;

-- 4. Comparar totals amb produccio (han de quadrar fins al moment restaurat)
SELECT DATE(fecha_pedido) AS dia, COUNT(*) AS comandes, SUM(importe_total) AS import
FROM pedidos
WHERE fecha_pedido >= NOW() - INTERVAL '7 days'
GROUP BY dia ORDER BY dia;

Pas 4 — Arrencar l'aplicació contra la còpia restaurada.

Aquest és el pas que descobreix els problemes de debò:

gcloud run deploy alpinashop-web-ensayo \
  --image=europe-west1-docker.pkg.dev/alpinashop-prod/alpinashop/catalogo:a3f91c2 \
  --region=europe-west1 --no-traffic --tag=ensayo \
  --add-cloudsql-instances=alpinashop-prod:europe-west1:alpinashop-pedidos-ensayo \
  --set-env-vars=ENTORNO=ensayo

URL=$(gcloud run services describe alpinashop-web-ensayo --region=europe-west1 \
      --format='value(status.traffic[?tag==`ensayo`].url)')
curl -sf -H "Authorization: Bearer $(gcloud auth print-identity-token)" "$URL/salud"
curl -sf -H "Authorization: Bearer $(gcloud auth print-identity-token)" "$URL/producto/1"

Pas 5 — Cronometrar, netejar i documentar.

FIN=$(date +%s)
echo "Temps total de restauracio: $(( (FIN - INICIO) / 60 )) minuts"

gcloud sql instances delete alpinashop-pedidos-ensayo --quiet
gcloud run services delete alpinashop-web-ensayo --region=europe-west1 --quiet

El resultat del primer assaig real

I aquí hi ha el valor de l'exercici, perquè el primer assaig no surt mai bé:

Troballa Impacte
La restauració va trigar 47 minuts, no els «uns minuts» que tothom suposava El RTO d'1 hora per a l'escenari 3 és just, no còmode
L'usuari de l'aplicació no existia a la instància clonada L'aplicació no va arrencar fins a crear-lo a mà. Hauria costat 20 minuts en un incident real
Faltava roles/cloudsql.admin al grup gcp-infra@ per clonar La Marta se'l va haver de demanar a si mateixa per una altra via
Les seqüències de PostgreSQL van quedar desincronitzades Els primers inserts haurien fallat per clau duplicada
Ningú no havia documentat el procediment La Marta el va reconstruir sobre la marxa

Cinc problemes que haurien convertit una restauració d'1 hora en una de 3 hores, amb la botiga aturada i tothom mirant. I cap no era detectable sense assajar.

Les cinc correccions es van escriure en un guió d'actuació (runbook), es va automatitzar la creació de l'usuari i el reajustament de seqüències en un script de post-restauració, i es va ajustar el RTO de l'escenari 3 d'1 hora a 90 minuts, que és un número honest.

L'assaig no serveix per confirmar que les còpies funcionen. Serveix per trobar els cinc problemes que impedeixen que funcionin.

  1. Pràctica operativa: guàrdies, runbooks i post mortem

Guàrdies sostenibles per a un equip de tres persones

La fiabilitat la sostenen persones, i un equip cremat és el risc de fiabilitat més gran que existeix. Amb tres persones tècniques, una guàrdia 24×7 tradicional és inviable: serien dues setmanes de cada tres de disponibilitat permanent.

El model realista d'AlpinaShop:

Franja Cobertura Què s'atén
DL-DV 8:00-20:00 Rotació setmanal d'una persona Tot
DL-DV 20:00-8:00 Només alertes de pàgina (taxa 14,4×) Caiguda total o pèrdua de diners
Caps de setmana Ídem Ídem
Campanya de tardor Dues persones de guàrdia, amb compensació acordada Tot

I les regles que ho fan sostenible, que importen tant com el calendari:

  1. Només desperta el que cal arreglar ja. Si una alerta arriba de matinada i l'acció és «mirar-ho demà», aquesta alerta no ha de sonar de nit. És la regla que més redueix el desgast.
  2. Compensació real. Hores o diners, acordat per escrit. La guàrdia no remunerada dura fins que algú se'n cansa.
  3. Màxim dues nits interrompudes seguides. Si passa, es relleva.
  4. Tota alerta nocturna es revisa l'endemà: era necessària? Si no, s'ajusta o s'elimina.
  5. Menys alertes és millor. La fatiga d'alertes (06-04) és un problema de fiabilitat: una persona que ha après a ignorar el mòbil ignorarà també la que importava.

Runbooks enllaçats des de les alertes

Un runbook és el document que diu què fer davant d'una alerta concreta. Viu a alpinashop-infra/runbooks/ i s'enllaça des del camp documentation de la política d'alerta.

# Runbook: errors 5xx al catàleg

**Alerta:** SLO catàleg - consum ràpid del pressupost (14.4x)
**Gravetat:** Pàgina (desperta)
**Última revisió:** 2026-08-05 · **Provat en simulacre:** sí

## 1. Confirmar (2 min)
- Tauler de campanya: [enllaç directe]
- Afecta totes les rutes o una? Filtrar per `url_map_name` i ruta.
- Comprovació externa: `curl -sS -o /dev/null -w '%{http_code}' https://alpinashop.example/salud`

## 2. Causa més probable: un desplegament recent (3 min)

gcloud run revisions list --service=alpinashop-web --region=europe-west1 --limit=5

Si la revisió activa es va desplegar fa menys de 2 hores, **REVERTIR PRIMER I DIAGNOSTICAR DESPRÉS**:

gcloud run services update-traffic alpinashop-web --region=europe-west1
--to-revisions=REVISION_ANTERIOR=100

## 3. Si no va ser un desplegament
| Símptoma | Causa probable | Acció |
| --- | --- | --- |
| `Memory limit exceeded` als registres | Memòria insuficient | Pujar `--memory`, revisar concurrència |
| Errors de connexió a BD | Pool esgotat o Cloud SQL degradat | Tauler de Cloud SQL; commutació |
| Latència alta sense errors | Dependència lenta | Cloud Trace, buscar el tram lent |
| 502 del balancejador | Cloud Run no respon | `gcloud run services describe` |
| Només des d'una geografia | Cloud Armor | Revisar regles recents (03-05) |

## 4. Escalar
Si en 30 min no hi ha diagnòstic: avisar en Dani (backend) i obrir cas amb suport de Google.

## 5. Tancar
- Comprovar que el SLI es recupera.
- Anotar inici, fi i pressupost consumit.
- **Post mortem obligatori si el tall va superar els 15 minuts.**

Les tres propietats d'un runbook útil: comença per confirmar (no per diagnosticar), posa primer la causa més probable —un desplegament recent, en el 70 % dels casos—, i diu explícitament quan escalar, perquè la persona de guàrdia no es quedi sola donant voltes a les 4 de la matinada.

I una quarta que es veu a la capçalera: «Provat en simulacre: sí». Un runbook que ningú no ha executat mai és una hipòtesi.

Post mortem sense culpables

Després d'un incident significatiu, un document amb estructura fixa:

Secció Contingut
Resum Què va passar, en dues frases
Impacte Durada, usuaris afectats, pressupost d'error consumit, comandes perdudes
Cronologia Amb hores: detecció, diagnòstic, mitigació, resolució
Causa arrel Els «cinc perquès», fins a arribar a una causa sistèmica
Què va anar bé Sí, aquesta secció importa
Accions Amb responsable i data. Concretes, no «tenir més cura»

Sense culpables (blameless) no significa que no hi hagi responsabilitats. Significa una cosa molt concreta:

Si la resposta a «per què va passar» és el nom d'una persona, l'anàlisi s'ha aturat massa aviat.

Exemple aplicat:

❌ "En Dani va desplegar sense provar."

✅ "Un canvi va arribar a produccio sense cobertura de proves per al cas d un carret buit.
   Per que? El pipeline no exigeix llindar de cobertura.
   Per que? Es va configurar rapid a 06-01 i va quedar pendent.
   Per que no es va detectar al canari? El canari va durar 10 min amb transit baix (07-02).
   ACCIONS:
   1. Llindar de cobertura del 70% al pipeline (Dani, 2026-08-20)
   2. Canari minim de 30 min o 500 peticions, el que sigui mes gran (Marta, 2026-08-15)
   3. Prova de regressio del carret buit (Dani, 2026-08-12)"

La raó pràctica —no moral— de l'absència de culpa: en una cultura on es busca el culpable, la gent amaga els errors, i els errors amagats no es corregeixen. En una cultura sense culpa, la gent explica el que va passar i el sistema millora. És una decisió d'eficàcia.

El registre d'incidents

Tots els post mortem en un lloc, amb etiquetes. Al cap d'un any permet veure patrons que cap incident aïllat no revela: «el 60 % dels nostres incidents són desplegaments» és una frase que canvia les prioritats d'un equip sencer.

  1. Proves de caos a escala raonable

Enginyeria del caos és provocar fallades a propòsit per verificar que el sistema les tolera. Netflix va popularitzar la idea matant instàncies en producció; una pime no necessita això, però sí que necessita alguna cosa.

Tot sistema té proves de recuperació de desastres. Uns les fan a propòsit; altres esperen que passin.

Els experiments d'AlpinaShop, ordenats de menys a més agressiu:

# Experiment On Hipòtesi Freqüència
1 Aturar el recomanador dev La pàgina se serveix amb els més venuts Mensual
2 Afegir 3 s de latència a Cloud SQL dev El temps d'espera talla i surt un 503 net, no una cascada Mensual
3 Commutació manual de Cloud SQL prod, finestra planificada Tall < 2 min, l'aplicació reconnecta sola Trimestral
4 Restauració de còpia prod → instància nova RTO < 90 min Trimestral
5 Tallar el túnel VPN a Sabadell prod La botiga continua; Pub/Sub reté Semestral
6 Simulacre de sala: «cau europe-west1» Paper El pla és executable Anual
7 Revertir un desplegament a cegues prod Qualsevol de l'equip ho fa en < 5 min Trimestral

Les regles que fan això responsable en lloc de temerari:

  1. Hipòtesi escrita abans. Si no saps què esperes, no és un experiment: és trencar coses.
  2. Començar en desenvolupament.
  3. Radi d'explosió petit, i ampliar-lo només quan l'anterior va sortir bé.
  4. Un botó d'aturada i la capacitat d'avortar en qualsevol moment.
  5. Mai en campanya. Ni el divendres a la tarda.
  6. Avisar l'equip. El caos no anunciat amb guàrdies reals és una broma pesada.

L'experiment 3 mereix un comentari, perquè espanta i no hauria de fer-ho: la commutació de Cloud SQL passarà algun dia, triada per Google i en el pitjor moment. Provocar-la un dimarts a les 10 del matí, amb tothom davant i amb la possibilitat d'arreglar-ho, és infinitament millor que descobrir un divendres de campanya que l'aplicació no reconnecta perquè li falta pool_pre_ping (07-02).

I l'experiment 7 és el més subestimat: que només la Marta sàpiga revertir un desplegament és un punt únic de fallada humana. Que en Dani i la Lucía ho hagin fet almenys un cop cadascun val més que qualsevol redundància d'infraestructura.

Errors Habituals i Consells

  • Posar com a objectiu «que no caigui mai». No es pot complir, mesurar ni fer servir per decidir. Tria un número i paga'l.
  • Confondre SLO i SLA. El SLO sempre més exigent que el SLA, amb marge folgat.
  • Mesurar el SLI des del servidor. Es mesura des d'on és l'usuari: el balancejador o una comprovació externa.
  • Definir el SLI de latència com «el p95 < X». És «la proporció de peticions < X». Un percentil no es combina amb un pressupost d'error.
  • Comptar els rebuigs legítims de la passarel·la com a errors. El SLI passaria a dependre de la solvència dels clients.
  • SLO sobre CPU o memòria. No són experiència d'usuari. Es monitoren, no es converteixen en objectiu.
  • Finestres de mes natural. Creen l'incentiu d'esperar al dia 1 per desplegar el que és arriscat. Finestra mòbil de 28 dies.
  • Un SLO sense política associada. Si en incomplir-lo no canvia res, és un adorn.
  • Congelar per esgotar el pressupost quan la culpa va ser de la plataforma. La política perd credibilitat i l'equip deixa de respectar-la.
  • Perseguir un pressupost consumit al 5 %. Senyal d'excés de conservadorisme o d'un SLO massa lax.
  • Alertes de llindar fix sobre errors. O són sorolloses o no detecten degradacions lentes. Fes servir taxa de consum amb dues finestres.
  • Deixar buit el camp documentation de l'alerta. A les 3 de la matinada, aquest text val més que la mateixa alerta.
  • Crides de xarxa sense temps d'espera. És el mecanisme pel qual una fallada petita es converteix en caiguda total.
  • Reintents sense jitter. Mil clients reintentant alhora tomben el servei just quan es recuperava.
  • Reintentar errors permanents. Un 403 no millora per insistir.
  • Tractar una dependència tova com a dura. El recomanador no ha de poder tombar la fitxa de producte.
  • Degradar sense marcar-ho als registres. El servei funciona a mitges durant setmanes i el SLI continua verd.
  • Còpies a la mateixa regió. No són còpies: són una il·lusió.
  • Creure que HA protegeix d'un esborrat accidental. El replica a l'instant a la instància en espera.
  • No provar la restauració. És el deute més perillós perquè és invisible fins al dia que importa.
  • Guàrdies sense compensació. Duren fins que algú se'n cansa, i llavors et quedes sense guàrdies i sense persona.
  • Buscar culpables en un post mortem. Els errors s'amaguen, i els errors amagats no es corregeixen.
  • Consell: comença per un SLO de disponibilitat i res més. Quatre SLO des del primer dia és massa. Un de bo val més que quatre de copiats.
  • Consell: la reversió ràpida val més que la redundància. La causa més freqüent de caiguda és un canvi, no una fallada de maquinari.
  • Consell: que tot l'equip sàpiga revertir un desplegament. És la redundància més barata que existeix.
  • Consell: cronometra l'assaig de restauració. El número real sempre sorprèn, i és el que dona un RTO honest.

Exercicis

Exercici 1 — Definir el SLO d'un servei nou

En Dani llançarà alpinashop-buscador (el servei de l'exercici de 07-02). Defineix el seu SLI i el seu SLO complets:

  1. Quins esdeveniments són vàlids i quins bons, resolent explícitament almenys sis casos límit.
  2. El SLO i la finestra, justificats.
  3. El pressupost d'error resultant en minuts i en peticions.
  4. Les dues alertes de taxa de consum amb els seus llindars.
  5. La política de què es fa en cada estat del pressupost.

Considera que el cercador és una dependència tova del catàleg: si falla, es pot mostrar un enllaç a la navegació per categories.

Exercici 2 — Dissenyar un pla de recuperació amb números

AlpinaShop llança AlpinaPro, un servei de subscripció per a clubs de muntanya: quota mensual, contingut exclusiu i reserves de material. Dades:

  • 400 subscriptors, 25 €/mes cadascun: 10.000 €/mes d'ingrés recurrent.
  • Base de dades pròpia alpinapro-suscripciones (PostgreSQL).
  • Integració amb la passarel·la per a cobraments recurrents el dia 1 de cada mes.
  • Els clubs reserven material amb 48 h d'antelació; una reserva perduda és una queixa seriosa.
  • Direcció diu que «no es pot caure mai», però el pressupost addicional és de 150 €/mes.

Dissenya: els SLI i SLO del servei, el RTO i RPO justificats amb números de negoci, l'estratègia de recuperació triada dins del pressupost, i la configuració concreta de còpies. Explica què li respons a direcció sobre el «no es pot caure mai».

Exercici 3 — Analitzar un incident i escriure el post mortem

Els fets. Dimarts, 18:42. Salta l'alerta de consum ràpid. Cronologia reconstruïda:

  • 18:30 — Cloud Build desplega la revisió alpinashop-web-00051 amb canari al 10 %. Les proves de fum passen.
  • 18:38 — La Marta aprova el pas al 100 % (tenia pressa, marxava a les 18:45).
  • 18:42 — Alerta: taxa de consum 22×. Errors 500 en el 35 % de les peticions.
  • 18:44 — La Marta veu l'alerta ja al cotxe. No pot actuar.
  • 18:51 — En Dani, avisat per la Marta, es connecta i reverteix a la revisió 00050.
  • 18:53 — Els errors desapareixen.
  • Impacte: 11 minuts, ~230 peticions fallides, 4 comandes perdudes, 27 % del pressupost mensual consumit.
  • Causa tècnica: la revisió nova llegia una variable d'entorn TARIFA_ENVIO que només estava definida a alpinashop-dev.

Escriu el post mortem complet seguint l'estructura de l'apartat 12, amb els cinc perquès portats fins a una causa sistèmica, i almenys cinc accions concretes amb responsable. Assenyala també què va fer bé l'equip.

Solucions

Solució 1 — Definir el SLO d'alpinashop-buscador

1. Definició del SLI.

SLI de disponibilitat = peticions de cerca amb resposta útil / peticions de cerca vàlides

Casos límit, resolts i anotats:

Cas Vàlid? Bo? Raó
200 amb 12 resultats
200 amb 0 resultats El sistema va funcionar; simplement no hi ha «piolet rosa fúcsia»
400 consulta malformada El sistema va rebutjar correctament una entrada invàlida
500 excepció interna No Fallada nostra
503 Elasticsearch caigut No És la nostra dependència; l'usuari no ho distingeix
429 límit de taxa a un rastrejador No No és un usuari a qui servir
Petició amb User-Agent de bot No No és experiència d'usuari
Temps d'espera esgotat (> 3 s) No Pitjor que un error: l'usuari espera i no rep res
Petició de la sonda de salut No No és trànsit real

El cas «0 resultats» és el que més discussió genera i és important encertar-lo. Comptar-lo com a dolent faria que el SLI mesurés la qualitat del catàleg en lloc de la fiabilitat del servei, i baixaria cada vegada que algú busca alguna cosa que no venem. La qualitat de la cerca es mesura a part, amb mètriques de producte —taxa de cerques sense resultat, taxa de clic— que no són un SLO.

SLI de latència = cerques servides en < 500 ms / cerques vàlides

500 ms i no 800 ms com el catàleg, perquè un cercador és interactiu: l'usuari escriu i espera. La percepció de lentitud apareix abans.

2. SLO i finestra.

SLI SLO Finestra Justificació
Disponibilitat 99,5 % 28 dies Menys exigent que el catàleg (99,9 %)
Latència < 500 ms 99 % 28 dies Coherent amb el catàleg

La justificació del 99,5 % és la part interessant de l'exercici. El cercador és una dependència tova: si falla, el catàleg mostra un enllaç a la navegació per categories i el client pot continuar comprant. Un servei tou no necessita el mateix objectiu que un de crític, i posar-li 99,9 % significaria gastar diners i enginyeria en una fiabilitat que no canvia el resultat de negoci.

Aquest és el raonament que cal endur-se: el SLO es deriva de l'impacte de negoci de la fallada, no de l'estima que es tingui al servei.

3. Pressupost d'error.

Amb 99,5 % sobre 28 dies: 0,5 % = 3 h 21 min 36 s.

En peticions: si el cercador rep unes 150.000 cerques vàlides en 28 dies, el pressupost són 750 cerques fallides.

És còmode, i a propòsit: permet desplegar millores del cercador amb freqüència sense que una fallada puntual bloquegi l'equip.

4. Les dues alertes.

Alerta Taxa Finestres Consum a la finestra Canal
Ràpida 14,4× 1 h i 5 min 2 % (≈ 4 min) 🎫 Tiquet, no pàgina
Lenta 6 h i 30 min 5 % (≈ 10 min) 🎫 Tiquet

Cap de les dues no desperta ningú, i aquesta és la conseqüència lògica de ser una dependència tova amb SLO del 99,5 %. Aixecar-se a les 4 de la matinada perquè el cercador falla, quan la botiga continua venent, és exactament el tipus d'alerta que produeix fatiga i fa que s'ignori la que sí que importa.

L'excepció que cal preveure: si el cercador cau i a més el catàleg comença a fallar —perquè el temps d'espera no tallava i es van esgotar els fils—, l'alerta que sonarà és la del catàleg, que sí que és de pàgina. El disseny d'alertes ha de reflectir el disseny de dependències.

5. Política de pressupost.

Consumit Estat Acció
< 60 % 🟢 Desplegament normal
60-90 % 🟡 Es continua desplegant; es revisa la causa del consum a la reunió setmanal
> 90 % 🟠 Només correccions. Revisar si la dependència d'Elasticsearch necessita més fiabilitat
> 100 % 🔴 Avaluar si l'interruptor de circuit del catàleg funciona bé, que és el que realment importa: que la fallada del cercador no arrossegui la botiga

La fila vermella és la clau de l'exercici. Quan un servei tou esgota el seu pressupost, la pregunta prioritària no és «com el faig més fiable» sinó «està ben aïllat?». Un cercador que falla el 2 % del temps sense afectar ningú és un problema menor; un cercador que falla el 0,5 % del temps i cada fallada degrada la fitxa de producte és un problema greu.

Solució 2 — Dissenyar un pla de recuperació per a AlpinaPro

Punt de partida: traduir el negoci a números.

Concepte Càlcul Valor
Ingrés recurrent mensual 400 × 25 € 10.000 €
Ingrés per hora (prorratejat) 10.000 / 730 ~13,70 €/h
Cost real d'una hora de caiguda Vegeu més avall Molt superior a 13,70 €

I aquí hi ha el matís que distingeix una anàlisi bona d'una de superficial: en un servei de subscripció, l'ingrés no es perd per estar caigut. Els 400 subscriptors paguen igual. El que es perd és una altra cosa, i és pitjor:

Impacte real Estimació
Reserves de material perdudes Una queixa seriosa per reserva; risc de baixa
Baixes per mala experiència Una baixa són 25 €/mes × mesos restants de vida del client. Amb 24 mesos de vida mitjana, una baixa són 600 €
Cobraments recurrents fallits el dia 1 Crític: si el sistema cau el dia 1, no es cobra a ningú
Reputació al nínxol Els clubs de muntanya es coneixen entre ells

Conclusió: una caiguda de 4 hores que provoqui 5 baixes costa 3.000 €, no 55 €. L'anàlisi per ingrés prorratejat subestima l'impacte en més d'un ordre de magnitud, i és un error molt comú en serveis de subscripció.

SLI i SLO proposats.

SLI SLO Justificació
Disponibilitat del portal 99,5 % 3 h 22 min al mes. Els clubs no consulten diàriament
Èxit de reserves 99,9 % Una reserva perduda és una queixa. Més exigent
Èxit del cobrament recurrent 99,99 % Crític i concentrat: falla el dia 1 o no falla

El tercer és el més interessant i el que se sol oblidar. El cobrament recurrent no és un servei continu: és un procés que s'executa un dia al mes. Un SLO mensual sobre ell no té sentit; el que cal és un objectiu diferent: «el 99,99 % dels cobraments programats s'executen correctament dins de les 24 hores següents», amb reintents. I un tractament arquitectònic específic: cua amb reintents i DLQ, no una execució única que o funciona o es perd.

RTO i RPO justificats.

Escenari RTO RPO Justificació amb números
Fallada de zona 5 min 0 Automàtic, no costa decidir-ho
Desplegament defectuós 5 min 0 Reversió de revisió
Esborrat de dades 2 h 5 min Perdre reserves és inacceptable. 5 min ≈ 0 reserves perdudes
Caiguda de regió 6 h 15 min Baixa probabilitat; 6 h de caiguda ≈ 3-5 baixes ≈ 2.400 €
Fallada el dia 1 (cobraments) 2 h 0 Es reintenta; el procés ha de ser idempotent

Estratègia dins de 150 €/mes.

Pressupost molt just. Repartiment:

Mesura Cost/mes Què cobreix
Cloud SQL HA regional ~60 € Fallada de zona, automàtic, RPO 0
Recuperació a un punt en el temps + còpies multiregió ~15 € Esborrat accidental amb RPO de 5 min; supervivència a la regió
Rèplica de lectura a europe-southwest1 ~55 € Llum pilot: promocionable en minuts si cau la regió
Uptime checks + alertes ~5 € Detecció
Cloud Run (escala a zero) ~5 € Còmput
Total ~140 €

Estratègia triada: llum pilot. La base de dades replicada a la segona regió és l'única cosa que es manté encesa; el còmput a Cloud Run s'aixeca amb tres comandes (apartat 7) perquè la imatge ja existeix i Cloud Run escala des de zero. RTO estimat: 30-60 minuts, millor que l'objectiu de 6 h.

El que no es fa i per què: actiu-actiu costaria 400-600 €/mes per cobrir un esdeveniment de probabilitat molt baixa, quan el pressupost sencer són 150 €.

Configuració de còpies.

resource "google_sql_database_instance" "alpinapro" {
  name             = "alpinapro-suscripciones"
  database_version = "POSTGRES_15"
  region           = "europe-west1"

  settings {
    tier              = "db-custom-1-3840"
    availability_type = "REGIONAL"

    backup_configuration {
      enabled                        = true
      start_time                     = "03:00"
      location                       = "eu"
      point_in_time_recovery_enabled = true
      transaction_log_retention_days = 7
      backup_retention_settings {
        retained_backups = 30
        retention_unit   = "COUNT"
      }
    }

    # Finestra de manteniment: MAI a prop del dia 1
    maintenance_window {
      day          = 3      # dimecres
      hour         = 4
      update_track = "stable"
    }
  }

  deletion_protection = true
}

resource "google_sql_database_instance" "alpinapro_replica_dr" {
  name                 = "alpinapro-suscripciones-dr-esw1"
  master_instance_name = google_sql_database_instance.alpinapro.name
  region               = "europe-southwest1"          # ALTRA regio
  database_version     = "POSTGRES_15"

  replica_configuration { failover_target = false }

  settings {
    tier              = "db-custom-1-3840"
    availability_type = "ZONAL"        # la replica de DR no necessita HA propia
  }
}

Dues decisions a assenyalar: la finestra de manteniment el dimecres, deliberadament lluny del dia 1 —un manteniment de Google durant el cicle de cobraments seria el pitjor moment possible—, i la rèplica de DR en mode ZONAL, perquè pagar HA per a una rèplica que només es fa servir en un desastre és duplicar el cost d'una assegurança.

Què se li respon a direcció.

«No podem garantir que no caigui mai, i qui ho prometi no diu la veritat. El que sí que podem fer és decidir quant estem disposats que caigui i pagar-ho.

Amb els 150 € al mes que tenim, el compromís és aquest: si falla una màquina o un centre de dades concret, es recupera sol en cinc minuts i sense perdre cap dada. Si algú esborra alguna cosa per error, ho recuperem en dues hores perdent com a màxim cinc minuts de reserves. I si caigués una regió sencera de Google —una cosa molt poc freqüent però possible—, estaríem funcionant en menys d'una hora des de Madrid.

Per al procés de cobrament del dia 1 tenim un tractament a part, perquè és el moment crític del mes: si alguna cosa falla, es reintenta automàticament durant 24 hores i cap cobrament no es perd.

El que no cobrim amb aquest pressupost és la caiguda simultània de dues regions o un problema global de Google. Cobrir-ho costaria entre tres i quatre vegades més, tots els mesos, per a un escenari que potser no passarà mai.

I hi ha una cosa que vull demanar a més del pressupost: provar tot això cada trimestre. Un pla de recuperació que no s'assaja no funciona el dia que cal, i ho sabem perquè ens va passar amb la botiga: la primera vegada que vam provar de restaurar la base de dades vam trobar cinc problemes que haurien triplicat el temps de recuperació.»

Solució 3 — Post mortem de l'incident de les 18:42


Post mortem: errors 500 al catàleg després del desplegament 00051

Data de l'incident: dimarts 2026-08-04, 18:42-18:53 Autora: Marta Ruiz · Revisat per: Dani, Lucía Estat: accions en curs · Classificació: sense culpables

Resum. Un desplegament promocionat al 100 % del trànsit contenia codi que depenia d'una variable d'entorn inexistent en producció, i va provocar errors 500 en el 35 % de les peticions durant 11 minuts.

Impacte.

Mètrica Valor
Durada 11 min (18:42 – 18:53)
Peticions fallides ~230
Comandes perdudes 4 (~260 € d'ingrés)
Pressupost d'error consumit 27 % del mensual en 11 minuts
Estat del SLO després de l'incident 🟡 Groc (58 % consumit)

Cronologia.

Hora Esdeveniment
18:30 Cloud Build desplega 00051, canari al 10 %. Proves de fum ✅
18:38 La Marta aprova el 100 %. Surt de l'oficina a les 18:45
18:42 Alerta de consum ràpid (22×). 35 % d'errors
18:44 La Marta veu l'alerta al cotxe. No pot actuar
18:47 La Marta truca a en Dani
18:51 En Dani reverteix a 00050
18:53 Errors a zero. Detecció: 4 min. Mitigació: 11 min

Causa arrel — els cinc perquès.

  1. Per què van fallar les peticions? El codi llegia os.environ["TARIFA_ENVIO"], que no existia en producció, i llançava KeyError.
  2. Per què no existia? Es va afegir a alpinashop-dev durant el desenvolupament i no es va propagar a producció.
  3. Per què no es va propagar? Les variables d'entorn de producció es gestionen en Terraform, però el desenvolupament les configura amb gcloud a mà. No hi ha cap mecanisme que garanteixi la paritat.
  4. Per què no ho va detectar el canari? Les proves de fum només comproven /salud i /producto/1, i cap d'aquestes rutes no fa servir TARIFA_ENVIO (només la del carret). A més el canari va durar 8 minuts amb trànsit baix.
  5. Per què es va promocionar al 100 % amb aquesta cobertura? Perquè el procés permet aprovar manualment sense cap comprovació automàtica de la revisió candidata, i l'aprovació es va donar amb pressa al final de la jornada.

Causa arrel sistèmica: no existeix verificació automàtica que una revisió candidata tingui tota la seva configuració present a l'entorn de destinació, ni una comprovació objectiva que hagi de passar abans de la promoció al 100 %. L'aprovació manual és l'únic control i depèn de l'estat i la disponibilitat d'una persona.

Què va anar bé — i aquesta secció importa tant com l'anterior:

  • L'alerta va funcionar: 4 minuts des del primer error fins a la notificació. Exactament el que 06-04 i les alertes de taxa de consum prometien.
  • La reversió va ser trivial: una comanda, 2 minuts, sense reconstruir res. La feina de 07-02 es va pagar sola en aquest incident.
  • L'escalat va funcionar: la Marta, sense poder actuar, va avisar en Dani en 3 minuts en lloc d'intentar arreglar-ho conduint.
  • El pressupost d'error va fer la seva feina: va convertir «11 minuts, no va ser per tant» en «el 27 % del pressupost mensual», que és una dada que obliga a actuar.
  • Ningú no va buscar culpables durant l'incident. Es va arreglar primer i es va analitzar després.

Accions.

# Acció Responsable Data Prevé
1 Totes les variables d'entorn de tots els entorns gestionades en Terraform. Prohibit gcloud run services update --set-env-vars fora del pipeline Marta 2026-08-15 La divergència entre entorns (perquès 2 i 3)
2 Pas de compilació que compara les variables d'entorn declarades al codi amb les definides a l'entorn de destinació i falla si en falta alguna Dani 2026-08-20 La causa directa
3 Proves de fum ampliades: /salud, /producto/1, /carrito, /api/categorias, cobrint tota ruta que llegeixi configuració Dani 2026-08-12 El perquè 4
4 Canari mínim de 30 minuts o 500 peticions, el que sigui més gran, amb verificació automàtica de taxa d'error abans d'habilitar l'aprovació Marta 2026-08-18 El perquè 5
5 Finestra de desplegament: no es promociona a producció després de les 17:00 ni els divendres a la tarda Equip Immediat Aprovar amb pressa al final del dia
6 Que la Lucía sàpiga revertir un desplegament. Pràctica a la propera prova de caos Marta 2026-09-01 El punt únic de fallada humana
7 Afegir aquest cas al registre d'incidents amb l'etiqueta configuracion-divergente Marta Immediat Veure patrons

Nota sobre l'acció 5. Podria semblar una regla burocràtica que alenteix l'equip, i val la pena justificar-la: no es prohibeix desplegar a la tarda —el canari pot córrer tota la nit al 10 %— sinó promocionar al 100 % quan no queda ningú disponible per reaccionar. La restricció no és sobre el risc del canvi, sinó sobre la capacitat de resposta. Aquesta distinció és la que fa que l'equip accepti la regla en lloc de saltar-se-la.

El que aquest incident NO va ser. No va ser un error d'en Dani per escriure codi que llegeix una variable, ni de la Marta per aprovar amb pressa. Va ser un sistema que permetia promocionar al 100 % del trànsit un canvi la configuració del qual ningú no havia verificat, en un moment en què ningú no podia reaccionar. Qualsevol dels tres hauria fet el mateix, i per això les set accions són sobre el sistema i cap sobre les persones.


Conclusió

AlpinaShop ha deixat d'aspirar a «que no caigui mai» i ha començat a gestionar la fiabilitat amb números.

Saps que cada nou multiplica el cost i has fet el càlcul que gairebé ningú no fa: passar de 99,9 % a 99,99 % estalviaria a AlpinaShop uns 70 € al mes en comandes i li costaria diversos centenars. Dir «99,99 % seria una mala inversió» amb la taula davant és una de les aportacions més valuoses d'un enginyer, i requereix haver calculat també els dos matisos: que no tot el temps caigut val igual i que hi ha costos que no són comandes perdudes.

Distingeixes SLI, SLO i SLA amb precisió, amb les tres regles que eviten gairebé tots els errors —el SLO sempre més exigent que el SLA, el SLI mesurat des d'on és l'usuari, i un SLO sense conseqüència no és un objectiu— i amb la definició operativa que resol els dubtes: esdeveniments bons / esdeveniments vàlids, on la feina de debò és decidir què és cada cosa.

Has triat quatre SLI recorrent el viatge del client —disponibilitat, latència, èxit del pagament i frescor analítica— resolent per escrit els casos límit: el 404 és bo, el 429 a un atacant ni tan sols és vàlid, i un rebuig per fons insuficients no és una fallada nostra, decisió que cal prendre al codi instrumentant rechazado_legitimo davant d'error_tecnico. I saps per què no hi ha SLI de CPU: perquè a cap client no li importa.

Domines el pressupost d'error: la taula de nous traduïda a minuts, la finestra mòbil de 28 dies que evita l'incentiu pervers del mes natural, i sobretot per a què serveix de debò — resoldre amb un número la tensió entre desplegar ràpid i no trencar res, amb una política de quatre estats i la clàusula de credibilitat de no congelar quan la fallada va ser de la plataforma. Amb l'ús que gairebé ningú no fa: un pressupost consumit al 5 % és tan mal senyal com un d'esgotat.

Saps crear SLO a Cloud Monitoring des de Terraform, distingir SLI basats en peticions de basats en finestres, i muntar alertes de taxa de consum amb dos llindars i dues finestres cadascun — la llarga per evitar falses alarmes, la curta perquè l'alerta s'apagui quan el problema s'arregla. I saps que el camp documentation, que gairebé tothom deixa buit, val més a les 3 de la matinada que la mateixa alerta.

Tens les arquitectures d'alta disponibilitat per nivells amb la taula component a component d'AlpinaShop, l'observació que bona part de la plataforma ja és multiregió sense esforç perquè els serveis globals de Google ho són, i les dues peces que sí que són regionals i crítiques. Saps exactament què compres amb l'HA de Cloud SQL —replicació síncrona de disc, una altra zona, commutació en 1-2 minuts— i què no cobreix: un DELETE sense WHERE es replica a l'instant. I saps que Cloud Run multiregió són tres comandes, però que no s'activa perquè la disponibilitat la determina el component menys disponible.

Dissenyes contra els modes de fallada: dependències dures i toves —amb la fallada de disseny més comuna, tractar-ne una de tova com a dura—, temps d'espera en tota crida de xarxa amb la regla que l'intern sempre sigui menor que l'extern, reintents amb retrocés exponencial i jitter per no crear ones sincronitzades, interruptors de circuit, degradació elegant en tres nivells amb la marca "degradado": true sense la qual la degradació es torna silenciosa, i limitació de càrrega amb descàrrega per prioritat.

Saps que alta disponibilitat i recuperació de desastres no són el mateix, i la dada que reordena prioritats: la causa més freqüent de pèrdua de dades és algú executant alguna cosa a l'entorn equivocat, i contra això l'HA no serveix perquè replica l'error. Manegues RTO i RPO i les quatre estratègies amb el seu cost, triant per comparació amb el cost real d'una hora de caiguda.

Tens el pla d'AlpinaShop amb set escenaris —on el més probable, un desplegament defectuós, ja estava resolt des de 07-02—, la configuració de còpies amb les quatre línies que fan la feina (point_in_time_recovery, location = "eu", retenció de registres i deletion_protection) i l'exportació de configuració, inclòs el pla de DR desat fora de Google Cloud.

I has pagat el deute crític de 07-04: l'assaig de restauració, cronometrat, amb verificació d'integritat i amb l'aplicació arrencada contra la còpia. Amb el resultat que importa: cinc problemes trobats —47 minuts en lloc d'«uns minuts», l'usuari de l'aplicació inexistent, permisos que faltaven, seqüències desincronitzades i cap procediment escrit— que haurien triplicat el temps de recuperació real. L'assaig no confirma que les còpies funcionen: troba els cinc problemes que impedeixen que funcionin.

Finalment, tens la pràctica operativa: guàrdies sostenibles per a tres persones amb la regla que més desgast evita —si l'acció és «mirar-ho demà», aquesta alerta no sona de nit—, runbooks enllaçats des de les alertes que comencen per la causa més probable i diuen quan escalar, post mortem sense culpables amb la raó pràctica i no moral d'aquesta cultura, i proves de caos a escala raonable amb les seves sis regles, on l'experiment més subestimat és que tot l'equip sàpiga revertir un desplegament.

Tot el d'aquesta lliçó funciona perquè hi ha tres persones que ho sostenen. I aquí hi ha el límit: els SLO viuen en un tauler que algú manté, la política de pressupost d'error es respecta perquè l'equip la va acordar, els runbooks s'actualitzen perquè la Marta se'n recorda, i res no impedeix tècnicament que demà algú creï una VM amb IP pública en una regió dels Estats Units, o desactivi un registre d'auditoria, o esborri per error un projecte sencer.

Això ja no és fiabilitat: és govern. I és l'única cosa que queda per construir. L'organització d'AlpinaShop té avui cinc projectes, tres carpetes, desenes de comptes de servei, quatre repositoris, un perímetre a mitges i cap política que governi el conjunt — més una llista de deutes de 07-04 que comença per «polítiques d'organització sense aplicar» i «registres d'auditoria sense retenció immutable».

L'última lliçó del mòdul tanca el cercle: estructura de l'organització, polítiques que s'hereten i s'apliquen soles, inventari de tot el que existeix, auditoria de debò amb retenció immutable, i zones d'aterratge desplegades amb Terraform — perquè el que s'ha construït seguint AlpinaShop no depengui que tres persones se'n recordin.

Curs de Google Cloud Platform (GCP)

Mòdul 1: Introducció a Google Cloud Platform

Mòdul 2: Serveis principals de GCP

Mòdul 3: Xarxes i seguretat

Mòdul 4: Dades i anàlisi

Mòdul 5: Aprenentatge automàtic i IA

Mòdul 6: DevOps i monitoratge

Mòdul 7: Temes avançats de GCP

Mòdul 8: Projecte final

© Copyright 2026. Tots els drets reservats