El downtime en Kubernetes no ocurre donde esperas
Antes de hablar de soluciones, hay que nombrar las causas reales. Hemos documentado las fuentes de incidentes en clusters de producción que atendemos: el 62% de los eventos de degradación de servicio se originan en configuraciones incorrectas de Deployments (probes, resource limits, rolling update strategy). Solo el 15% tiene origen en fallas de infraestructura subyacente.
Los problemas más comunes: rolling updates que dejan el servicio sin réplicas disponibles, pods que entran en CrashLoopBackOff durante deploys por probes demasiado agresivas, y nodos sobrecomprometidos que activan el OOM Killer del sistema operativo en el peor momento posible.
Pod Disruption Budgets: la primera línea de defensa
El error más crítico que encontramos en clusters de producción es ejecutar rolling updates o mantenimiento de nodos sin definir PodDisruptionBudgets (PDB). Sin un PDB, Kubernetes puede terminar tantos pods simultáneamente como necesite durante una operación de drain o un rolling update, dejando el servicio sin réplicas disponibles.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
namespace: production
spec:
minAvailable: 2 # siempre mínimo 2 pods activos
selector:
matchLabels:
app: apiLa regla operacional: todo Deployment con más de una réplica que reciba tráfico de producción debe tener un PDB. Sin excepción. Con 3 réplicas y sin PDB, el Cluster Autoscaler puede drenar 2 nodos simultáneamente y dejar una sola réplica activa — exactamente cuando no quieres eso.
Rolling Update Strategy: maxSurge y maxUnavailable importan
La configuración default de rolling update en Kubernetes (maxUnavailable: 25%, maxSurge: 25%) puede dejar tu servicio con solo el 75% de capacidad durante un update. Para un Deployment con 4 réplicas y tráfico alto, eso puede ser suficiente para saturar las réplicas restantes.
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # nunca reducir réplicas durante el updateCon maxUnavailable: 0, Kubernetes primero levanta el pod nuevo (maxSurge: 1), espera que pase la readiness probe y solo entonces termina el pod antiguo. Esto garantiza que durante el update siempre tenemos al menos el número de réplicas deseadas.
Readiness y Liveness Probes: configuración que determina el uptime
Las probes mal configuradas son el killer silencioso del uptime. El escenario más frecuente: initialDelaySeconds demasiado bajo para la aplicación, la readiness probe pasa antes de que el pool de conexiones a la base de datos esté listo, el pod recibe tráfico, el primer request real falla, y si la liveness probe es también agresiva, el proceso entra en CrashLoopBackOff.
El /health/ready y /health/live deben ser endpoints semánticamente distintos. Ready verifica si el pod puede servir tráfico ahora (conexión a DB activa, dependencias disponibles). Live solo verifica si el proceso está vivo y sin deadlock.
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 3
successThreshold: 1
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 20
failureThreshold: 4Resource Requests y Limits: el equilibrio entre throttling y OOM
Sin resource requests definidos, el scheduler no tiene información para colocar pods óptimamente. Sin limits, un pod puede consumir toda la memoria del nodo y activar el OOM Killer del OS — que no distingue entre tu aplicación y componentes del sistema Kubernetes.
La estrategia que usamos en producción: medir durante al menos dos semanas con container_memory_working_set_bytes y container_cpu_usage_seconds_total en Prometheus. Requests = p50. Limits de memoria = p99.5 con 20% de margen.
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"Advertencia: limits de CPU agresivos generan throttling silencioso que se manifiesta como latencia elevada sin errores visibles. Monitorear container_cpu_cfs_throttled_seconds_total para detectarlo antes de que afecte SLAs.
Anti-Affinity y Distribución Multizona
Tres réplicas en el mismo nodo es alta disponibilidad aparente pero single point of failure real. Si el nodo falla, pierdes el 100% de la capacidad. Con topologySpreadConstraints puedes distribuir pods entre nodos y zonas de disponibilidad simultáneamente.
spec:
template:
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: apitopologySpreadConstraints es más flexible que podAntiAffinity. maxSkew: 1 significa que la diferencia máxima entre el número de pods en cualquier topología no puede superar 1. whenUnsatisfiable: DoNotSchedule hace que el pod espere hasta poder ubicarse en una topología equilibrada.
Graceful Shutdown: el cierre limpio que nadie configura
Cuando Kubernetes termina un pod, envía SIGTERM. La aplicación tiene terminationGracePeriodSeconds para cerrarse limpiamente. Si no maneja SIGTERM correctamente, las requests en vuelo se cortan abruptamente. El problema menos obvio: entre que Kubernetes envía SIGTERM y que el load balancer deja de enviar tráfico al pod, pueden pasar varios segundos.
spec:
terminationGracePeriodSeconds: 60
containers:
- name: api
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]El preStop con sleep 10 le da tiempo al kube-proxy para actualizar las iptables rules y dejar de enrutar tráfico al pod antes de que inicie el shutdown. Este patrón es crítico en servicios con requests de larga duración o conexiones WebSocket persistentes.
Monitoreo Proactivo: alertar antes del downtime
Las alertas que se disparan cuando ya hay CrashLoopBackOff son demasiado tardías. Para un SLO de 99.9%, la detección de degradación debe ocurrir antes del outage. Las alertas de Prometheus/Alertmanager que activamos en producción:
- PodRestartRateHigh — más de 3 restarts en 15 minutos (alerta antes del CrashLoopBackOff oficial de Kubernetes)
- DeploymentReplicasMismatch — réplicas disponibles < deseadas por más de 5 minutos
- ContainerCPUThrottling > 30% — throttling silencioso antes de que afecte latencia medible
- PodPendingTooLong — pod en Pending más de 5 minutos (señal de problema de scheduling o capacity)
- NodeNotReady — nodo no disponible (permite acción preventiva antes de que el scheduler redistribuya pods)
Preguntas frecuentes
¿Cuántas réplicas mínimas necesito para zero downtime en Kubernetes?
¿Qué hace exactamente el Pod Disruption Budget?
¿Por qué mis pods entran en CrashLoopBackOff después de un rollout?
¿Cuándo usar HPA vs. KEDA para autoescalado?
¿Cómo detecto si tengo CPU throttling en mis pods?
¿Tienes un cluster de Kubernetes en producción con incidentes recurrentes? Nuestro equipo puede hacer un audit técnico e identificar los riesgos específicos de tu infraestructura.
Habla con el equipo