~/blog $ git show 2026-08-11
49 microservicios, una falla invisible: cuando “Running” no significa que funciona
· 5 min de lectura · Victor Benavides
En octubre pasado confesé, aquí mismo, que había sobre-ingenieriado a sabiendas un side project hasta convertirlo en veinticinco microservicios. Hora de una actualización: el último conteo fue cuarenta y nueve.
Me gustaría decirte que eso fue un plan. No lo fue. Algo que era tan simple en mi cabeza — un prototipo pequeño, un puñado de servicios — se fue encontrando con el mundo real, y el mundo real siguió pidiendo cosas: ticketing, pagos, control de acceso, notificaciones, reportes. En algún punto del camino dejó de ser "el prototipo" y se convirtió en una plataforma que corre eventos en vivo de verdad, con gente de verdad en la puerta. El post del overengineering era sobre ego y aprendizaje. Este es sobre lo que pasa cuando eso que construiste por diversión empieza a cargar peso — y una de sus piezas invisibles falla en silencio.
Viernes, 17:33
Imagina la escena: un evento en vivo, asistentes llegando, el staff escaneando tickets en la entrada. Mi plataforma en medio de todo. Y entonces, un viernes por la noche, exactamente a las 17:33 UTC, los logins dejan de funcionar.
No había salido ningún deploy. Nadie había tocado nada. Incluso habíamos congelado los cambios de infraestructura durante la semana del evento, específicamente para evitar este tipo de momento. Pero la nube no firma tu calendario de freeze: nuestro proveedor decidió que los nodos del clúster necesitaban un redeploy forzado de mantenimiento, y los nodos empezaron a rotar — llevándose consigo a cada pod que vivía en ellos.
Eso, por sí solo, debió estar bien. Kubernetes reprograma pods todo el día; ese es justamente el punto. La plataforma está diseñada para sobrevivir a nodos muriendo.
No sobrevivió. Y la razón es la falla más instructiva que he tenido en todo el año.
La pieza invisible
Cada servicio de la plataforma corre con un sidecar — un contenedor compañero (Dapr, en mi caso) que maneja las llamadas entre servicios, el estado, el pub/sub. El contenedor de la aplicación sin su sidecar está sordo y mudo: el proceso corre, pero no puede hablar con nada.
Los sidecars no aparecen por arte de magia. Un componente del clúster — un webhook de inyección — se los adjunta a cada pod al nacer. Y ese inyector es, a su vez, solo otro pod... lo que significa que también estaba rotando cuando los nodos se cayeron.
Aquí está la trampa. El webhook estaba configurado con failurePolicy: Ignore — que se traduce como: "si el inyector no está disponible cuando nace un pod, deja que el pod arranque de todas formas." Durante los minutos que el inyector estuvo caído, catorce pods nacieron sin sidecars. Catorce servicios corriendo, con apariencia saludable, y completamente incapaces de hablar.
"Running" no es "funcionando"
Esta es la parte que se me quedó grabada. Todos los dashboards estaban en verde.
Kubernetes reportaba los pods como Running — cierto. Los health probes pasaban — también cierto, porque los probes le preguntaban al proceso de la app "¿estás arriba?", y lo estaba. Nadie había hecho la pregunta que importaba: "¿puedes realmente alcanzar las cosas de las que dependes?"
El sistema no nos mintió. Respondió exactamente las preguntas que lo habíamos configurado para responder. Simplemente no habíamos configurado las preguntas correctas. La primera señal honesta que recibimos no vino de la observabilidad — fueron usuarios sin poder iniciar sesión, durante un evento en vivo, mientras cada gráfico decía que todo estaba bien.
Si leíste el post del sábado, ya viste esta forma antes: un host estático sirviendo un "página no encontrada" con un HTTP 200 saludable. La misma enfermedad, un radio de explosión más grande. Un check que verifica el envoltorio y no el contenido es peor que no tener check — fabrica confianza falsa.
El arreglo tomó minutos. El saber tomó más.
Una vez que entendimos, la recuperación fue casi vergonzosamente simple: borrar los pods que nacieron a medias. El scheduler los recreó, el inyector — ya de pie para entonces — hizo su trabajo, los sidecars se adjuntaron, los logins volvieron. Minutos de trabajo.
La parte cara fue todo lo anterior: la ventana donde el único síntoma era "los usuarios no pueden entrar" y cada dashboard en verde discutía activamente contra la verdad.
Una línea
La lección permanente cabe en una línea de configuración: failurePolicy: Fail — "si no puedo inyectar el sidecar, no dejes que el pod arranque en absoluto."
Mira el trade-off, porque genuinamente hay uno. Fail significa que si el inyector alguna vez está caído, los pods no se programan — tu clúster se niega ruidosamente a avanzar. Ignore significa que los pods siempre arrancan — algunos rotos de una forma que nada reporta. Ambas son fallas. Pero una falla te despierta con una alerta, y la otra le sonríe a tus dashboards mientras tus usuarios sufren.
Yo me quedo con la falla ruidosa todas las veces. Un sistema que se niega a correr es un problema que arreglas en minutos. Un sistema que corre medio-vivo es un problema que descubres — normalmente desde afuera, normalmente en el peor momento posible. 👉 Falla fuerte, falla temprano, falla de una forma que se señale a sí misma.
Lo que 49 servicios me enseñaron de verdad
No que los microservicios sean malos — los lectores frecuentes saben que construí esto a propósito. Lo que el incidente me enseñó es que con cuarenta y nueve servicios, el comportamiento del sistema vive en las costuras, no en los servicios. Cada servicio tenía tests. Cada servicio tenía probes. La falla vivía en un webhook que ninguno de mis servicios sabía que existía, controlado por un default que nadie había cuestionado.
Así que el checklist real que salió de ese viernes:
- Hazle a tus health checks la pregunta que importa: no "¿está arriba el proceso?" sino "¿puede hacer su trabajo?"
- Encuentra las piezas cuya ausencia es silenciosa — inyectores, admission hooks, renovaciones de certificados, DNS — y haz que cada una falle fuerte.
- Asume que la nube va a elegir la peor semana para hacer mantenimiento, porque lo va a hacer.
La plataforma siguió creciendo después de ese viernes. También mi respeto por cada pequeño componente silencioso que nunca me había molestado en mirar.
El post del jueves jala un hilo distinto de la misma plataforma: autorización — y por qué esconderle una página a alguien no es lo mismo que quitarle el permiso. ✨ Nos vemos.