Debug Playbook: Container OOMKilled
Symptom:
- Container
my-webapp is being terminated with OOMKilled status in Docker logs or Kubernetes events. - Terminations are intermittent and not consistently tied to specific load patterns.
Hypothesis List:
- Cgroup Memory Limit Exceeded: The container's allocated memory (cgroup limit) is insufficient for its workload.
- Application Memory Leak/Spike: The application itself has a memory leak or a transient memory spike that exceeds its available resources.
- JVM/Runtime Heap Misconfiguration: For applications using runtimes like JVM, the heap settings are not optimized for the container's memory limit.
- Sidecar/Helper Process Memory: Other processes within the container (e.g., agents, sidecars) are consuming unexpected memory.
- Host-Level OOM Killer: The host system itself is under memory pressure, leading to the host's OOM killer terminating processes, including Docker daemon or containers.
Checks (with Commands):
1. Review Container Logs for OOMKilled Event Details: * docker logs my-webapp * kubectl describe pod my-webapp-xyz12 -n default (if Kubernetes) * *Interpretation:* Look for OOMKilled messages, exit codes (e.g., 137), and any preceding memory-related warnings from the application. For instance, a Java application might log java.lang.OutOfMemoryError before the container is killed.
2. Check Docker Cgroup Memory Limits: * docker inspect my-webapp | grep -i 'Memory' * cat /sys/fs/cgroup/memory/docker/<container_id>/memory.limit_in_bytes (replace <container_id> with actual ID from docker ps for my-webapp) * *Interpretation:* Compare Memory (limit) with MemoryUsage (current usage). If usage is consistently near the limit (e.g., 90% or more) just before an OOM event, the limit is likely too low.
3. Monitor Container Memory Usage Over Time: * docker stats my-webapp --no-stream (for snapshot) * docker stats my-webapp (for real-time monitoring) * kubectl top pod my-webapp-xyz12 -n default --containers (if Kubernetes) * *Interpretation:* Observe memory trends. A gradual, continuous increase suggests a leak. A sudden, sharp spike indicates a transient high-memory operation. Note the peak usage recorded before the OOM.
4. Inspect Process-Level Memory Inside the Container: * docker exec -it my-webapp bash (or sh) * Once inside: ps aux --sort -rss * Once inside: top (then press M for memory sort) * *Interpretation:* Identify which specific process within the container is consuming the most memory. For a Java application, this will likely be the java process. This helps differentiate between the main application and any sidecars or helper scripts.
5. Analyze Application-Specific Memory (e.g., JVM Heap): * If Java is the application type, enable GC logging: -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps * Use jmap or jstat for heap analysis if possible (requires JDK inside container or remote JMX access). For example, jmap -heap <pid> inside the container. * *Interpretation:* Review gc.log for frequent full GC cycles, long pause times, and java.lang.OutOfMemoryError: Java heap space messages. Compare the JVM's configured maximum heap (-Xmx) to the container's cgroup memory limit, ensuring a buffer for non-heap memory.
6. Check Host System Memory: * free -h * dmesg | grep -i oom * *Interpretation:* Check free -h for overall host memory usage. dmesg output can reveal if the host's own OOM killer was triggered, which might indicate a broader system-level memory issue affecting multiple containers or the Docker daemon itself.
Likely Fixes:
- Increase Container Memory Limit: If cgroup limits are too restrictive, increase
memory and memory-swap in Docker run commands or Kubernetes resource limits (limits.memory). Start with a conservative increase (e.g., 25%) and monitor performance. - Optimize Application Memory Usage:
* Address identified memory leaks in the Java code through profiling tools. * Optimize data structures or algorithms to reduce memory footprint. * For Java, tune JVM heap (-Xmx, -Xms) and garbage collector settings. Ensure -Xmx is significantly less than the container's cgroup memory limit (e.g., 75-85% of the limit) to leave room for non-heap memory, native libraries, and OS overhead.
- Adjust Sidecar/Helper Process Resource Allocation: If other processes (e.g., log shippers, monitoring agents) are consuming significant memory, reconfigure their resource usage or move them to separate, dedicated containers.
- Scale Out/Distribute Load: If the workload genuinely requires more memory than a single container can efficiently handle, consider horizontal scaling of the
my-webapp service or implementing sharding strategies. - Upgrade Host Resources: If host-level OOM is occurring, the underlying server may need more physical RAM or a reduction in the number of containers deployed on it.
Verification Steps:
- Deploy Fix and Monitor: Apply the chosen fix (e.g., updated Docker run command, new Kubernetes deployment) and redeploy the
my-webapp container. - Observe Memory Metrics: Continuously monitor
docker stats my-webapp or kubectl top pod my-webapp-xyz12 for the my-webapp container over several hours or days. - Check Logs for OOMKilled Events: Verify that no new
OOMKilled events occur for my-webapp over a sustained period under typical load conditions. - Load Testing: Conduct load tests that simulate peak conditions to ensure the fix holds and prevents OOM events under stress.