Guía completa de límites de recursos en Docker: evita que una fuga de memoria tumbe el servidor

Suena la alarma del móvil: «servidor sin respuesta», «CPU al 100 %», «timeout de SSH». Te conectas por VPN tres veces antes de lograrlo, escribes ssh — timeout. El servidor está colgado.
Tras un reinicio forzado, los logs cuentan la historia: un contenedor que llevaba seis meses en marcha con fuga de memoria, de 500MB a 16GB, agotó toda la RAM del host; ni SSH tenía recursos para responder. Otros tres contenedores sanos cayeron con él — producción en llamas. En 2024, Docker 27.0.3 tuvo un bug grave de fuga de memoria que acabó con 68 contenedores de un solo golpe por el OOM Killer.
Este artículo resuelve una cosa: cómo evitar que un solo contenedor tumbe todo el servidor. Desde los cgroups en profundidad hasta --memory y --cpus en la práctica, y luego docker stats, cAdvisor y Prometheus, cubrimos los límites de recursos en Docker de punta a punta. Al terminar, al menos sabrás hacer que el contenedor caiga solo cuando aparezca una fuga de memoria, en lugar de sacrificar todo el host.
¿Por qué un contenedor puede tumbar el servidor?
Docker tiene una «característica»: por defecto, los contenedores no tienen límite de recursos. Suena liberador, ¿verdad? Tan liberador que un contenedor puede consumir toda la memoria y la CPU del host, y todos caen juntos.
Caso real con Docker 27.4.0: un usuario vio que dockerd pasó de unos cientos de MB a 8GB en pocos días, dejando el servidor casi inutilizable. No es que Docker sea inestable — el problema es que, si no pones límites, el contenedor es un caballo desbocado: consume lo que quiera.
Entonces el kernel de Linux activa un «asesino»: el OOM Killer (Out Of Memory Killer). Cuando la memoria se agota, elige el proceso «más adecuado» para matar y liberar RAM. ¿Cómo elige? Asigna una puntuación (oom_score) a cada proceso; cuanto más alta, más probable la muerte. Los procesos dentro de contenedores suelen puntuar alto, pero el daemon de Docker es más astuto y baja su prioridad OOM (oom_score_adj a -500), así que suelen morir tus contenedores.
En docker ps el contenedor desaparece; en los logs verás Exit Code 137. Eso significa: 128 + 9 (señal SIGKILL), en otras palabras, «asesinado a la fuerza». No es una salida limpia: es un corte del kernel.
Los síntomas de una fuga de memoria suelen ser estos:
- El uso de memoria del contenedor sube como un cohete, de cientos de MB a varios GB sin parar
- El servidor empieza a usar swap a lo loco; el disco parpadea sin descanso
- Otros contenedores responden cada vez más lento y acaban cayendo
- En tus gráficos de monitorización aparece una bonita recta ascendente
En 2024 hubo un caso llamativo en la comunidad Storj: un contenedor pasó de unos cientos de MB normales a 37GB y casi tumbó todo el nodo de almacenamiento. Con un límite de memoria de 1GB, el OOM Killer lo habría eliminado a tiempo y el servidor habría seguido estable.
[Imagen: gráfico de curva de fuga de memoria]
Prompt: server memory usage graph showing sharp upward spike, red critical zone at 90%, dark background, monitoring dashboard style, high quality
cgroups: el fundamento de los límites de recursos
Seguro que has oído que Docker usa «cgroups» para limitar recursos. ¿Qué son? En resumen, cgroups (Control Groups) es una función del kernel de Linux que asigna cuotas de recursos a grupos de procesos — como una «tarjeta de recursos» por proceso: cuando se agota la cuota, no hay más crédito.
Al crear un contenedor, Docker crea un cgroup y mete ahí sus procesos. Cuando ejecutas docker run -m 512m nginx, Docker escribe en /sys/fs/cgroup/memory/docker/<containerID>/memory.limit_in_bytes el valor 536870912 (512MB en bytes). El kernel lo lee y dice: este contenedor como máximo 512MB; si se pasa, fuera.
cgroups v1 frente a v2:
- v1: memoria, CPU y E/S de disco en subsistemas separados, cada uno por su lado
- v2: gestión unificada, jerarquía más clara, mejor para escenarios como contenedores que necesitan control global
- Sistemas antiguos (p. ej. RHEL 7) siguen en v1; los nuevos (Ubuntu 20.04+, RHEL 8+) usan v2
Si quieres ver la configuración real del cgroup de un contenedor:
# Obtener el ID completo del contenedor
docker inspect --format='{{.Id}}' my_container
# Ver límite de memoria (cgroups v1)
cat /sys/fs/cgroup/memory/docker/<containerID>/memory.limit_in_bytes
# Ver cuota de CPU (cgroups v1)
cat /sys/fs/cgroup/cpu/docker/<containerID>/cpu.cfs_quota_us
La primera vez que vi esos archivos me descolocó: ¿los límites se configuran con el sistema de archivos? Luego entendí la filosofía «todo es un archivo» de Linux: el kernel expone la configuración de cgroups como archivos; Docker escribe valores y el kernel aplica los límites. Diseño elegante.
[Imagen: diagrama de jerarquía de cgroups]
Prompt: Linux cgroups hierarchy diagram, containers grouped under docker cgroup, memory and CPU subsystems, tree structure, technical illustration, clean design, high quality
Parámetros de límite de memoria al detalle
Docker ofrece muchos parámetros de memoria, pero en la práctica solo importan unos pocos. Los repasamos uno a uno.
1. --memory / -m (límite duro, el más crítico)
Es el parámetro que salva vidas. Cuando el contenedor alcanza este valor, el OOM Killer actúa y el contenedor muere. El mínimo es 6MB (casi inútil para algo serio); en producción suele empezarse en cientos de MB.
# Limitar el contenedor a 512MB de memoria como máximo
docker run -m 512m nginx
# También puedes usar GB
docker run -m 2g my-app
¿Cómo elegir el valor? Mi regla: haz pruebas de carga y mira el uso normal de la app; multiplica por 1,2–1,5. Si usa 300MB en reposo, 400–450MB suele ir bien. Muy bajo y el contenedor muere a menudo; muy alto y pierdes protección.
2. --memory-swap (espacio de intercambio, fácil de malinterpretar)
Este parámetro confunde a muchos. Define el total memoria + swap, no el tamaño de swap por separado.
# 512MB de memoria + 512MB de swap (1GB en total)
docker run -m 512m --memory-swap 1g nginx
# Desactivar swap (solo memoria)
docker run -m 512m --memory-swap 512m nginx
# Swap ilimitado (¡peligroso!)
docker run -m 512m --memory-swap -1 nginx
Si no defines --memory-swap, por defecto swap = memory: el total disponible es el doble de memory. Con -m 512m, el contenedor puede usar hasta 1GB (512m RAM + 512m swap).
Recomendación en producción: desactiva swap (--memory-swap igual a --memory) o limita swap a la mitad de memory como máximo. No uses -1: un contenedor abusando de swap puede matar el disco.
3. --memory-reservation (límite blando)
Es una «cuota flexible». Con memoria de sobra en el host, el contenedor puede superar este valor; bajo presión, el kernel intenta mantenerlo por debajo. Debe ser menor que --memory.
# Límite blando 750MB, límite duro 1GB
docker run -m 1g --memory-reservation 750m nginx
Útil cuando la app a veces pide mucha memoria de golpe (p. ej. batch) pero en reposo consume poco.
4. --kernel-memory (memoria del kernel, usar con cuidado)
Limita la memoria del kernel usada por el contenedor (buffers de red, caché de sistema de archivos, etc.); esa memoria no puede ir a swap. En serio: si no sabes exactamente qué haces, no lo toques — puedes impedir que el contenedor arranque.
5. --oom-kill-disable (parámetro peligroso, atención)
Desactiva el OOM Killer para que el contenedor no muera al superar memoria. Suena bien, pero es un error grave. Si la memoria se descontrola y no hay kill, el host entero se queda sin RAM.
# ¡Así se rompe un servidor! (memoria sin límite efectivo)
docker run --oom-kill-disable nginx
# Si lo usas, combínalo siempre con límite de memoria
docker run -m 512m --oom-kill-disable nginx
¿Cuándo usarlo? Casi nunca. Solo si una app muy especial no puede ser matada de repente (p. ej. checkpoint de base de datos) y garantizas control de memoria en la aplicación.
Caso real: en AWS un usuario tumbó una instancia EC2; tras mucho rastreo, un contenedor sin límite de memoria llegó a 30GB y dejó la instancia sin respuesta. Con -m 2g, al tocar el límite el contenedor cae y reinicia, y el servidor sigue estable.
[Imagen: diagrama de relación entre parámetros de memoria]
Prompt: Docker memory parameters diagram, showing memory and swap relationship, visual chart with bars and labels, technical illustration, blue and orange colors, high quality
Parámetros de límite de CPU al detalle
Los límites de CPU son más «suaves» que los de memoria: no matan el proceso, solo lo frenan. Pero una CPU descontrolada también puede dejar el servidor inutilizable.
1. --cpus (la forma más intuitiva)
Indica cuántos núcleos de CPU puede usar el contenedor; admite decimales.
# Como máximo 1,5 núcleos de CPU
docker run --cpus="1.5" nginx
# Solo medio núcleo
docker run --cpus="0.5" my-app
Por debajo usa --cpu-period y --cpu-quota; Docker calcula la proporción. Con 1,5, el contenedor puede usar como máximo la capacidad de 1,5 núcleos — un núcleo al 100 % más otro al 50 %.
2. --cpu-shares (peso relativo, no límite duro)
Define la prioridad en la planificación de CPU; el valor por defecto es 1024. Clave: solo importa cuando la CPU está saturada. Si el host tiene CPU libre, el contenedor puede usar lo que necesite.
# El contenedor A recibe el doble de tiempo de CPU que B
docker run --cpu-shares 2048 --name app_a my-app
docker run --cpu-shares 1024 --name app_b my-app
Con 2 núcleos al 100 %, A y B se reparten en proporción 2:1 — A ~1,33 núcleos, B ~0,67. Con CPU holgada, ambos pueden ir a toda velocidad.
¿Cuándo usarlo? Varios contenedores y quieres priorizar un servicio crítico (p. ej. API frente a tareas en segundo plano).
3. --cpuset-cpus (anclar a núcleos concretos)
Fija el contenedor a ciertos núcleos; el resto queda fuera.
# Solo los núcleos 0 y 3
docker run --cpuset-cpus="0,3" nginx
# Núcleos del 2 al 5
docker run --cpuset-cpus="2-5" my-app
Casos de uso:
- Arquitectura NUMA: en servidores multi-socket, anclar al mismo socket reduce accesos remotos a memoria
- Evitar invalidación de caché: procesos en núcleos fijos mejoran la tasa de acierto de caché
- Aislar servicios críticos: núcleos dedicados para cargas importantes
En un entorno Kubernetes vi anclar la base de datos a los 4 últimos núcleos de un host de 8 y dejar los 4 primeros al servicio web; la mejora fue notable.
4. --cpu-period y --cpu-quota (control fino)
Son parámetros de bajo nivel; --cpus los encapsula.
--cpu-period: periodo del planificador CFS, por defecto 100000 microsegundos (100 ms)--cpu-quota: microsegundos de CPU permitidos por periodo
# En 100 ms, solo 50 ms de CPU (equivalente a 0,5 núcleos)
docker run --cpu-period=100000 --cpu-quota=50000 nginx
# Equivalente a
docker run --cpus="0.5" nginx
En la mayoría de casos basta --cpus; los parámetros finos sirven si necesitas periodos más cortos y planificación más frecuente.
Caso real: un bug dejó un pool de hilos en bucle infinito; la CPU del contenedor llegó al 800 % (host de 8 núcleos). Sin límites, todo el servidor colapsó y ni SSH respondía. Tras poner --cpus="2" en todos los contenedores, un bug similar solo ralentiza al culpable; el resto sigue estable.
[Imagen: comparación de pruebas de límite de CPU]
Prompt: CPU usage comparison chart, with and without limits, before/after graph showing CPU spike prevention, performance monitoring dashboard, clean visualization, high quality
Plan de monitorización en la práctica
Poner límites es solo el primer paso; también debes saber cuánto consume cada contenedor. Si descubres la fuga cuando ya disparó el OOM, vas tarde.
Herramienta 1: docker stats (integrada, coste cero)
Lo más simple; viene con Docker.
# Actualización en tiempo real; Ctrl+C para salir
docker stats
# Una sola salida, útil en scripts
docker stats --no-stream
# Solo contenedores concretos
docker stats nginx_container mysql_container
La salida se ve así:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O
a1b2c3d4e5f6 nginx 0.50% 45.2MiB / 512MiB 8.83% 1.2kB / 0B
Ventajas: listo para usar, sin instalación.
Inconvenientes: solo estado actual, sin histórico, sin alertas ni visualización rica. Sirve para diagnóstico puntual, no para monitorización a largo plazo.
Herramienta 2: cAdvisor (de Google, experto en contenedores)
cAdvisor (Container Advisor) detecta todos los contenedores del host, recoge CPU, memoria, red, E/S de disco, ofrece interfaz web y endpoint de métricas en formato Prometheus.
Arranque sencillo:
docker run -d \
--name=cadvisor \
--restart=always \
-p 8080:8080 \
-v /:/rootfs:ro \
-v /var/run:/var/run:ro \
-v /sys:/sys:ro \
-v /var/lib/docker/:/var/lib/docker:ro \
-v /dev/disk/:/dev/disk:ro \
gcr.io/cadvisor/cadvisor:latest
Tras el arranque, http://IP-del-servidor:8080 muestra tendencias por contenedor; http://IP-del-servidor:8080/metrics expone datos para Prometheus.
Ventajas: completo, integrable con Prometheus.
Inconvenientes: retiene solo ~2 minutos de datos; para histórico necesitas Prometheus.
Herramienta 3: Prometheus + Grafana (esquema de nivel empresarial)
Stack completo:
- cAdvisor: recolección de métricas de contenedores
- Prometheus: scrape y almacenamiento
- Grafana: visualización y alertas
Configuración completa en docker-compose.yml:
version: '3.8'
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
restart: always
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
restart: always
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
restart: always
volumes:
prometheus_data:
grafana_data:
prometheus.yml de apoyo:
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
Tras el despliegue:
- Prometheus scrapea cAdvisor cada 15 segundos
- Grafana en
http://IP-del-servidor:3000(admin/admin por defecto) - Añade Prometheus como fuente de datos (
http://prometheus:9090) - Importa un dashboard (recomendado ID 19908, pensado para contenedores Docker)
Métricas clave:
container_memory_usage_bytes: memoria actual del contenedorcontainer_memory_max_usage_bytes: pico histórico de memoriacontainer_cpu_load_average_10s: carga media de CPU a 10 scontainer_fs_io_time_seconds_total: tiempo de E/S en disco
Configura una alerta cuando el uso de memoria supere el 80 % (email, Slack, etc.) y detectarás el problema antes de que explote.
Montar todo lleva trabajo, pero compensa. Yo monitorizo más de 20 contenedores con Grafana y hace tiempo que no me despiertan a las 3 de la madrugada.
[Imagen: panel de monitorización de contenedores en Grafana]
Prompt: Grafana dashboard showing Docker container metrics, memory and CPU graphs, clean modern UI, dark theme, monitoring panels with colorful charts, high quality
Flujo completo para diagnosticar fugas de memoria
Suena la alerta o el contenedor cae sin aviso: sigue este flujo para localizar el problema rápido.
Paso 1: detectar la anomalía
Primero, docker stats para ver qué contenedor dispara la memoria:
docker stats --no-stream | grep -v "0.00%"
¿Un contenedor cerca del límite? Comprueba si el OOM Killer actuó:
# Eventos OOM del contenedor
docker events --filter 'event=oom' --since '24h'
# Código de salida del contenedor
docker inspect <nombre-contenedor> --format='{{.State.ExitCode}}'
# Si devuelve 137, el OOM Killer lo mató
Paso 2: analizar el uso de memoria
Entra al contenedor y mira qué proceso consume:
# Entrar al contenedor
docker exec -it <nombre-contenedor> /bin/bash
# Ranking por memoria (si hay top/htop)
top -o %MEM
# O con ps
ps aux --sort=-%mem | head -n 10
En apps Java, puedes exportar un heap dump:
# PID del proceso Java
jps
# Exportar heap dump
jcmd <PID> GC.heap_dump /tmp/heap.hprof
# Copiar el archivo fuera
docker cp <nombre-contenedor>:/tmp/heap.hprof ./
Paso 3: respuesta de emergencia
Si el servicio ya está afectado, alivia de inmediato:
# Reiniciar el contenedor (pierdes datos temporales dentro)
docker restart <nombre-contenedor>
# Si sigue en marcha, ajustar límite de memoria al vuelo
docker update --memory 1g --memory-swap 1g <nombre-contenedor>
# Limpiar recursos no usados (cuidado: borra imágenes y contenedores huérfanos)
docker system prune -a
Paso 4: solución de fondo
Los parches temporales solo contienen la hemorragia; para curar:
- Corregir el código: localizar la fuga (conexiones sin cerrar, caché sin límite, objetos grandes retenidos, etc.)
- Poner límites: si aún no los tienes, añade
--memoryya - Desplegar monitorización: Prometheus+Grafana para alertar antes del desastre
- Reinicio automático:
--restart=on-failure:3, hasta 3 reinicios tras OOM
Lista rápida de comandos:
# Límites configurados en todos los contenedores
docker ps --format "{{.Names}}" | xargs docker inspect \
--format='{{.Name}}: Memory={{.HostConfig.Memory}} CPU={{.HostConfig.NanoCpus}}'
# Historial de reinicios del contenedor
docker inspect --format='{{.RestartCount}}' <nombre-contenedor>
# Últimas 100 líneas de log
docker logs --tail 100 <nombre-contenedor>
# Detalle de uso de memoria
docker stats --no-stream --format \
"table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}" <nombre-contenedor>
Repaso del caso Storj (37GB): el usuario hizo esto:
- Tras ver la subida de memoria,
docker restartpara recuperar el servicio - Añadió
-m 1gpara no volver a tumbar el host - Exportó logs y heap dump al equipo de desarrollo
- Tras el parche, actualizó la imagen y redesplegó
- Desplegó cAdvisor con alerta al 70 % de memoria
Fue pesado, pero desde entonces no se repitió.
[Imagen: diagrama de flujo de diagnóstico de memoria]
Prompt: flowchart showing memory leak diagnosis process, step by step from detection to resolution, arrows connecting boxes, clean infographic style, blue and green colors, high quality
Mejores prácticas y trampas a evitar
Resumen de cómo usar esto bien en producción.
Lista obligatoria en producción
✅ Todo contenedor debe tener límite de memoria
No confíes en la suerte. Aunque sea un Nginx estático, ponle 512MB. Mejor conservador que lamentar después.
✅ En desarrollo, simula los límites de producción
No corras sin límites en local y descubras en producción que falta memoria. En dev, usa ~80 % de los valores de prod y detecta problemas antes.
✅ Revisa el uso con regularidad
Una vez al mes mira docker stats; las cargas cambian — sube o baja recursos según toque.
❌ No desactives el OOM Killer
Salvo que estés 100 % seguro de que la app controla su memoria, no uses --oom-kill-disable. Es un parámetro suicida.
❌ No uses swap ilimitado
--memory-swap -1 tienta, pero es una mina. El swap masivo mata el disco; a veces es mejor que el OOM Killer actúe.
❌ No pongas límites demasiado bajos
Por debajo de lo que la app necesita tendrás OOM constantes y peor disponibilidad. Haz pruebas de carga y luego fija el límite.
La forma correcta en Docker Compose
services:
web:
image: nginx:latest
deploy:
resources:
limits:
cpus: '1.5'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart: on-failure:3
Aquí deploy (sintaxis Compose v3). limits es duro; reservations es blando. En reposo 256MB puede bastar; en picos hasta 512MB.
Gestionar muchos contenedores a la vez
Para un grupo de microservicios con un pool común de recursos, cgroup-parent:
# Crear un cgroup padre con límite total
docker run --cgroup-parent=/my-services -m 2g service-a
docker run --cgroup-parent=/my-services -m 2g service-b
# Ambos comparten el cgroup padre; la memoria total no supera el límite del padre
Útil cuando varios contenedores forman una unidad de negocio y quieres controlar el conjunto.
Fórmulas orientativas de límites
| Tipo de aplicación | Memoria sugerida | CPU sugerida |
|---|---|---|
| Nginx estático | 256-512MB | 0,5-1 núcleo |
| API Node.js | 512MB-1GB | 1-2 núcleos |
| Microservicio Java | 1-2GB | 2-4 núcleos |
| Base de datos (MySQL/PostgreSQL) | 2-4GB | 2-4 núcleos |
| Cola de mensajes (RabbitMQ/Kafka) | 1-2GB | 1-2 núcleos |
Son estimaciones conservadoras; el tráfico real manda. En pruebas de carga observa el pico y multiplica por ~1,5 para el límite.
Comparación con la gestión de recursos en Kubernetes
Si has usado Kubernetes, requests y limits se parecen a Docker:
- requests: parecido a
--memory-reservation - limits: parecido a
--memory
K8s centraliza la configuración en YAML; Docker es más flexible (docker update al vuelo).
Último consejo
Los límites no son «configurar una vez y olvidar». La app evoluciona, el tráfico crece y los datos de monitorización te dirán si ampliar recursos u optimizar código. Revisa de vez en cuando; no dejes la configuración como adorno.
[Imagen: tabla comparativa de buenas prácticas de límites de recursos]
Prompt: comparison table showing Docker resource limits best practices, checkmarks and crosses, clean infographic style, professional layout, high quality
Resumen
Volvamos al inicio: despertar a las 3 de la madrugada porque el servidor cayó por un contenedor. Esa lección dolorosa me enseñó que la «libertad» por defecto de Docker es una trampa. Sin límites explícitos, le entregas al contenedor el poder de vida o muerte sobre el host.
Mirando atrás, la defensa es clara:
Primera línea: límites de recursos (prevención)
Pon --memory y --cpus en cada contenedor, como una rienda al caballo. Si se descontrola, cae solo sin arrastrar al servidor. Es el paso más básico y el más importante.
Segunda línea: monitorización y alertas (detección)
docker stats para el presente; cAdvisor + Prometheus + Grafana para tendencias e histórico. Alerta al 80 % de memoria y puedes ver la señal 48 horas antes del desastre.
Tercera línea: flujo de diagnóstico (respuesta)
Si pasa algo: detectar → analizar → contener → arreglar de raíz. Sin pánico; los comandos están en el artículo.
Desde cgroups hasta el detalle de --memory-swap, desde Compose hasta la comparación con Kubernetes, aquí está lo esencial de los límites en Docker. Lo que falta es actuar.
Haz estas tres cosas ahora:
- Revisa producción con este comando y mira qué contenedores no tienen límite:
docker ps --format "{{.Names}}" | xargs docker inspect \
--format='{{.Name}}: Memory={{.HostConfig.Memory}} CPU={{.HostConfig.NanoCpus}}'
Memory=0 significa bomba de relojería.
-
Despliega monitorización: copia el docker-compose.yml del artículo; en 30 minutos lo tienes.
-
Pon un recordatorio mensual: 1 hora con
docker statspara revisar si el uso es razonable.
No quiero que aprendas como yo, con una alarma a las 3 de la madrugada. Cuanto antes configures límites, menos problemas. No esperes al incidente.
Flujo completo de configuración de límites de recursos en Docker
Evita que una fuga de memoria en un contenedor tumbe el servidor: desde cgroups hasta --memory y --cpus en la práctica, y luego el plan de monitorización
⏱️ Estimated time: 1 hr
- 1
Step 1: Entender la gravedad del problema y los cgroups
Gravedad del problema:
• Una fuga de memoria en un contenedor pasó de 500MB a 16GB
• Agotó toda la RAM del servidor; ni SSH respondía
• Otros contenedores en ejecución también cayeron; el entorno de producción se vino abajo
Principio de cgroups:
• Los cgroups del kernel de Linux controlan el uso de recursos de los contenedores
• Docker limita CPU, memoria, E/S, etc. mediante cgroups
• Evita que un solo contenedor tumbe todo el servidor
En 2024, Docker 27.0.3 tuvo un bug grave de fuga de memoria que acabó con 68 contenedores de un solo golpe por el OOM Killer del kernel de Linux. - 2
Step 2: Configurar límites de recursos: memoria y CPU
Límite de memoria:
• Usa --memory: docker run --memory=512m container-name
• Configura en docker-compose: deploy.resources.limits.memory: 512m
• Define límite de swap: --memory-swap
Límite de CPU:
• Usa --cpus: docker run --cpus=1.0 container-name
• Usa --cpu-shares para el peso de CPU
• En docker-compose: deploy.resources.limits.cpus: '1.0'
Verificación:
• Usa docker stats para ver el uso de recursos
• Confirma que los límites están activos - 3
Step 3: Desplegar monitorización y buenas prácticas
Plan de monitorización:
• docker stats en tiempo real: docker stats container-name
• cAdvisor ofrece una interfaz visual:
docker run -d -p 8080:8080 --name=cadvisor google/cadvisor
• Prometheus+Grafana para alertas de nivel producción:
configura Prometheus para scrapear métricas de cAdvisor y visualízalas en Grafana
Buenas prácticas:
• En producción, los límites de recursos son obligatorios
• Define valores razonables (memoria, CPU)
• Configura alertas de monitorización
• Revisa periódicamente el uso de recursos (dedica 1 hora al mes a docker stats)
• Actúa en cuanto detectes anomalías
FAQ
¿Por qué hay que configurar límites de recursos en Docker?
• Una fuga de memoria en un contenedor pasó de 500MB a 16GB
• Agotó toda la RAM del servidor; ni SSH respondía
• Otros contenedores en ejecución también cayeron; el entorno de producción se vino abajo
En 2024, Docker 27.0.3 tuvo un bug grave de fuga de memoria que acabó con 68 contenedores de un solo golpe por el OOM Killer del kernel de Linux.
Los límites evitan que un solo contenedor tumbe el servidor: cuando aparece una fuga de memoria, el contenedor cae solo en lugar de arrastrar a todo el host.
¿Cómo configurar límites de recursos en contenedores Docker?
• Usa --memory: docker run --memory=512m container-name
• Configura en docker-compose: deploy.resources.limits.memory: 512m
• Define límite de swap: --memory-swap
Límite de CPU:
• Usa --cpus: docker run --cpus=1.0 container-name
• Usa --cpu-shares para el peso de CPU
• En docker-compose: deploy.resources.limits.cpus: '1.0'
Verificación:
• Usa docker stats para ver el uso de recursos
• Confirma que los límites están activos
¿Cómo monitorizar el uso de recursos de contenedores Docker?
docker stats en tiempo real:
• docker stats container-name
cAdvisor ofrece una interfaz visual:
• docker run -d -p 8080:8080 --name=cadvisor google/cadvisor
Prometheus+Grafana para alertas de nivel producción:
• Configura Prometheus para scrapear métricas de cAdvisor
• Visualiza en Grafana
Despliegue: copia el docker-compose.yml del artículo y ejecútalo; en 30 minutos lo tienes listo.
Revisión periódica: dedica 1 hora al mes a docker stats y comprueba si el uso de recursos es razonable.
¿Cuáles son las mejores prácticas para límites de recursos en Docker?
• En producción, los límites de recursos son obligatorios
• Define valores razonables (memoria, CPU)
• Configura alertas de monitorización
• Revisa periódicamente el uso de recursos (dedica 1 hora al mes a docker stats)
• Actúa en cuanto detectes anomalías
Cuanto antes configures los límites, menos dolores de cabeza. Pon un recordatorio en el calendario: 1 hora al mes con docker stats para revisar si el uso es razonable.
17 min de lectura · Publicado el: 18 dic 2025 · Actualizado el: 21 ago 2026
Guía práctica de Docker
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Guía completa de Docker Secrets: mejores prácticas para gestionar contraseñas y claves API en contenedores
Deja de escribir la contraseña de la base de datos en el Dockerfile. Esta guía explica cómo gestionar información sensible con Docker Secrets, compara Docker/K8s/Vault y ofrece un checklist completo para producción.
Parte 31 de 38
Siguiente
Comando docker logs: 7 trucos para localizar problemas en contenedores
Guía del comando docker logs con 7 trucos prácticos: visualización en tiempo real, filtrado por tiempo, búsqueda con grep, ubicación de archivos de log y buenas prácticas en producción para localizar problemas en contenedores.
Parte 33 de 38



Comentarios
Inicia sesión con GitHub para dejar un comentario