Tot el que hem construït fins ara té una data de posada en marxa. El que ve després no en té: és el dia a dia de qui manté la plataforma viva, i és exactament el que cap tutorial no explica. Els tutorials acaben quan el pod passa a Running. La realitat comença aquí.
La realitat són les tres de la matinada d'un dissabte amb una alerta al mòbil. És la reunió en què algú de finances pregunta per què la factura del núvol ha pujat un 60 % en quatre mesos. És el desenvolupador que vol desplegar un divendres a les sis de la tarda. És la conversa incòmoda sobre si es pot prometre un 99,99 % de disponibilitat o només un 99,9 %. És l'actualització del clúster que cal fer abans que la versió perdi el suport, i que ningú no vol tocar perquè funciona.
Aquesta lliçó tanca el mòdul amb aquesta part: gestió d'incidències, runbooks, pressupost d'error com a eina de decisió, gestió del canvi, planificació de capacitat, costos i calendari de manteniment. I tanca també el recorregut complet de Rutas Norte.
Advertiment de compliment normatiu. Els procediments de gestió d'incidències descrits aquí afecten la continuïtat del servei i, quan una incidència implica accés indegut, pèrdua o exposició de dades personals, activen obligacions de notificació amb terminis legals estrictes. La classificació de severitats, els canals de comunicació i les plantilles d'anàlisi posterior han de ser revisats pel responsable de compliment normatiu de l'organització, que ha de determinar a més en quins casos una incidència constitueix una violació de seguretat notificable.
Contingut
- Gestió d'incidències
- Runbooks
- SLI, SLO i pressupost d'error
- Gestió del canvi
- Planificació de capacitat per al pont de maig
- Costos
- Calendari de manteniment
- La maduresa de l'equip
- Gestió d'incidències
1.1. Nivells de severitat
La severitat no la determina com d'alarmant soni el problema, sinó l'impacte en l'usuari i en el negoci. Definir-ho per endavant evita la discussió de "això és greu?" justament quan no hi ha temps per tenir-la.
| Sev | Definició | Exemples a Rutas Norte | Resposta | Comunicació |
|---|---|---|---|---|
| 1 | Servei caigut o inutilitzable per a la majoria d'usuaris; o pèrdua/exposició de dades | www.rutasnorte.example no respon; postgres-reserves sense primari; no es pot comprar cap bitllet |
Immediata, 24×7. Es desperta qui calgui | Client + direcció, cada 30 min |
| 2 | Funcionalitat crítica degradada, o parcialment caiguda | Els pagaments fallen en el 30 % dels intents; latència p95 per damunt de 2 s; pre bloquejat en plena publicació del pont |
Immediata en horari ampliat (7:00-24:00) | Interna + direcció |
| 3 | Funcionalitat no crítica afectada; sense impacte directe en la venda | informes-ocupacio falla dues nits seguides; Grafana inaccessible; una rèplica de PostgreSQL caiguda amb les altres dues sanes |
Següent dia laborable | Interna |
| 4 | Molèstia o risc latent; sense impacte actual | Certificat que caduca d'aquí a 20 dies; disc al 70 %; alerta sorollosa que cal ajustar | Es planifica com a tasca | Cap |
Dues regles que eviten molt de dany:
- Davant del dubte, es puja la severitat. Baixar-la quan s'entén el problema és barat. Descobrir al cap de dues hores que era una sev 1 tractada com a sev 3 és car.
- Qualsevol pot declarar una incidència. No cal permís ni jerarquia. Un fals positiu costa vint minuts; una incidència real no declarada costa hores.
1.2. Els rols durant una incidència
En incidències de severitat 1 i 2 se separen explícitament tres rols. Amb equips petits una persona en pot dur dos, però mai no es combinen comandament i tècnic.
| Rol | Què fa | Què NO fa |
|---|---|---|
| Comandament d'incidència | Coordina, decideix, manté la cronologia, decideix quan escalar i quan declarar resolt | No toca el teclat. No diagnostica |
| Comunicació | Informa el negoci, la direcció i els clients; actualitza la pàgina d'estat; filtra les interrupcions | No decideix tècnicament |
| Tècnics | Diagnostiquen i apliquen mitigacions, informant el comandament de tot el que fan | No parlen amb el negoci, no decideixen la comunicació |
Per què separar-los no és burocràcia, és una lliçó que s'aprèn patint-la. La persona que està depurant no pot alhora contestar a tres canals, parlar amb direcció i dur la cronologia: si ho intenta, fa les quatre coses malament. Sense algú decidint, s'acaba amb tres persones aplicant mitigacions contradictòries sobre el mateix sistema —un reinicia pods, un altre fa rollout undo, un tercer escala a mà—, que és un patró real i freqüent. Sense comunicació dedicada, o no s'informa ningú, o els tècnics es passen la incidència responent "ja està?" en lloc d'arreglant-la. I el comandament, en no tenir les mans al teclat, manté la perspectiva: és qui s'adona que fa quaranta minuts que persegueixen una hipòtesi que no porta enlloc.
1.3. El guió dels primers deu minuts
graph TB
A[Alerta o avís] --> B{Impacte real<br/>en usuaris?}
B -->|No| C[Sev 3 o 4<br/>tasca planificada]
B -->|Sí| D[Declarar incidència<br/>obrir canal dedicat]
D --> E[Assignar comandament<br/>qui la declara, si no hi ha ningú més]
E --> F[Min 0-2: abast<br/>què falla, per a qui, des de quan?]
F --> G[Min 2-4: què ha canviat?<br/>desplegaments, config, infraestructura]
G --> H[Min 4-7: MITIGAR<br/>encara no diagnosticar]
H --> I[Min 7-10: comunicar<br/>estat + propera actualització]
I --> J{Mitigat?}
J -->|Sí| K[Ara sí: diagnosticar<br/>amb calma]
J -->|No| L[Escalar: més gent,<br/>proveïdor, més severitat]
Minuts 0-2. Abast. Què falla, per a qui i des de quan. Les respostes s'escriuen al canal:
curl -s http://alertmanager.rutas-norte.example/api/v2/alerts \
| jq -r '.[] | select(.status.state=="active")
| "\(.labels.alertname)\t\(.labels.severitat)\t\(.startsAt)"' | sort -k3
curl -sG http://prometheus.rutas-norte.example/api/v1/query --data-urlencode 'query=
sum(rate(api_reserves_peticions_total{codi=~"5.."}[5m]))
/ sum(rate(api_reserves_peticions_total[5m]))' | jq -r '.data.result[0].value[1]'Minuts 2-4. Què ha canviat? El 70-80 % de les incidències les causa un canvi recent. És la pregunta de més rendiment del procés sencer.
git -C manifests log --since='6 hours ago' --oneline -- overlays/pro
argocd app history api-reserves-pro | tail -5
kubectl get events -A --sort-by=.lastTimestamp | tail -30
kubectl -n rutas-norte-pro get pods --sort-by=.status.startTime | tail -10Minuts 4-7. Mitigar, no diagnosticar. Aquest és el punt on més equips s'equivoquen. L'impuls natural és entendre què passa. L'objectiu correcte és que deixi de fer mal, i entendre-ho després.
| Mitigació | Quan | Comanda |
|---|---|---|
| Revertir l'últim desplegament | Hi va haver desplegament les últimes hores | git revert + argocd app sync |
| Avortar el canari | Hi ha un Rollout en curs |
kubectl argo rollouts abort api-reserves |
| Apagar una bandera | La fallada és d'una funcionalitat concreta | Canvi al servei de banderes |
| Escalar manualment | La saturació n'és la causa | kubectl scale --replicas=N |
| Reiniciar el component afectat | Fuita de memòria o estat corrupte | kubectl rollout restart deploy/X |
| Degradar deliberadament | Un dependent extern falla | Bandera de mode degradat |
Minuts 7-10. Comunicar. Un missatge curt i honest, amb la propera actualització compromesa:
[SEV 2] Fallades intermitents en confirmar reserva
Inici: 11:42 · Detectat: 11:47 · Estat: mitigant
Impacte: aproximadament el 25 % dels intents de compra fallen.
La consulta d'horaris funciona amb normalitat.
Acció: revertint el desplegament d'api-reserves 2.8.1 (11:38).
Propera actualització: 12:15
Comandament: Marta · Comunicació: IkerEl que fa útil aquest missatge: diu l'impacte en termes d'usuari, no d'infraestructura; diu què s'està fent; i compromet una hora concreta per a la següent actualització, cosa que talla d'arrel les interrupcions.
1.4. La comunicació al negoci
| Audiència | Què necessita | Què NO necessita |
|---|---|---|
| Direcció | Impacte en vendes, temps estimat, si hi ha risc de dades | CrashLoopBackOff, PromQL, noms de pods |
| Atenció al client | Què dir al client i quina alternativa oferir-li | Detalls tècnics |
| Clients | Que es coneix el problema i que s'està resolent | La causa tècnica |
| Equip tècnic | Tot | — |
Regles: es comunica abans que preguntin; es diu el que se sap i explícitament el que no; mai no es promet una hora de resolució que no es pot garantir (es promet la propera actualització, no la resolució); i les males notícies es donen aviat.
1.5. L'anàlisi posterior sense culpables
Es fa dins les 48 hores següents a tota incidència de severitat 1 i 2, i a qualsevol de la qual es pugui aprendre alguna cosa.
Sense culpables significa una cosa molt concreta: es parteix del fet que tothom va actuar raonablement amb la informació i les eines que tenia en aquell moment. Si algú va executar una comanda destructiva, la pregunta no és per què ho va fer, sinó per què el sistema va permetre que una comanda així fos fàcil d'executar per error. Si algú no va veure l'alerta, la pregunta no és per què no la va veure, sinó per què l'alerta no era visible.
El motiu no és amabilitat, és eficàcia: tan bon punt es busquen culpables, la gent deixa d'explicar el que va passar realment, i sense això no hi ha anàlisi possible.
La plantilla:
# Anàlisi posterior — INC-2026-041
## Resum
Una frase: què va passar, a qui va afectar, quant va durar.
## Impacte
- Durada: 11:42 – 12:31 (49 min)
- Usuaris afectats: ~2.400 intents de compra fallits
- Impacte econòmic estimat: ~11.000 € en vendes no completades
- Dades: cap pèrdua ni exposició ← si n'hi hagués, activa el protocol legal
## Cronologia (hores exactes)
- 11:38 · Desplegament d'api-reserves 2.8.1 (promoció aprovada)
- 11:42 · Comencen els errors 500 a POST /reserves
- 11:47 · Es dispara ApiReservesTaxaErrorAlta (for: 5m)
- 11:49 · La Marta declara SEV 2 i assumeix el comandament
- 11:53 · S'identifica el desplegament de les 11:38 com a sospitós
- 11:58 · git revert + sync forçat
- 12:06 · Taxa d'error de tornada a la normalitat
- 12:31 · Es declara resolta després de 25 min d'observació
## Causa arrel
La versió 2.8.1 afegia una consulta a `regles_preu` sense índex sobre
(linia, franja_horaria). Amb el volum de producció, cada confirmació
de reserva feia un recorregut seqüencial d'1,2 M de files, i esgotava el
pool de connexions i provocava errors 500 en cascada.
## Per què no es va detectar abans
1. `pre` té 40.000 files a `regles_preu`; producció, 1,2 M.
La consulta era instantània a pre.
2. L'anàlisi del canari es va saltar: la promoció es va fer amb
`promote --full` per arribar abans de la congelació del pont.
3. No hi ha alerta sobre consultes lentes de PostgreSQL.
## Què va funcionar bé
- L'alerta es va disparar 5 min després de l'inici del problema.
- La reversió per Git va trigar 8 min de decisió a servei restablert.
- La separació de rols va funcionar: ningú no va interrompre els tècnics.
## Accions de seguiment
| # | Acció | Tipus | Responsable | Data |
|---|---|---|---|---|
| 1 | Índex sobre regles_preu(linia, franja_horaria) | Correctiva | Ane | 06/08 |
| 2 | Poblar `pre` amb un volum representatiu de producció (anonimitzat) | Preventiva | Iker | 20/08 |
| 3 | Prohibir `promote --full` a pro tret d'autorització del comandament | Preventiva | Marta | 13/08 |
| 4 | Alerta sobre pg_stat_statements: consultes > 1 s | Detecció | Ane | 20/08 |
| 5 | Runbook «latència de l'API disparada» | Resposta | Jon | 27/08 |Quatre criteris perquè les accions serveixin d'alguna cosa: responsable amb nom (no "l'equip"), data concreta, prioritzades (millor tres que es fan que dotze que no) i revisades a la reunió setmanal fins a tancar-se.
L'acció número 2 d'aquest exemple és la més valuosa i la més costosa: el problema de fons no va ser la consulta, va ser que pre no s'assemblava a producció. Aquest tipus d'acció és la que evita famílies senceres d'incidències futures.
- Runbooks
Un runbook és el procediment escrit per a un símptoma concret. El seu valor real és que permet que algú que no és expert en aquell component respongui correctament a les tres de la matinada.
2.1. La plantilla
# [Nom del símptoma]
**Alerta:** NomDeLAlerta · **Severitat:** N · **Actualitzat:** data
## Símptoma
Què s'observa, tal com ho veu qui rep l'alerta.
## Impacte
Què significa per a l'usuari i per al negoci. Determina la urgència.
## Comprovació
Comandes exactes per confirmar el problema i acotar-ne l'abast.
## Mitigació
Què fer JA perquè deixi de fer mal. Ordenat del més segur al més agressiu.
## Resolució
Com arreglar-ho de debò, sense pressa, després de mitigar.
## Escalat
Si en X minuts no està mitigat: a qui avisar i amb quina informació.Regles d'un runbook útil: comandes copiables i executables tal qual, no descripcions; mitigació abans que diagnòstic; enllaçat des de l'anotació de l'alerta que el dispara; i revisat després de cada ús, perquè un runbook desactualitzat és pitjor que cap.
2.2. Runbook: disc de postgres-reserves gairebé ple
# Disc de postgres-reserves gairebé ple
**Alerta:** PostgresDiscAlt · **Severitat:** 2 (crítica si >95 %) · **Act.:** 2026-07-30
## Símptoma
El volum de dades o de WAL de postgres-reserves supera el 85 % d'ús.
## Impacte
Al 100 %, PostgreSQL DEIXA D'ACCEPTAR ESCRIPTURES. No es poden comprar
bitllets. Sev 1 immediata. Del 85 % al 100 % poden passar hores o minuts
segons la càrrega.
## Comprovació
kubectl -n rutas-norte-pro exec postgres-reserves-1 -- df -h /var/lib/postgresql/data /var/lib/postgresql/wal
# Dades o WAL? És la pregunta que decideix tota la resta.
# Si és WAL: està fallant l'arxivat?
kubectl -n rutas-norte-pro exec postgres-reserves-1 -- psql -U postgres -tc \
"SELECT last_archived_wal, last_failed_wal, last_failed_time FROM pg_stat_archiver;"
# Si és WAL: hi ha un slot de replicació abandonat retenint segments?
kubectl -n rutas-norte-pro exec postgres-reserves-1 -- psql -U postgres -tc \
"SELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retingut FROM pg_replication_slots;"
# Si són dades: què ocupa?
kubectl -n rutas-norte-pro exec postgres-reserves-1 -- psql -U postgres reserves -tc \
"SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10;"
## Mitigació
1. **Expandir el volum** (el més segur; la classe rutasnorte-rapida
permet expansió en línia, veure 05-05):
kubectl -n rutas-norte-pro patch pvc postgres-reserves-1 \
-p '{"spec":{"resources":{"requests":{"storage":"300Gi"}}}}'
kubectl -n rutas-norte-pro get pvc postgres-reserves-1 -w
2. **Si és un slot abandonat** (actiu=false i uns quants GB retinguts):
confirmar amb l'equip que aquella rèplica ja no existeix i eliminar-lo:
kubectl -n rutas-norte-pro exec postgres-reserves-1 -- psql -U postgres -c \
"SELECT pg_drop_replication_slot('nom_del_slot');"
⚠ Eliminar un slot ACTIU trenca aquella rèplica. Verificar-ho abans.
3. **Si és l'arxivat que falla**: revisar credencials del magatzem i
connectivitat. Mentre no s'arxivi, el WAL s'acumula sense parar.
4. **MAI** esborrar fitxers de pg_wal a mà. Corromp el clúster.
## Resolució
- Dades: revisar retenció de taules històriques; particionar `reserves`
per data; VACUUM FULL en finestra de manteniment si hi ha inflament.
- WAL: reparar l'arxivat; ajustar max_wal_size si el pic és normal.
- Revisar la tendència de creixement i planificar capacitat a 6 mesos.
## Escalat
Si en 20 min no baixa del 90 %, o si supera el 95 %: elevar a SEV 1,
avisar el comandament de guàrdia i preparar la restauració d'11-02 per si
el clúster entra en mode de només lectura.2.3. Runbook: latència de l'API disparada
# Latència d'api-reserves disparada
**Alerta:** ApiReservesLatenciaAlta (p95 > 500 ms, 10 min) · **Sev:** 2 · **Act.:** 2026-08-06
## Símptoma
El percentil 95 de latència d'api-reserves supera els 500 ms de manera
sostinguda. Llindar de l'SLO: 300 ms.
## Impacte
La compra de bitllets es percep lenta; la conversió cau. Per damunt de
2 s, els clients abandonen i la botiga-web comença a exhaurir temps d'espera.
## Comprovació
# 1. És tota l'API o una ruta concreta?
curl -sG http://prometheus.rutas-norte.example/api/v1/query --data-urlencode 'query=
histogram_quantile(0.95, sum by (ruta, le) (rate(api_reserves_duracio_segons_bucket[5m])))' \
| jq -r '.data.result[] | "\(.metric.ruta)\t\(.value[1])"' | sort -k2 -rn
# 2. És càrrega (més peticions) o degradació (mateixes peticions, més lentes)?
curl -sG http://prometheus.rutas-norte.example/api/v1/query --data-urlencode 'query=
sum(rate(api_reserves_peticions_total[5m]))'
# 3. L'HPA està escalant o està al sostre?
kubectl -n rutas-norte-pro get hpa api-reserves
# 4. Estrangulament de CPU o pressió de memòria?
kubectl -n rutas-norte-pro top pods -l app.kubernetes.io/name=api-reserves
# 5. És la base de dades?
kubectl -n rutas-norte-pro exec postgres-reserves-1 -- psql -U postgres reserves -tc \
"SELECT substr(query,1,60), calls, round(mean_exec_time::numeric,1) AS ms
FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;"
# 6. S'exhaureixen les connexions del pooler?
kubectl -n rutas-norte-pro logs deploy/postgres-reserves-pooler-rw --tail=50 | grep -i "pool\|wait"
# 7. És la passarel·la de pagaments externa?
curl -sG http://prometheus.rutas-norte.example/api/v1/query --data-urlencode 'query=
histogram_quantile(0.95, sum by (le) (rate(api_reserves_pagaments_duracio_segons_bucket[5m])))'
## Mitigació
| Troballa | Acció |
|---|---|
| HPA a maxReplicas | Pujar maxReplicas temporalment i verificar que hi ha nodes |
| Desplegament recent | Revertir (veure runbook de desplegament fallit) |
| Consulta lenta nova | Apagar la bandera de la funcionalitat implicada |
| Pool de connexions exhaurit | Pujar default_pool_size del Pooler (marge fins a ~150) |
| Passarel·la de pagaments lenta | Activar mode degradat: reserva sense pagament immediat |
| Estrangulament de CPU | Comprovar que no s'ha introduït limits.cpu (veure 11-01) |
## Resolució
Corregir la causa concreta: índex absent, consulta N+1, dimensionament
del pool, requests de CPU mal calculades. Afegir el cas al conjunt de
proves de càrrega de k6 perquè no torni.
## Escalat
Si p95 > 2 s durant més de 10 min: elevar a SEV 1. Si la causa és la
passarel·la externa: contactar amb el proveïdor i activar mode degradat.2.4. Runbook: un desplegament que ha sortit malament
# Desplegament fallit d'api-reserves
**Disparador:** alerta després de desplegament, o Rollout avortat · **Sev:** 2 · **Act.:** 2026-08-06
## Símptoma
Després d'un desplegament: errors 5xx, latència alta, pods reiniciant-se o un
AnalysisRun d'Argo Rollouts en Failed.
## Impacte
Depèn de l'avanç del desplegament. Amb canari avortat, l'impacte ja
està contingut. Amb promote --full, afecta el 100 % dels usuaris.
## Comprovació
kubectl argo rollouts get rollout api-reserves -n rutas-norte-pro
git -C manifests log -3 --oneline -- overlays/pro
argocd app history api-reserves-pro | tail -5
kubectl -n rutas-norte-pro logs -l app.kubernetes.io/name=api-reserves \
--tail=100 --since=15m | grep -i error | head -30
## Mitigació (ordenada per velocitat)
1. **Bandera** (5 s) — si el canvi està darrere d'una bandera, apagar-la.
2. **Avortar el Rollout** (10 s) — si continua en curs:
kubectl argo rollouts abort api-reserves -n rutas-norte-pro
3. **Revertir a Git** (3-4 min) — SEMPRE, encara que s'hagi fet 1 o 2,
perquè selfHeal tornaria a posar la versió dolenta:
cd manifests
git revert --no-edit $(git log -1 --format=%H -- overlays/pro)
git push origin main
argocd app sync api-reserves-pro
argocd app wait api-reserves-pro --health --timeout 300
4. ⚠ **Si el desplegament incloïa migració d'esquema NO compatible**,
NO revertir sense consultar. Pot corrompre dades. Escalar immediatament.
## Verificació posterior
Executar les capes 1-9 de la verificació d'11-01. No declarar resolt
fins a 15 min sense alertes.
## Resolució
Anàlisi posterior obligatòria. Preguntes clau: per què no ho va detectar
`pre`? per què no ho va detectar el canari? es va saltar alguna porta?
## Escalat
Si després de revertir el problema persisteix, la causa NO era el desplegament:
tornar al runbook de latència o obrir investigació general (07-06).2.5. Runbook: node perdut
# Node perdut o NotReady
**Alerta:** NodeNoDisponible (NotReady > 5 min) · **Sev:** 3, o 2 si en són uns quants · **Act.:** 2026-07-15
## Símptoma
Un o més nodes en NotReady o desapareguts del clúster.
## Impacte
Normalment cap: els pods es reprogramen sols. És SEV 2 si en cauen
uns quants alhora, si afecta una instància de postgres-reserves, o si el
clúster es queda sense capacitat per reprogramar.
## Comprovació
kubectl get nodes -o wide
kubectl describe node <node> | grep -A15 Conditions
kubectl get pods -A -o wide --field-selector spec.nodeName=<node>
kubectl get pods -A --field-selector status.phase=Pending # hi ha on reprogramar?
kubectl -n rutas-norte-pro get cluster postgres-reserves # afecta l'estat?
## Mitigació
1. **Un sol node, resta sana**: normalment no cal fer res. Karpenter
n'aprovisiona un de nou en 1-3 min. Verificar amb `kubectl get nodes -w` que
no queden pods en Pending.
2. **Pods Pending per falta de capacitat**: comprovar que Karpenter pot
escalar (límits de la classe de node, quotes del núvol, IP de la VPC)
amb `kubectl -n karpenter logs deploy/karpenter --tail=50`.
3. **Un PDB bloqueja el desallotjament** (veure coherència HPA/PDB d'11-01):
`kubectl -n rutas-norte-pro get pdb`.
4. **Era una instància de postgres-reserves**: verificar que l'operador
va promocionar i que s'està reconstruint la rèplica.
5. **NotReady que no es reemplaça**: forçar el drenatge i esborrar-lo:
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data --force
kubectl delete node <node>
## Resolució
Identificar si va ser interrupció d'instància interrompible (normal i esperat,
veure 10-06), fallada de maquinari, esgotament de recursos del kubelet o problema
de xarxa. Si es repeteix a la mateixa zona, sospitar de la zona i considerar
excloure-la temporalment.
## Escalat
Si cauen 3 nodes o més en 10 min, o si una zona sencera desapareix: SEV 2,
avisar el comandament, i avaluar el procediment de commutació d'11-05.2.6. Enllaçar els runbooks des de les alertes
El runbook només serveix si apareix en el moment de l'avís. L'anotació runbook de les regles de 07-04 és el que ho aconsegueix:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: rutas-norte-alertes
namespace: rutas-norte-pro
spec:
groups:
- name: api-reserves
rules:
- alert: ApiReservesLatenciaAlta
expr: |
histogram_quantile(0.95, sum by (le) (
rate(api_reserves_duracio_segons_bucket[5m]))) > 0.5
for: 10m
labels: { severitat: "2", equip: desenvolupament, servei: api-reserves }
annotations:
resum: "Latència p95 d'api-reserves per damunt de 500 ms"
descripcio: "p95 actual: {{ $value | humanizeDuration }}"
runbook: "https://wiki.rutasnorte.example/runbooks/api-latencia-alta"
panell: "https://grafana.rutas-norte.example/d/api-reserves"
- alert: PostgresDiscAlt
expr: |
(1 - kubelet_volume_stats_available_bytes{persistentvolumeclaim=~"postgres-reserves.*"}
/ kubelet_volume_stats_capacity_bytes{persistentvolumeclaim=~"postgres-reserves.*"}) > 0.85
for: 5m
labels: { severitat: "2", equip: plataforma }
annotations:
resum: "Volum {{ $labels.persistentvolumeclaim }} al {{ $value | humanizePercentage }}"
runbook: "https://wiki.rutasnorte.example/runbooks/postgres-disc-ple"I una política senzilla que manté la qualitat: tota alerta que desperta algú ha de tenir runbook. Si no en té, o s'escriu o l'alerta no hauria de despertar ningú.
- SLI, SLO i pressupost d'error
A 07-04 vam veure què són. Aquí serviran per prendre decisions, que és l'única cosa que els distingeix d'un adorn en un panell.
3.1. Els indicadors de Rutas Norte
Un SLI mesura el que l'usuari experimenta, no el que fa la infraestructura. "CPU al 80 %" no és un SLI; "el 99,7 % de les cerques responen en menys de 400 ms" sí.
| SLI | Definició precisa | Per què aquest |
|---|---|---|
| Disponibilitat de compra | % de POST /reserves amb resposta no-5xx |
És la transacció que genera ingressos |
| Latència de cerca | % de GET /horaris respostes en < 400 ms |
És el primer que fa l'usuari; si va lent, se'n va |
| Disponibilitat del web | % de peticions a www amb resposta 2xx o 3xx |
Sense web no hi ha venda |
| Frescor d'informes | % de dies amb informes-ocupacio completat abans de les 07:00 |
Operacions hi planifica la flota |
# SLI de disponibilitat de compra, finestra de 30 dies
sum(rate(api_reserves_peticions_total{ruta="/reserves", metode="POST", codi!~"5.."}[30d]))
/
sum(rate(api_reserves_peticions_total{ruta="/reserves", metode="POST"}[30d]))3.2. Fixar l'objectiu amb criteri de negoci
L'error clàssic és triar el número per estètica: "99,99 % sona seriós". Cada nou addicional multiplica aproximadament per deu el cost.
| SLO | Caiguda al mes | Cost relatiu | Què exigeix |
|---|---|---|---|
| 99,0 % | 7 h 18 min | × 1 | Un clúster, bones pràctiques |
| 99,5 % | 3 h 39 min | × 2 | Alta disponibilitat multizona |
| 99,9 % | 43 min | × 4 | Guàrdia, runbooks, desplegaments segurs |
| 99,95 % | 21 min | × 8 | Guàrdia 24×7 activa, redundància regional |
| 99,99 % | 4 min | × 20 | Actiu-actiu multiregió, automatització total |
Les tres preguntes que fixen el número: què costa cada minut caigut? (a Rutas Norte, uns 90 €/min un dia normal i uns 900 €/min durant el pont de maig); què percep l'usuari? (ningú no nota tres minuts al mes, tothom nota set hores); i què fa la competència i què s'ha promès per contracte?
Els SLO de Rutas Norte:
| SLI | SLO | Finestra | Justificació |
|---|---|---|---|
| Disponibilitat de compra | 99,9 % | 30 dies mòbils | 43 min/mes ≈ 3.900 € en dia normal. Acceptable. El salt al 99,95 % exigiria guàrdia 24×7 activa, que costa més del que estalvia |
| Latència de cerca | 99,0 % < 400 ms | 30 dies mòbils | L'1 % de cerques lentes no mou la conversió de manera mesurable |
| Disponibilitat del web | 99,9 % | 30 dies mòbils | Igual que la compra: sense web no hi ha venda |
| Frescor d'informes | 95 % dels dies | 90 dies | Operacions tolera un retard ocasional |
Cal notar que cap no és 100 %. Un SLO del 100 % significa que qualsevol canvi és inacceptable, i això paralitza el producte.
3.3. El pressupost d'error com a eina de decisió
Pressupost d'error = 100 % − SLO. Amb un SLO del 99,9 % en 30 dies, el pressupost és de 43 minuts i 12 segons d'indisponibilitat al mes.
I aquí hi ha la idea que ho canvia tot: aquest pressupost és un recurs que es pot gastar deliberadament. No és una fallada tenir-lo consumit en part; és per a això que existeix.
# Pressupost consumit a la finestra de 30 dies (0 = intacte, 1 = exhaurit)
(1 - (
sum(rate(api_reserves_peticions_total{ruta="/reserves",metode="POST",codi!~"5.."}[30d]))
/ sum(rate(api_reserves_peticions_total{ruta="/reserves",metode="POST"}[30d]))
)) / (1 - 0.999)La política de Rutas Norte, aprovada per direcció i per l'equip:
| Pressupost consumit | Què es pot fer | Qui decideix |
|---|---|---|
| < 50 % | Ritme normal. Es pot assumir més risc: canaris més ràpids, canvis d'infraestructura | L'equip |
| 50-75 % | Ritme normal, però cada desplegament de risc alt requereix canari complet sense promote --full |
L'equip |
| 75-100 % | Alerta: només correccions i funcionalitats de baix risc. Prioritat a les accions d'anàlisi posteriors pendents | Plataforma |
| > 100 % (exhaurit) | Congelació de funcionalitats. L'equip dedica el 100 % del seu temps a fiabilitat fins a recuperar el pressupost. Sense excepcions sense aprovació de direcció | Direcció |
El valuós d'aquesta política és que converteix una discussió d'opinions en una decisió basada en un número. Quan producte demana una funcionalitat més i plataforma diu que el sistema és fràgil, la conversa deixa de ser "jo crec que" i passa a ser "el pressupost està al 130 %, i això és el que vam acordar fer en aquest cas".
I funciona en les dues direccions, que és el que la fa justa: si el pressupost està al 20 % a mitjan mes, plataforma no pot bloquejar un desplegament al·legant risc. El sistema està sent més fiable del que es va prometre, i això significa que es pot anar més de pressa.
Alertes sobre el pressupost: s'alerta per taxa de consum, no per llindar absolut. L'expressió combina una finestra llarga i una de curta per evitar falsos positius, i es dispara quan el ritme exhauriria en dos dies el pressupost de trenta.
- alert: PressupostErrorConsumRapid
expr: |
(1 - (sum(rate(api_reserves_peticions_total{ruta="/reserves",codi!~"5.."}[1h]))
/ sum(rate(api_reserves_peticions_total{ruta="/reserves"}[1h])))) > 14.4 * 0.001
and
(1 - (sum(rate(api_reserves_peticions_total{ruta="/reserves",codi!~"5.."}[5m]))
/ sum(rate(api_reserves_peticions_total{ruta="/reserves"}[5m])))) > 14.4 * 0.001
for: 2m
labels: { severitat: "2", equip: plataforma }
annotations:
resum: "Consum ràpid del pressupost d'error de compra"
runbook: "https://wiki.rutasnorte.example/runbooks/pressupost-error"
- Gestió del canvi
4.1. Finestres i congelacions
| Tipus de canvi | Quan es permet | Aprovació |
|---|---|---|
| Correcció de baix risc | Qualsevol dia laborable, 9:00-16:00 | Revisió de codi |
| Funcionalitat amb canari | De dilluns a dijous, 9:00-15:00 | Porta de promoció d'11-03 |
| Migració d'esquema | Dimarts o dimecres, 10:00-12:00 | Plataforma + desenvolupament |
| Canvi d'infraestructura | Dimarts o dimecres, 9:00-12:00 | Plataforma + avís 48 h |
| Actualització del clúster | Finestra programada trimestral | Pla escrit + assaig a nopro |
| Correcció d'incidència | Sempre, sense finestra | Comandament d'incidència |
Congelacions programades:
| Període | Durada | Què es congela |
|---|---|---|
| Pont de maig | Del 29 d'abril a les 18:00 al 5 de maig a les 9:00 | Tot tret de correccions de sev 1 i 2 |
| Setmana Santa | Divendres anterior a dilluns posterior | Ídem |
| Agost (setmanes 32-33) | 2 setmanes | Canvis d'infraestructura; la resta amb aprovació |
| Nadal | 22 des. a 7 gen. | Tot tret de correccions crítiques |
Una congelació no és "no es treballa": és "no es canvia producció". Durant la congelació del pont de maig l'equip fa el que no pot fer la resta de l'any: escriure runbooks, millorar proves, revisar alertes sorolloses, saldar deute tècnic que no toca producció.
4.2. Per què desplegar els divendres a la tarda és mala idea
No és superstició, és aritmètica del temps de detecció i de la capacitat de resposta. Els problemes no apareixen a l'instant: fuites de memòria, inflament de taules, esgotament de connexions i ompliment de disc triguen hores o dies, i un desplegament del divendres a les 18:00 pot fallar el dissabte a les 4:00. El cap de setmana hi ha menys gent i pitjor disponible: el temps de restauració de 12 minuts d'11-03 es mesura en horari laborable, i el dissabte al matí és fàcilment el triple. El trànsit del cap de setmana és diferent: a Rutas Norte el dissabte es venen més bitllets d'oci que qualsevol dia laborable, així que és quan pitjor cau una fallada. Ningú no està vigilant, perquè qui desplega se'n va a casa. I hi ha un efecte pervers: sabent tot això, qui desplega el divendres a les 18:00 ho fa amb pressa i saltant-se comprovacions, per marxar.
La regla de Rutas Norte: últim desplegament a producció, dijous a les 15:00, i el divendres es dedica a observar el que s'ha desplegat durant la setmana. L'excepció són les correccions d'incidència, que no tenen finestra perquè el cost de no fer-les és més gran.
- Planificació de capacitat per al pont de maig
5.1. Els números de partida
| Mètrica | Dia normal | Pont de maig (previsió) | Factor |
|---|---|---|---|
Peticions/s a api-reserves (mitjana) |
85 | 850 | ×10 |
| Pic de peticions/s | 240 | 2.400 | ×10 |
| Reserves/dia | 3.100 | 24.000 | ×7,7 |
| Consultes/s a PostgreSQL | 320 | 3.400 | ×10,6 |
| Amplada de banda de sortida | 45 Mbps | 480 Mbps | ×10,7 |
El pic no està uniformement repartit: es concentra entre les 10:00 i les 13:00 del dimecres i dijous previs al pont, quan la gent compra el bitllet de tornada.
5.2. El càlcul, component a component
api-reserves. De la prova de càrrega de 09-06: una rèplica amb 200m de CPU sosté unes 70 peticions/s amb p95 de 180 ms. Per a 2.400 peticions/s:
2400 / 70 = 34,3 rèpliques necessàries
+ 20 % de marge de seguretat = 41 rèpliques
maxReplicas actual: 40 → pujar a 55I cal verificar que l'aritmètica tanca en tota la cadena, incloses les connexions a PostgreSQL, que van ser el coll d'ampolla d'11-02:
41 × 200m CPU = 8,2 vCPU de requests + botiga-web (0,75) + workers + sistema ≈ 14 vCPU
Nodes de 4 vCPU amb ~3,2 d'útils → 5 nodes mínim, 8 amb marge
Karpenter: el límit de la classe de node ha de permetre'n almenys 12
41 rèpliques × 20 connexions = 820 sol·licitades
PgBouncer max_client_conn 1000 ✔ (marge del 18 %)
default_pool_size 40 + reserve 10 = 50 de reals contra 197 disponibles ✔ResourceQuota del namespace (03-04): amb 41 rèpliques de l'API més la resta, la quota actual de 20 vCPU es queda curta. Cal pujar-la a 32 abans del pont, o l'HPA demanarà pods que la quota rebutjarà: una fallada silenciosa i desconcertant.
PostgreSQL: 3.400 consultes/s davant de les 320 habituals. La prova de càrrega va mostrar que el primari aguanta fins a 4.200 consultes/s abans de degradar-se, amb les lectures d'informes ja derivades a rèpliques. Suficient, però sense marge per a una consulta nova mal indexada, que és exactament el que va passar a l'incident INC-2026-041.
5.3. La prova de càrrega que ho valida
// proves/carrega-pont-maig.js — s'executa a rutas-norte-pre
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';
const taxaFallades = new Rate('fallades_negoci');
const duracioCompra = new Trend('duracio_compra_completa');
export const options = {
scenarios: {
// Perfil realista: rampa de pujada, altiplà de 20 min i baixada.
pont_maig: {
executor: 'ramping-arrival-rate',
startRate: 85,
timeUnit: '1s',
preAllocatedVUs: 300,
maxVUs: 2000,
stages: [
{ target: 85, duration: '2m' }, // línia base
{ target: 850, duration: '5m' }, // rampa
{ target: 850, duration: '20m' }, // altiplà sostingut
{ target: 2400, duration: '3m' }, // pic
{ target: 2400, duration: '5m' }, // pic sostingut
{ target: 85, duration: '5m' }, // baixada
],
},
},
thresholds: {
'http_req_duration{tipus:cerca}': ['p(95)<400'],
'http_req_duration{tipus:reserva}': ['p(95)<800'],
'http_req_failed': ['rate<0.005'],
'fallades_negoci': ['rate<0.01'],
},
};
const BASE = __ENV.BASE_URL || 'https://api-pre.rutasnorte.example';
export default function () {
group('flux complet de compra', () => {
const cerca = http.get(`${BASE}/horaris?linia=BIL-SAN&data=2026-05-01`,
{ tags: { tipus: 'cerca' } });
check(cerca, { 'cerca 200': (r) => r.status === 200 });
sleep(Math.random() * 3 + 1); // l'usuari mira els resultats
if (Math.random() < 0.12) { // només el 12 % reserva: embut real
const inici = Date.now();
const reserva = http.post(`${BASE}/reserves`,
JSON.stringify({ linia: 'BIL-SAN', data: '2026-05-01', places: 2 }),
{ headers: { 'Content-Type': 'application/json',
'Idempotency-Key': `k6-${__VU}-${__ITER}` },
tags: { tipus: 'reserva' } });
duracioCompra.add(Date.now() - inici);
taxaFallades.add(!check(reserva, { 'reserva 201': (r) => r.status === 201 }));
}
});
}Resultats de la prova del 15 d'abril de 2026:
| Mètrica | Objectiu | Resultat | Veredicte |
|---|---|---|---|
| p95 cerca | < 400 ms | 287 ms | ✔ |
| p95 reserva | < 800 ms | 612 ms | ✔ |
| Taxa d'error HTTP | < 0,5 % | 0,08 % | ✔ |
| Rèpliques assolides | < 55 | 47 | ✔ |
| Temps de reacció de l'HPA | < 2 min | 95 s | ✔ |
| CPU màxima de PostgreSQL | < 80 % | 71 % | ✔ |
| Espera en cua de PgBouncer | 0 | 0 | ✔ |
| Nodes aprovisionats per Karpenter | — | 11 (en 3 min) | ⚠ veure nota |
La nota: Karpenter va trigar tres minuts a aprovisionar els nodes del pic. Durant aquest temps hi va haver pods en Pending i el p95 va pujar a 1,1 s. Acció presa: sobreaprovisionar amb pods de baixa prioritat (PriorityClass negativa, de 06-05) que Karpenter desallotja instantàniament quan arriba càrrega real, i deixa nodes ja calents.
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata: { name: farciment-capacitat }
value: -10 # prioritat negativa: els desallotja qualsevol càrrega real
globalDefault: false
description: "Reserva capacitat calenta. Es desallotja en arribar càrrega real."
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: farciment-capacitat, namespace: plataforma }
spec:
replicas: 6 # 6 la setmana del pont; 2 la resta de l'any
selector: { matchLabels: { app: farciment-capacitat } }
template:
metadata: { labels: { app: farciment-capacitat } }
spec:
priorityClassName: farciment-capacitat
terminationGracePeriodSeconds: 0
containers:
- name: pausa
image: registry.k8s.io/pause:3.9
resources: { requests: { cpu: "1", memory: 2Gi } }5.4. La llista de preparació del pont
| # | Acció | Quan |
|---|---|---|
| 1 | Prova de càrrega completa a pre amb el perfil del pont |
3 setmanes abans |
| 2 | Pujar maxReplicas dels HPA segons el càlcul |
2 setmanes abans |
| 3 | Ampliar la ResourceQuota del namespace | 2 setmanes abans |
| 4 | Verificar límits de Karpenter i quotes del núvol | 2 setmanes abans |
| 5 | Pujar farciment-capacitat de 2 a 6 rèpliques |
1 setmana abans |
| 6 | Revisar i provar tots els runbooks | 1 setmana abans |
| 7 | Confirmar el torn de guàrdia amb noms i telèfons | 1 setmana abans |
| 8 | Restauració de còpia provada | 1 setmana abans |
| 9 | Congelació de canvis | 29 abril 18:00 |
| 10 | Revisió diària de mètriques durant el pont | Diari |
| 11 | Desfer 2, 3 i 5 | 1 setmana després |
El punt 11 s'oblida sistemàticament i és el que fa que la factura de l'any següent arrenqui un 20 % més alta.
- Costos
6.1. Per què la factura es dispara
| Causa | Mecanisme | Magnitud típica |
|---|---|---|
requests sobredimensionades |
Es reserva 1 vCPU i se n'usen 80m. Es paga el reservat | 30-50 % de malbaratament |
| Entorns que ningú no apaga | dev i pre a plena capacitat 168 h/setmana usant-se'n 40 |
60 % del cost de no-producció |
| Autoescalat sense sostre | El maxReplicas posat "per si de cas" s'assoleix durant un atac o un bucle |
Pics de factura inexplicables |
| Recursos orfes | PV de pods esborrats, balancejadors de Services eliminats a mitges, IP elàstiques sense assignar | 5-15 %, creixent amb el temps |
| Retenció de logs i mètriques | Guardar-ho tot a 30 dies "per si de cas" | Sovint supera el cost de còmput |
| Trànsit entre zones | Pods repartits parlant entre si; cada byte entre zones es factura | 3-8 %, invisible fins que es mesura |
| Volums sobredimensionats | Un volum de 500 GB amb 40 GB usats costa el mateix que un de ple | Variable |
| Tot sota demanda | No fer servir instàncies interrompibles per a càrregues tolerants | Fins a un 70 % de sobrecost en aquestes càrregues |
I una causa de fons que les engloba: ningú no veu la factura del que desplega. Un desenvolupador que posa requests: 2 en lloc de 200m no rep cap senyal.
6.2. Visibilitat amb OpenCost
helm install opencost opencost/opencost -n plataforma \
--set opencost.prometheus.internal.enabled=true \
--set opencost.prometheus.internal.serviceName=prometheus-operated \
--set opencost.prometheus.internal.namespaceName=monitoratge# Cost per namespace en els últims 7 dies
curl -sG http://opencost.plataforma:9003/allocation/compute \
-d window=7d -d aggregate=namespace -d accumulate=true \
| jq -r '.data[0] | to_entries[] | "\(.key)\t\(.value.totalCost | . * 100 | round / 100) €"' \
| sort -k2 -rnrutas-norte-pro 1842.30 €
rutas-norte-pre 612.45 €
monitoratge 384.10 €
rutas-norte-dev 298.72 €
plataforma 156.90 €
argocd 41.20 €Que monitoratge costi més que rutas-norte-dev sorprèn sempre, i és normal: Prometheus amb retenció llarga i moltes sèries és car.
| Eina | Model | Quan |
|---|---|---|
| OpenCost | Codi obert, CNCF | Començar. Cobreix l'essencial |
| Kubecost | Comercial, amb nivell gratuït | Informes, pressupostos, alertes de cost, recomanacions |
| Eines del proveïdor | Incloses | Bé per a la factura global; dolentes per al detall per pod |
6.3. Repartiment per equip i entorn
Aquí és on les etiquetes de 02-07 deixen de ser una convenció i esdevenen un instrument de gestió.
# Cost per equip
curl -sG http://opencost.plataforma:9003/allocation/compute \
-d window=30d -d aggregate=label:rutasnorte.example%2Fequip -d accumulate=true \
| jq -r '.data[0] | to_entries[] | "\(.key)\t\(.value.totalCost | round) €"'__unallocated__ és l'indicador de qualitat de l'etiquetatge: 341 € que ningú no pot explicar. La política de Kyverno d'11-05 que exigeix l'etiqueta rutasnorte.example/equip existeix precisament per portar aquest número a zero.
El repartiment informatiu funciona millor que la facturació interna real. Un informe mensual per equip, comparat amb el mes anterior, canvia comportaments sense la burocràcia de cobrar de debò. N'hi ha prou que algú vegi que el seu servei ha passat de 300 a 700 euros.
6.4. Les palanques, ordenades per rendibilitat
| # | Palanca | Estalvi típic | Esforç | Risc |
|---|---|---|---|---|
| 1 | Dimensionar bé amb les recomanacions del VPA (09-02) | 25-40 % | Baix | Baix |
| 2 | Apagar no-producció de nit i caps de setmana | 60 % de dev+pre |
Baix | Nul |
| 3 | Instàncies interrompibles per a càrregues tolerants | Fins a un 70 % en aquestes càrregues | Mitjà | Baix si hi ha PDB |
| 4 | Recursos orfes: volums, balancejadors, IP | 5-15 % | Baix, una vegada | Nul |
| 5 | Retenció de logs i mètriques | 20-40 % d'observabilitat | Baix | Mitjà: menys històric |
| 6 | Volums sobredimensionats | Variable | Mitjà | Baix |
| 7 | Compromisos d'ús a 1-3 anys | 20-40 % de la base estable | Baix | Compromís financer |
Palanca 1: dimensionar bé. El VPA en mode recomanació, sense aplicar canvis, diu què es malbarata:
kubectl -n rutas-norte-pro get vpa api-reserves \
-o jsonpath='{.status.recommendation.containerRecommendations[0]}' | jq{"containerName":"api","lowerBound":{"cpu":"142m","memory":"198Mi"},
"target":{"cpu":"186m","memory":"241Mi"},"upperBound":{"cpu":"312m","memory":"402Mi"}}Amb requests: 200m i target: 186m, api-reserves està ben dimensionada. El cas interessant va ser worker-notificacions, amb requests: 1 de CPU i un target de 120m: un malbaratament del 88 % multiplicat per les seves rèpliques.
Palanca 2: apagar de nit. La més rendible en relació esforç/estalvi.
apiVersion: batch/v1
kind: CronJob
metadata:
name: apagar-nopro
namespace: plataforma
spec:
schedule: "0 20 * * 1-5" # 20:00 de dilluns a divendres (un altre CronJob encén a les 7:30)
jobTemplate:
spec:
template:
spec:
serviceAccountName: gestor-horaris
restartPolicy: OnFailure
containers:
- name: apagar
image: registry.rutasnorte.example/utils/kubectl:1.30
command:
- /bin/sh
- -c
- |
for NS in rutas-norte-dev rutas-norte-pre; do
# Desem les rèpliques actuals en un ConfigMap per restaurar-les.
kubectl -n "$NS" get deploy -o json \
| jq -r '.items[] | "\(.metadata.name)=\(.spec.replicas)"' > /tmp/r.txt
kubectl -n "$NS" create configmap repliques-desades \
--from-file=/tmp/r.txt --dry-run=client -o yaml | kubectl -n "$NS" apply -f -
kubectl -n "$NS" scale deploy --all --replicas=0
done
# postgres-reserves de pre NO s'apaga: restaurar-lo costa
# més temps del que s'estalvia en diners.Amb l'encesa a les 7:30, dev i pre funcionen 57 hores setmanals en lloc de 168: un estalvi del 66 % en aquests entorns.
Palanca 3: instàncies interrompibles. Continuant el que es va decidir a 10-06:
| Càrrega | És interrompible? | Raó |
|---|---|---|
api-reserves |
Parcialment (50 %) | Amb PDB i repartiment topològic, tolera interrupcions; es manté una base sota demanda |
botiga-web |
Sí | Sense estat, arrencada en segons |
worker-notificacions |
Sí | Processament de cua, reintentable per disseny |
informes-ocupacio |
Sí | Treball per lots nocturn, amb backoffLimit |
postgres-reserves |
No | Una interrupció del primari provoca commutació. Sota demanda sempre |
| Prometheus | No | Perdre el pod perd dades no persistides |
Palanca 4: recursos orfes. L'escombrada que convé fer trimestralment:
kubectl get pv --no-headers | awk '$5=="Released" || $5=="Available" {print $1, $2, $5}'
aws ec2 describe-volumes --filters Name=status,Values=available \
--query 'Volumes[].{ID:VolumeId,GB:Size,Creat:CreateTime}' --output table
aws ec2 describe-addresses --query 'Addresses[?AssociationId==null].PublicIp'
aws elbv2 describe-load-balancers --query 'LoadBalancers[].LoadBalancerName'A la primera escombrada de Rutas Norte van aparèixer 14 volums orfes (1,8 TB, uns 180 €/mes) d'entorns efímers d'11-03 esborrats abans que existís el CronJob de neteja.
Palanca 5: retenció. La política revisada de Rutas Norte:
| Dada | Abans | Després | Justificació |
|---|---|---|---|
| Logs d'aplicació | 30 dies, tots | 7 dies complets + 90 dies només nivell>=warn |
Els logs de depuració de fa tres setmanes no es consulten mai |
| Mètriques a Prometheus | 30 dies a resolució completa | 15 dies completa + 1 any agregada en emmagatzematge remot | L'històric es consulta agregat |
| Traces | 30 dies | 7 dies, amb mostreig al 10 % | Suficient per investigar |
| Còpies de PostgreSQL | 30 dies | 30 dies | No es toca. Subjecte a compliment normatiu |
Aquesta última fila importa: la retenció de còpies amb dades personals no és una palanca de cost, és una decisió de compliment. Reduir-la per estalviar és un error greu.
6.5. L'abans i el després de la factura
| Concepte | Abans (mar. 2026) | Després (jul. 2026) | Estalvi | Palanca |
|---|---|---|---|---|
Còmput pro |
2.840 € | 2.190 € | −650 € | Dimensionament (1) + interrompibles (3) |
Còmput dev+pre |
1.420 € | 510 € | −910 € | Apagada nocturna (2) + dimensionament (1) |
| Observabilitat | 890 € | 520 € | −370 € | Retenció (5) |
| Emmagatzematge | 640 € | 445 € | −195 € | Orfes (4) + volums (6) |
| Trànsit de xarxa | 310 € | 285 € | −25 € | Repartiment per zona ajustat |
| Balancejadors | 180 € | 95 € | −85 € | Consolidació en un sol Ingress |
| Plans de control | 146 € | 146 € | 0 € | — |
| Total mensual | 6.426 € | 4.191 € | −2.235 € | −34,8 % |
Gairebé 27.000 euros anuals, amb unes tres setmanes-persona de feina i sense degradar cap SLO. Les dades del període confirmen que la disponibilitat de compra es va mantenir en el 99,94 %, per damunt de l'objectiu.
L'advertiment obligatori: optimitzar costos té un punt a partir del qual comença a costar fiabilitat. Reduir requests per sota del que la càrrega necessita provoca desallotjaments; portar-ho tot a instàncies interrompibles provoca interrupcions simultànies; retallar la retenció de mètriques deixa sense dades l'anàlisi d'una incidència. La factura no és l'SLO. Quan entrin en conflicte, guanya l'SLO, i aquesta decisió convé tenir-la escrita abans que passi.
- Calendari de manteniment
Res d'això no és opcional; l'única cosa opcional és si es fa planificat o d'urgència.
| Tasca | Periodicitat | Durada | Responsable | Si no es fa |
|---|---|---|---|---|
| Revisió d'alertes disparades i soroll | Setmanal | 30 min | Plataforma | Fatiga d'alertes: s'ignoren totes |
| Revisió d'accions d'anàlisis posteriors | Setmanal | 30 min | Comandament rotatori | Les mateixes incidències es repeteixen |
| Revisió de vulnerabilitats (Trivy, imatges en execució) | Setmanal | 1 h | Seguretat | S'acumulen fins a ser inabordables |
| Revisió de costos per equip | Mensual | 1 h | Plataforma + finances | La factura creix sense explicació |
| Actualització de complements (Helm) | Mensual | 2-4 h | Plataforma | S'acumulen salts de versió impossibles |
| Rotació de secrets d'aplicació | Trimestral | 2 h | Plataforma | Credencials d'anys, sense caducitat |
| Prova de restauració de còpies | Trimestral | 4 h | Plataforma | La còpia no existeix (11-02) |
| Revisió d'RBAC i accessos | Trimestral | 3 h | Seguretat | Permisos de gent que ja no hi és |
| Revisió i prova de runbooks | Trimestral | 3 h | Tot l'equip | Runbooks desactualitzats = pitjor que res |
| Revisió d'SLO i pressupost d'error | Trimestral | 2 h | Plataforma + producte | Objectius que ja no reflecteixen el negoci |
| Actualització menor del clúster | Trimestral | 1 dia | Plataforma | Es perd el suport de la versió |
| Prova de càrrega amb el perfil de pic | Semestral | 1 dia | Plataforma + desenvolupament | Sorpreses en temporada alta |
| Simulacre de recuperació davant de desastres | Anual | 2 dies | Tot l'equip | El pla d'11-05 no funciona (i no se sap) |
| Revisió de retenció i compliment normatiu | Anual | 1 dia | Plataforma + compliment | Risc legal |
| Renovació de certificats no automatitzats | Segons caduquin | — | Plataforma | Caiguda per certificat caducat |
Sobre els certificats: els de cert-manager es renoven sols, però sempre en queda algun fora de l'automatisme (certificats de client cap a la passarel·la de pagaments, certificats interns de la malla, certificats de signatura). Convé un inventari amb alertes a 30, 14 i 7 dies.
El pressupost de manteniment. Sumant les tasques, el manteniment consumeix aproximadament el 20-25 % de la capacitat de l'equip de plataforma. És un número que cal dir en veu alta i defensar davant de qui planifica: un equip al 100 % en funcionalitats noves és un equip que està acumulant deute operatiu a crèdit, i aquest deute es cobra en forma d'incidències.
- La maduresa de l'equip
La maduresa d'una plataforma no es mesura per les eines que fa servir. Es mesura pel que passa quan alguna cosa va malament.
| Nivell | Com es reconeix |
|---|---|
| Reactiu | Se n'assabenta pels clients. Ningú no sap què s'ha desplegat. Els canvis es fan a mà. Cada incidència s'afronta des de zero. Algú concret és imprescindible |
| Instrumentat | Hi ha mètriques i alertes, però moltes són soroll. Hi ha runbooks, alguns actualitzats. Els desplegaments són automàtics però els incidents, artesanals |
| Gestionat | Els SLO estan definits i es fan servir per decidir. Tota alerta que desperta té runbook. Les anàlisis posteriors generen accions que es tanquen. La capacitat es planifica. Els costos s'atribueixen |
| Optimitzat | El pressupost d'error governa la planificació. Els simulacres són rutina. La majoria de les incidències es mitiguen soles. L'equip dedica més temps a prevenir que a apagar focs |
Rutas Norte va passar de reactiu a gestionat en uns catorze mesos, i no va ser la tecnologia el que ho va permetre, sinó quatre canvis d'hàbit: anàlisis posteriors sense culpables en totes les incidències, que a la sisena van revelar que la meitat compartien causa de fons; runbooks escrits per qui va patir la incidència, immediatament després; el pressupost d'error com a argument compartit, que va convertir la tensió entre producte i plataforma en una conversa amb dades; i el calendari de manteniment defensat com a feina real, amb lloc a la planificació i no a les estones lliures.
Tres senyals que un equip madura de debò: les incidències s'escurcen abans de deixar de passar (no s'eliminen les fallades, es respon millor); baixa el nombre de persones imprescindibles, perquè que només una sàpiga resoldre alguna cosa és un risc i no una virtut; i augmenta el temps dedicat a prevenir, que és l'indicador definitiu d'haver sortit del mode apagafocs.
Errors Comuns i Consells
- Diagnosticar abans de mitigar. Entendre la causa arrel és important, però no mentre els usuaris estan patint. Primer que deixi de fer mal.
- El comandament d'incidència amb les mans al teclat. Perd la perspectiva i deixa de coordinar. En sev 1 i 2, el comandament no toca res.
- Anàlisis posteriors que busquen culpables. La gent deixa d'explicar el que va passar i l'anàlisi perd tot el seu valor.
- Accions de seguiment sense responsable ni data. No es fan. Millor tres accions amb nom i data que dotze en una llista.
- Alertes sense runbook que desperten algú. O s'escriu el runbook, o l'alerta no hauria de despertar ningú.
- SLO al 99,99 % sense haver calculat el que costa. Cada nou multiplica el cost per deu aproximadament. Tria el número amb criteri de negoci.
- Pressupost d'error com a panell decoratiu. Si no canvia el que fa l'equip quan s'exhaureix, no serveix de res.
- Optimitzar costos sense vigilar els SLO. Reduir
requestsmassa provoca desallotjaments; retallar la retenció deixa cega la següent investigació. - Oblidar desfer els ajustos del pic. Pujar
maxReplicasi la quota per al pont i no baixar-los després és la via més habitual que la factura pugi permanentment. - Consell: cronometra els runbooks. Un runbook que a la pràctica porta quaranta minuts quan diu deu necessita revisió.
- Consell: fes que la guàrdia roti de debò. Si sempre respon la mateixa persona, el coneixement no es distribueix i aquesta persona acaba cremada.
- Consell: celebra les incidències ben gestionades. Un equip que només rep atenció quan alguna cosa surt malament acaba amagant problemes.
Exercicis
Exercici 1: gestionar una incidència
Dissabte, 09:14. Arriba l'alerta ApiReservesTaxaErrorAlta (12 % d'errors 5xx). És el primer cap de setmana de juliol, amb molt trànsit d'oci. L'últim desplegament a pro va ser el dijous a les 14:30. Estàs de guàrdia i hi ha una altra persona localitzable. Descriu: (a) la severitat que declares i per què; (b) els rols que assignes; (c) les comandes exactes dels teus primers cinc minuts; (d) el missatge de comunicació que envies als deu minuts.
Exercici 2: decidir amb el pressupost d'error
Som a 18 de setembre. El pressupost d'error de la disponibilitat de compra (SLO 99,9 %, finestra de 30 dies) està consumit al 118 %: dues incidències sev 2 se'n van endur 51 minuts. Producte demana desplegar el nou comparador de preus, que fa tres setmanes que està llest i que direcció vol per a la campanya de tardor. Plataforma té quatre accions d'anàlisi posterior pendents. Aplica la política de l'apartat 3.3: què decideixes, qui decideix, què proposes com a alternativa i quines condicions posaries per aixecar la congelació?
Exercici 3: pla de reducció de costos
La factura mensual d'un clúster és: còmput pro 3.100 €, còmput no-producció 1.900 €, observabilitat 1.200 €, emmagatzematge 800 €, xarxa 400 €, balancejadors 300 €. Dades addicionals: el VPA recomana una mitjana del 45 % de les requests actuals; dev i pre funcionen 24×7; Prometheus reté 45 dies a resolució completa; hi ha 22 volums en estat Released; cap càrrega no fa servir instàncies interrompibles. Proposa un pla ordenat per rendibilitat, amb l'estalvi estimat de cada palanca, el risc associat i què mesuraries per comprovar que no es degrada el servei.
Solucions
Solució 1.
(a) Severitat 2. Un 12 % d'errors és degradació seriosa d'una funcionalitat crítica (la compra), però no caiguda total: el 88 % dels intents funciona. Seria sev 1 si superés el 50 % o si no es pogués comprar en absolut. El context de cap de setmana de juliol amb molt trànsit reforça la urgència i justifica trucar a la segona persona.
(b) Com que som dos: jo assumeixo comandament i comunicació; la persona localitzable, el rol tècnic. Si escala a sev 1, es truca a una tercera persona i se separen comandament i comunicació. El que no es fa és que el comandament es posi a depurar.
(c) Primers cinc minuts:
# Abast
curl -s http://alertmanager.rutas-norte.example/api/v2/alerts \
| jq -r '.[] | select(.status.state=="active") | "\(.labels.alertname)\t\(.startsAt)"'
kubectl -n rutas-norte-pro get pods -l app.kubernetes.io/name=api-reserves
kubectl -n rutas-norte-pro get hpa api-reserves
# Què ha canviat? (el desplegament va ser fa 43 h: probablement NO és la causa)
git -C manifests log --since='48 hours ago' --oneline -- overlays/pro
kubectl get events -A --sort-by=.lastTimestamp | tail -20
# Què falla exactament?
kubectl -n rutas-norte-pro logs -l app.kubernetes.io/name=api-reserves \
--tail=100 --since=20m | grep -i error | head -20
# Sospitosos habituals d'un cap de setmana amb trànsit alt
kubectl -n rutas-norte-pro get cluster postgres-reserves
kubectl -n rutas-norte-pro exec postgres-reserves-1 -- df -h /var/lib/postgresql/data
kubectl -n rutas-norte-pro logs deploy/postgres-reserves-pooler-rw --tail=30 | grep -i waitEl raonament clau: com que el desplegament va ser fa 43 hores i el problema comença ara, la hipòtesi més probable no és un canvi de codi sinó saturació pel trànsit del cap de setmana (HPA al sostre, connexions exhaurides, disc ple) o un problema acumulatiu (fuita de memòria, inflament de taules). Se'n va al runbook de latència i al de disc.
(d) Comunicació als deu minuts:
[SEV 2] Fallades intermitents en comprar bitllets
Inici: 09:14 · Estat: investigant
Impacte: aproximadament el 12 % dels intents de compra falla i cal
reintentar-ho. La cerca d'horaris funciona amb normalitat.
Acció: investigant saturació pel trànsit del cap de setmana;
l'HPA està escalant. Descartat el desplegament de dijous.
Propera actualització: 09:40
Comandament: [nom] · Tècnic: [nom]Solució 2. Decisió: no es desplega el comparador de preus. El pressupost està exhaurit (118 %), cosa que activa la congelació de funcionalitats de la política. La decisió correspon a direcció, no a l'equip, precisament perquè una excepció té cost de negoci i l'ha d'assumir qui té aquesta responsabilitat. El paper de plataforma és presentar la dada, la política acordada i les alternatives, no bloquejar unilateralment.
Alternativa que es proposa: durant les properes dues setmanes l'equip tanca les quatre accions pendents, i el comparador es desplega darrere d'una bandera de funcionalitat apagada (11-04). Així el codi arriba a producció sense canviar el comportament, es valida tècnicament, i l'activació queda llesta per al moment en què el pressupost es recuperi. Això satisfà parcialment producte sense consumir pressupost.
Condicions per aixecar la congelació: (1) les quatre accions tancades i verificades; (2) el pressupost a la finestra de 30 dies mòbils per sota del 100 %, cosa que passarà automàticament en sortir les incidències de la finestra si no n'hi ha de noves; (3) el desplegament del comparador s'ha de fer amb canari complet, sense promote --full, amb anàlisi de la mètrica de conversió; (4) revisió conjunta de si l'SLO del 99,9 % continua sent l'adequat, perquè dues incidències sev 2 en un mes poden indicar que l'objectiu és massa exigent per a la inversió actual, o que hi ha un problema de fons sense resoldre.
Solució 3. Total actual: 7.700 €/mes.
| Ordre | Palanca | Càlcul | Estalvi/mes | Risc |
|---|---|---|---|---|
| 1 | Apagar no-producció nits i caps de setmana | 1.900 × 0,66 | ~1.254 € | Nul. Només cal gestionar l'arrencada matinal |
| 2 | Dimensionar segons VPA (aplicar amb marge, no al 45 % exacte) | (3.100 + 646) × ~0,30 | ~1.124 € | Baix-mitjà. Aplicar amb marge del 20 % sobre target i vigilar desallotjaments |
| 3 | Retenció de Prometheus de 45 a 15 dies + agregat remot | 1.200 × 0,50 | ~600 € | Mitjà. Es perd històric fi; mitigar amb emmagatzematge remot agregat |
| 4 | Instàncies interrompibles en càrregues tolerants | ~40 % del còmput apte × 0,65 | ~550 € | Baix amb PDB i topologySpread correctes. Mai per a la base de dades |
| 5 | Eliminar 22 volums Released |
Estimat | ~150 € | Nul, prèvia verificació que no contenen dades necessàries |
| 6 | Consolidar balancejadors en un sol Ingress | 300 × 0,50 | ~150 € | Baix |
| Total | ~3.828 € (−50 %) |
Ordre justificat: primer el de risc nul i esforç baix (1, 5), després el de més estalvi absolut (2), i finalment el que exigeix més cura (3, 4).
Què mesurar per comprovar que no es degrada el servei: (a) el pressupost d'error de tots els SLO abans i després de cada palanca, que és l'indicador definitiu; (b) desallotjaments per pressió de memòria i reinicis OOMKilled després d'aplicar la palanca 2; (c) latència p95 i temps de reacció de l'HPA després de la palanca 4, perquè les interrupcions augmenten la rotació de pods; (d) temps de resolució d'incidències després de la palanca 3, que és on es notaria la falta d'històric. Regla d'aplicació: una palanca cada vegada, amb dues setmanes d'observació entre elles, per poder atribuir qualsevol degradació a la seva causa.
Conclusió
Hem recorregut el dia a dia de qui manté una plataforma viva. La gestió d'incidències amb severitats definides per endavant, rols separats —i el comandament sense tocar el teclat—, el guió dels primers deu minuts que prioritza mitigar sobre diagnosticar, i l'anàlisi posterior sense culpables les accions de la qual tenen responsable i data. Els runbooks, amb la seva plantilla i quatre casos reals de Rutas Norte enllaçats des de les anotacions de les alertes. Els SLI i SLO triats amb criteri de negoci, i el pressupost d'error convertit en una política que decideix què fa l'equip el mes que ve. La gestió del canvi, amb les seves finestres, la seva congelació del pont de maig i les raons aritmètiques per les quals no es desplega els divendres a la tarda. La planificació de capacitat, amb els números del ×10 i la prova de càrrega que els valida. Els costos, amb les palanques ordenades per rendibilitat i una reducció real del 35 % sense tocar cap SLO. I el calendari de manteniment, que consumeix una quarta part de l'equip i cal defensar com a feina real.
Amb això es tanca el mòdul 11 i, amb ell, el recorregut de Rutas Norte. Vam començar amb una empresa que venia bitllets d'autobús i uns manifests solts. Li vam donar Pods, Deployments i Services; configuració i secrets; xarxes, Ingress i TLS; emmagatzematge persistent amb còpies; StatefulSets, operadors i planificació avançada; observabilitat amb mètriques, logs i alertes; seguretat des de la imatge fins a l'RBAC; autoescalat per al pont de maig; Helm, Kustomize, GitOps i un clúster gestionat. I en aquest últim mòdul ho hem vist tot funcionar junt: una aplicació web completa en producció amb els seus tretze objectes, una base de dades amb estat amb la seva commutació mesurada i la seva restauració cronometrada, una canalització que va del git push a producció sense tocar mai kubectl apply, desplegaments canaris que avorten sols quan Prometheus diu que alguna cosa va malament, una flota de clústers governada des d'un repositori, i l'operació diària que sosté tot l'anterior.
Si has arribat fins aquí, hi ha una cosa que convé dir amb claredat: ja tens el coneixement que mesuren les certificacions de Kubernetes. El CKA pregunta per clústers, nodes, xarxes, emmagatzematge i resolució de problemes: és el que hem fet als mòduls 1 a 7 i a les incidències d'aquest. El CKAD pregunta per Pods, Deployments, configuració, sondes i treballs: és el mòdul 2 al 6 i tota la lliçó 11-01. El CKS pregunta per enduriment, polítiques, seguretat d'imatges i auditoria: és el mòdul 8 complet.
El que falta no és coneixement, és format. Els exàmens són pràctics, cronometrats, amb una terminal i la documentació oficial com a única ajuda, i penalitzen sense pietat la lentitud amb kubectl. És un tipus de destresa diferent del que es necessita per operar una plataforma real: allà l'important és decidir bé; aquí, a més, cal fer-ho ràpid i sense dubtar de la sintaxi.
D'això tracta el mòdul 12, Preparació per a la Certificació de Kubernetes: què mesura exactament cada examen, com es distribueix el temps, quines dreceres de kubectl marquen la diferència entre acabar i no acabar, què buscar a la documentació permesa i com preparar-se amb simulacres cronometrats. Començarem pel CKA.
Curs de Kubernetes
Mòdul 1: Introducció a Kubernetes
- Què és Kubernetes?
- Arquitectura de Kubernetes
- Conceptes i Terminologia Clau
- Configuració d'un Clúster de Kubernetes
- La CLI de Kubernetes: kubectl
- Objectes, Manifests YAML i el Model Declaratiu
- El Projecte del Curs: la Plataforma Rutas Norte
Mòdul 2: Components Principals de Kubernetes
- Pods
- ReplicaSets
- Deployments
- Actualitzacions, Rollbacks i Estratègies de Desplegament
- Serveis
- Namespaces
- Etiquetes, Selectors i Anotacions
Mòdul 3: Gestió de Configuració i Secrets
- ConfigMaps
- Secrets
- Variables d'Entorn
- Quotes i Límits de Recursos
- LimitRanges i Classes de Qualitat de Servei (QoS)
- ServiceAccounts i Accés a l'API des dels Pods
Mòdul 4: Xarxes a Kubernetes
- Xarxes de Clúster
- Tipus de Serveis
- DNS Intern i Descobriment de Serveis
- Controladors d'Ingress
- TLS i Gestió de Certificats amb cert-manager
- Polítiques de Xarxa
Mòdul 5: Emmagatzematge a Kubernetes
- Volums
- Volums Persistents
- Reclamacions de Volums Persistents
- Classes d'Emmagatzematge
- Aprovisionament Dinàmic, Expansió i Snapshots
- Còpies de Seguretat i Restauració de Dades
Mòdul 6: Conceptes Avançats de Kubernetes
- StatefulSets
- DaemonSets
- Treballs i CronJobs
- Init Containers, Sidecars i Patrons Multicontenidor
- Planificació: Afinitat, Taints i Toleracions
- Definicions de Recursos Personalitzats (CRDs)
- Operadors i el Patró Controlador
Mòdul 7: Monitoratge i Registre
- Verificacions de Salut i Sondes
- Servidor de Mètriques i kubectl top
- Monitoratge amb Prometheus
- Visualització i Alertes amb Grafana i Alertmanager
- Registre Centralitzat amb Elasticsearch, Fluentd i Kibana (EFK)
- Depuració d'Aplicacions i Esdeveniments del Clúster
Mòdul 8: Seguretat a Kubernetes
- Control d'Accés Basat en Rols (RBAC)
- Contextos de Seguretat i Enduriment del Contenidor
- Polítiques de Seguretat de Pods i Pod Security Standards
- Seguretat de Xarxa
- Seguretat d'Imatges
- Auditoria, Escaneig i Gestió de Vulnerabilitats
Mòdul 9: Escalat i Rendiment
- Autoescalat Horitzontal de Pods
- Autoescalat Vertical de Pods
- Autoescalat de Clúster
- Escalat per Esdeveniments i Mètriques Personalitzades amb KEDA
- Alta Disponibilitat: PodDisruptionBudgets i Topologia
- Ajust de Rendiment
Mòdul 10: Ecosistema i Eines de Kubernetes
- Minikube i Entorns Locals amb kind
- Kubeadm
- Helm
- Kustomize
- GitOps amb Argo CD i Flux
- Kubernetes Gestionat: EKS, AKS i GKE
Mòdul 11: Estudis de Cas i Aplicacions del Món Real
- Desplegament d'una Aplicació Web
- Execució d'Aplicacions amb Estat
- CI/CD amb Kubernetes
- Estratègies de Desplegament: Blue-Green i Canary
- Gestió Multi-Clúster
- Operació en Producció: Incidències, Runbooks i Costos
