OOMKilled en Kubernetes: que es y como solucionarlo
OOMKilled es uno de esos errores que aparecen de repente en produccion y pueden dejar servicios caidos sin aviso. El nombre lo dice todo: Out Of Memory Killed. El kernel del sistema mato tu proceso porque se quedo sin memoria. En Kubernetes ocurre cuando un contenedor supera el limite de memoria definido en su spec. En este post te explico como diagnosticarlo y como solucionarlo. Si no tienes claro como funcionan los pods, repasa primero el post de Pods, Deployments y Services.
Que significa OOMKilled en Kubernetes
Cuando un contenedor supera el limite de memoria definido en su spec, el kernel de Linux activa el OOM Killer y termina el proceso. Kubernetes registra el estado como OOMKilled y, dependiendo de la restartPolicy, lo reinicia o no.
kubectl get pods
NAME READY STATUS RESTARTS AGE
mi-app-xyz 0/1 OOMKilled 3 5m
O puede aparecer como CrashLoopBackOff si lleva varios reinicios
Paso 1: Confirmar que es OOMKilled
kubectl describe pod mi-app-xyz
Busca en la seccion Containers - Last State:
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Exit code 137 = 128 + 9 (SIGKILL)
Siempre indica que el proceso fue terminado por el kernel
El exit code 137 es la firma del OOMKilled. Si ves ese codigo de salida, es memoria, no hay duda.
Paso 2: Ver cuanta memoria usaba el contenedor
kubectl top pods
NAME CPU(cores) MEMORY(bytes)
mi-app-xyz 50m 480Mi
kubectl top pods --containers
kubectl describe pod mi-app-xyz | grep -A 3 Limits
Limits:
memory: 512Mi
cpu: 500m
Causas mas comunes de OOMKilled en Kubernetes
Causa 1: El limite de memoria es demasiado bajo
La causa mas habitual. Se definio un limite conservador que la aplicacion supera bajo carga real. La regla general: el limite debe ser al menos un 20-30% mayor que el uso maximo observado.
Causa 2: Memory leak en la aplicacion
La aplicacion consume cada vez mas memoria hasta superar el limite. Se distingue porque la memoria sube de forma continua incluso sin carga, y el pod se reinicia cada pocas horas.
Causa 3: Picos de memoria bajo carga alta
La aplicacion funciona bien normalmente pero bajo carga alta tiene picos que superan el limite. Tipico en apps que hacen procesamiento en memoria: parseo de JSONs grandes, generacion de informes, etc.
Causa 4: Sin limite definido
Si no defines resources.limits.memory, el contenedor puede usar toda la memoria del nodo. El OOM Killer del kernel actua a nivel de nodo y puede matar cualquier proceso, no solo el contenedor culpable.
Soluciones para OOMKilled en Kubernetes
Solucion 1: Aumentar el limite de memoria
spec:
containers:
- name: mi-app
resources:
requests:
memory: 256Mi
cpu: 100m
limits:
memory: 512Mi
cpu: 500m
kubectl apply -f deployment.yaml
O directamente:
kubectl set resources deployment mi-app --containers=mi-app --limits=memory=512Mi
Solucion 2: Ajustar requests vs limits correctamente
- requests: la memoria que Kubernetes reserva en el nodo. Influye en el scheduling.
- limits: el maximo que puede usar el contenedor. Si lo supera, OOMKilled.
Buena practica: limit = 2x request para apps con picos. Para apps mas predecibles: limit = 1.5x request. Nunca pongas limit igual a request si la app tiene picos.
Solucion 3: VPA para ajuste automatico
El Vertical Pod Autoscaler analiza el uso real y ajusta los requests y limits automaticamente:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: mi-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: mi-app
updatePolicy:
updateMode: Auto
kubectl describe vpa mi-app-vpa
Solucion 4: Investigar y corregir memory leaks
- Revisar conexiones a base de datos que no se cierran
- Revisar listeners o handlers que no se limpian
- Usar herramientas de profiling: pprof para Go, heapdump para Node.js, jmap para Java
- Revisar caches en memoria sin limite de tamaño
Como monitorizar memoria para prevenir OOMKilled
Alerta Prometheus cuando el uso supera el 80% del limite:
container_memory_working_set_bytes
/ on(pod, container)
kube_pod_container_resource_limits{resource="memory"}
> 0.8
Ver uso de memoria ordenado:
kubectl top pods -n mi-namespace --sort-by=memory
Ver eventos OOMKilled en el cluster:
kubectl get events --field-selector reason=OOMKilling
Tabla resumen de diagnostico
| Sintoma | Causa | Solucion |
|---|---|---|
| Exit code 137, uso cerca del limite | Limite demasiado bajo | Subir memory limit |
| Memoria sube sin parar, reinicio cada horas | Memory leak | Corregir en la aplicacion |
| OOMKilled solo bajo carga alta | Picos de memoria | Subir limit o reducir concurrencia |
| OOMKilled sin limit definido | Sin limits.memory | Definir siempre limits.memory |
Conclusion
OOMKilled siempre tiene una causa concreta. El 80% de los casos son limite demasiado bajo o memory leak. Con kubectl describe pod y kubectl top pods tienes suficiente para diagnosticarlo en minutos. Define siempre resources.requests y resources.limits en tus contenedores — sin ellos Kubernetes no puede gestionar la memoria correctamente.
Si tu pod ademas aparece como CrashLoopBackOff revisa el post sobre CrashLoopBackOff en Kubernetes. Tienes alguna causa de OOMKilled que no aparece aqui? Cuentamela en los comentarios.
