Este es el primer postmortem del curso. No es un relato de héroes: es el registro de una prueba de carga que se salió de control y de lo que aprendí al arreglarlo.
\nResumen
\nDurante un ensayo de capacidad en el clúster de homologación, subí el HPA del backend a 10 réplicas y disparé una rampa de 50 usuarios autenticados. La prueba no devolvió errores HTTP, pero la latencia p95 fue de 8,4 segundos. Minutos después, el primario de PostgreSQL perdió los probes de liveness y CloudNativePG inició un failover. El antiguo primario quedó atascado en el apagado y tuve que eliminarlo para que el clúster promoviera la réplica.
\nNo se perdió ningún dato. El servicio siguió respondiendo a través de los pods antiguos durante la mayor parte del incidente. Pero el susto fue grande — y las lecciones, valiosas.
\nLínea de tiempo (resumida)
\n| Hora | \nEvento | \n
|---|---|
| T+0 | \nHPA ajustado a maxReplicas: 10; comienza la prueba de 50 VUs autenticados | \n
| T+1 min | \nLos workers alcanzan 92% y 82% de memoria | \n
| T+2 min | \nEl backend escala a 10 pods; p95 sube a 8,4 s | \n
| T+3 min | \nLos probes de Postgres comienzan a fallar por timeout | \n
| T+5 min | \nCNPG inicia failover; el primario antiguo entra en shutdown | \n
| T+8 min | \nEl primario antiguo no termina el shutdown; el clúster queda Failing over | \n
| T+10 min | \nElimino el pod del primario antiguo; CNPG promueve la réplica | \n
| T+12 min | \nClúster healthy; HPA reducido a 6; la latencia se normaliza | \n
Causa raíz
\nNo fue “Kubernetes”. Fue una combinación de decisiones:
\n-
\n
- HPA sin presupuesto de memoria. El límite de 10 pods se definió pensando en CPU, pero cada pod de backend usaba ~1,2–1,3 GiB. En workers de 8 GiB, 10 pods competían con la base de datos, Elasticsearch, MinIO y RabbitMQ. \n
- Presión de memoria en los nodos. Con un 92% de uso, el kubelet y los probes empiezan a sufrir. Postgres no fue eliminado por OOM, pero los probes de liveness/readiness comenzaron a agotar el timeout. \n
- Probe agresivo en una base de datos bajo carga. El liveness de CNPG usa el instance manager; bajo contención de CPU/red, el timeout de 25 s fue suficiente para marcar al primario como enfermo. \n
- Failover sin plan de fencing. El primario antiguo no terminó el shutdown; el clúster quedó en
Failing overhasta la eliminación manual del pod. \n
Lo que funcionó
\n-
\n
- CNPG promovió la réplica correctamente tan pronto como el primario antiguo salió. \n
- El backend siguió sirviendo con los pods antiguos; no hubo caída total. \n
- El PDB de Postgres impidió desalojos accidentales antes de tiempo (y después demostró por qué existe). \n
- El runbook de failover se escribió a partir del incidente y quedó en el repositorio. \n
Lo que no funcionó
\n-
\n
- Ajustar el HPA solo por CPU, ignorando la memoria por pod. \n
- Ejecutar carga autenticada sin mirar
kubectl top nodescada minuto. \n
- Probar a través del NodePort directo: el rate limit vio la IP de NAT (
10.244.1.0) y generó 429, enmascarando el resultado real. A través del ingress, la misma prueba tuvo 0% de error. \n
- Dejar el HPA al máximo durante el failover, presionando aún más los nodos. \n
Acciones tomadas
\n| Acción | \nResultado | \n
|---|---|
HPA reducido a maxReplicas: 6 | \n
Estabilidad inmediata | \n
| Workers ampliados a 16 GiB | \nMargen para 8–10 pods con datos | \n
| Prueba rehecha a través del ingress | \n0% de error, p95 83,9 ms en 50 VUs | \n
| Runbook de failover CNPG | \nProcedimiento documentado | \n
| Plan de capacidad | \nHPA máximo según presupuesto de memoria, no solo CPU | \n
Lecciones
\n-
\n
- HPA es una decisión de capacidad, no un número mágico. Calcula memoria por pod × réplicas × nodos. \n
- PDB protege, pero no salva de la falta de recursos. Evita desalojos; no impide que el nodo se quede sin memoria. \n
- El probe de base de datos es sensible a la contención. Ajusta el timeout y
failureThresholdcon datos reales. \n
- El failover necesita fencing. Si el primario antiguo no muere, el clúster no promueve la réplica por sí solo. \n
- Prueba por el camino real. NodePort directo no representa el tráfico del ingress. \n
- Postmortem sin culpa. El objetivo es corregir el sistema, no a la persona que apretó el botón. \n
Checklist para tu clúster
\n-
\n
- [ ] Sé cuánta memoria usa cada réplica del backend. \n
- [ ] El HPA tiene
maxReplicascompatible con la memoria de los workers. \n
- [ ] Postgres tiene PDB y anti-affinity. \n
- [ ] Sé qué hacer si el primario antiguo no termina el shutdown. \n
- [ ] Pruebo la carga a través del ingress, no a través del NodePort. \n
- [ ] Tengo runbook de failover y restore. \n
Próximo
\nEn el capítulo 05 vamos a montar el control plane en HA — y a entender por qué el etcd con quórum es la diferencia entre “alta disponibilidad” y “tres máquinas que se caen juntas”.
\nSeguir leyendo
Lea también
TutoriaisManual LSI / Broadcom MegaRAID — MegaCli y StorCLI
Con esta guía, su equipo gana autonomía para diagnosticar rebuilds, resolver foreign configurations, gestionar BBU y CacheCade — con escenarios reales de producción documentados.
20 min de leitura
Nginx Cheatsheet
Guía de bolsillo con ejemplos listos para configurar Nginx en producción: desde instalación y virtual hosts hasta ajuste de rendimiento y resolución de problemas.
12 min de leitura
KubernetesCurso Kubernetes 27 — SLO/SLI, on-call y postmortem sin culpa
Qué prometer, cómo medir y qué hacer cuando el error se dispara: SLI, SLO, presupuesto de error, alertas que despiertan a alguien y postmortem sin caza de brujas.
5 min de leitura
Comentarios
Aún sin comentarios
Sea el primero en compartir su opinión sobre este artículo.

Deje un comentario