IT-Admin.tech

Resolución de problemas de almacenamiento en contenedores: trampas de OverlayFS, device-mapper y loopback

Operator analysiert ein textfreies Diagramm zu Container-Storage mit OverlayFS-Layern, device-mapper Thin-Pool und...
Das Storage-Backend entscheidet oft über Performance und „no space left“-Fehler – besonders bei OverlayFS-Layern, Thin-Pools und Loopback-Setups.

Los contenedores rara vez funcionan lentos o inestables por “Docker en sí” — con mucha frecuencia la configuración de almacenamiento subyacente es la causa real. Esta solución de problemas de almacenamiento de contenedores se centra en tres áreas problemáticas clásicas en operación: OverlayFS (normalmente usado como overlay2), device-mapper (devicemapper/thin provisioning) y las particularmente engañosas configuraciones loopback. El objetivo es un runbook que pueda usar también bajo presión: clasificar síntomas, acotar causas, verificar con pasos de comprobación fiables y aplicar cambios de modo que exista una vía de retorno.

Importante: Muchos problemas de almacenamiento inicialmente parecen “errores de aplicación” (timeouts, errores 500, build falla), pero en realidad son latencia de I/O, cuellos de botella en metadatos o una combinación defectuosa de controlador/sistema de archivos. Quien separe esto de forma rigurosa ahorra horas en llamadas de incidentes.

Cómo funciona el almacenamiento de contenedores internamente (visión breve para la solución de problemas)

Los motores de contenedores como Docker almacenan imágenes y capas de escritura de contenedores en un Storage-Backend. El Storage-Driver determina cómo se implementan las “layers” (capas de imagen de solo lectura) y la capa de escritura del contenedor. En Linux overlay2 (OverlayFS) es hoy el estándar; configuraciones más antiguas usan a veces devicemapper. Además existen los Volumes (datos persistentes, normalmente como ruta del host o mediante plugins de volumen), que son independientes del layering de imágenes.

Para la solución de problemas la distinción es central:

  • Capas de imagen/contenedor (Storage-Driver): builds, pull/push, tiempos de arranque, “copy-on-write”.
  • Volumes/Bind-Mounts: bases de datos, uploads, datos de colas, directorios de logs — a menudo el punto caliente de rendimiento.
  • Sistema de archivos & Blockdevice: XFS/ext4, LVM, RAID, SAN, virtio, NVMe — aquí surgen picos de latencia y límites de metadatos.

Síntomas: Cómo detectar pronto problemas de almacenamiento

Las señales de advertencia típicas en la operación de contenedores son sorprendentemente repetibles. Preste especial atención a estos patrones:

  • “no space left on device”, aunque df -h todavía muestre espacio (frecuentemente inodos, Thin-Pool lleno, o metadatos de Overlay).
  • Builds extremadamente lentos con muchos archivos pequeños (operaciones de metadatos, costes de copy-up en OverlayFS).
  • Contenedores que arrancan con retraso o se quedan bloqueados en pasos de init intensivos en I/O.
  • Alta iowait en el host, acompañada de timeouts en las aplicaciones.
  • Errores poco claros en el log del Docker daemon relacionados con mounts, “failed to mount overlay”, “Invalid argument”.
  • Caída repentina del rendimiento tras una actualización de kernel/distribución o migración de sistema de archivos (p. ej. opciones XFS, d_type/ftype).

Evaluación inicial: ¿Qué configuración de almacenamiento está en uso?

Textfreie Grafik: Datenpfade von Blockdevice und Filesystem zu DockerRootDir und den Storage-Drivern overlay2 und...
Chequeo rápido: ¿Dónde residen los datos de Docker y qué driver depende de ellos?

Antes de depurar detalles, establezca una línea base: controlador de almacenamiento, Root-Dir, Filesystem, opciones de montaje y si están afectados volúmenes o capas.

Comprobar docker info y rutas

Shell
docker info --format 'Driver={{.Driver}}
DockerRootDir={{.DockerRootDir}}
BackingFilesystem={{.BackingFilesystem}}
SupportsDType={{.DriverStatus}}'

docker info | sed -n '/Storage Driver/,$p' | sed -n '1,80p'

Por qué ayuda: El controlador de almacenamiento delimita la clase de problema. DockerRootDir muestra dónde están los datos (por defecto: /var/lib/docker). El Filesystem subyacente decide si OverlayFS funciona correctamente.

Filesystem y opciones de montaje en DockerRootDir

Shell
DOCKER_DIR=$(docker info --format '{{.DockerRootDir}}')

df -Th "$DOCKER_DIR"
findmnt -no SOURCE,FSTYPE,OPTIONS -T "$DOCKER_DIR"

# Inodes: wichtig bei "no space left" trotz freiem Speicher
df -ih "$DOCKER_DIR"

Interpretación: Para OverlayFS son habituales XFS o ext4. XFS debe soportar d_type (históricamente visible en Docker como ftype=1). Los inodos suelen ser el verdadero cuello de botella cuando hay muchos archivos pequeños y capas.

OverlayFS/overlay2: causas comunes, pasos de comprobación, soluciones

Admin zeigt auf eine skizzierte Schichtenstruktur für OverlayFS-Layering am Arbeitsplatz.
Los problemas de OverlayFS suelen aparecer con muchas operaciones de metadatos y efectos de Copy-up.

OverlayFS es un mecanismo del kernel que „superpone“ dos niveles de directorios: una cadena de lowerdir (capas de la imagen, solo lectura) y un upperdir (capa de escritura del contenedor). Cuando un contenedor modifica un archivo de una capa inferior, ocurre un Copy-up: el archivo se copia al nivel superior y allí se modifica. Esto es funcionalmente robusto, pero tiene características claras en rendimiento y metadatos.

Punto conflictivo 1: XFS sin d_type (ftype=0) o „Invalid argument“

Un clásico: Docker arranca, pero en ciertas operaciones aparecen errores de montaje o el arranque de contenedores falla. Trasfondo: OverlayFS necesita d_type (Directory Entry Type) para reconocer tipos de archivo de forma fiable. En XFS es una opción de formateo; en ext4 en la práctica suele no ser problemático.

Shell
# Nur sinnvoll, wenn das Backing-FS XFS ist:
# Ausgabe: ftype=1 ist gut; ftype=0 ist problematisch.
XFS_DEV=$(findmnt -no SOURCE -T "$(docker info --format '{{.DockerRootDir}}')")

# xfs_info erwartet das Blockdevice oder den Mountpoint
xfs_info "$(findmnt -no TARGET -T "$(docker info --format '{{.DockerRootDir}}')")" | tr ' ' 'n' | grep -E '^ftype=' || true

Solución en la práctica: ftype no es „conmutable“. Si XFS se formateó con ftype=0, la única opción es migrar a un filesystem formateado correctamente (p. ej. un nuevo XFS con ftype=1 o ext4) y volver a configurar el Docker-Data-Root. Planifique obligatoriamente tiempo de inactividad y un repull de imágenes (véase la sección „Migration und Rückfallstrategie“).

Punto conflictivo 2: „no space left“ por escasez de inodos o metadatos

Overlay2 erzeugt viele Verzeichnisse und Metadaten pro Layer. Bei CI-Workloads oder Build-Hosts ist Inode-Erschöpfung häufiger als echter Kapazitätsmangel.

Shell
DOCKER_DIR=$(docker info --format '{{.DockerRootDir}}')

df -h "$DOCKER_DIR"
df -ih "$DOCKER_DIR"

# Hotspot-Suche: wo liegen besonders viele Einträge?
# (Kann dauern, bei Bedarf außerhalb der Peak-Zeit laufen lassen.)
sudo du -x -d 2 -h "$DOCKER_DIR" 2>/dev/null | sort -h | tail -n 30

Por qué falla: En algunos sistemas de archivos/particiones los inodos son fijados. Incluso si hay 200 GB libres, una cuota de inodos completa puede bloquear de forma estricta las operaciones de escritura. Además, los mecanismos de aprovisionamiento fino o de cuotas a nivel de VM/SAN pueden producir efectos similares.

Trampa 3: Caídas de rendimiento por copy-up y „chown -R“ en imágenes

Muchos administradores detectan el problema primero en la aplicación. Lo típico, sin embargo, es una imagen/entrypoint que al arrancar ajusta permisos de forma recursiva o modifica directorios grandes. En OverlayFS eso provoca copy-ups y actualizaciones de metadatos – en muchos archivos pequeños es costoso.

Compruebe si el punto crítico está en la capa de contenedor o en volúmenes. Un indicador rápido es la carga de I/O al arrancar y si las rutas están dentro de bind-mounts. En entornos productivos aplica: los directorios de datos (p. ej. bases de datos) deben residir en volúmenes, no en la capa de escritura del contenedor.

Comprobaciones de OverlayFS que realmente ayudan en un incidente

Shell
# Kernel- und OverlayFS-Sichtbarkeit
uname -r
lsmod | grep -i overlay || true

# Docker-Daemon-Logs (systemd)
sudo journalctl -u docker --since "-2h" | tail -n 200

# Aktive Mounts: Overlay-Mounts zeigen lowerdir/upperdir/workdir
mount | grep -E ' type overlay ' | head -n 20

Interpretación: Con frecuencia verá aquí directamente „failed to mount overlay“ o errores del kernel que indican opciones incompatibles, cadenas demasiado largas de lowerdir (raro hoy en día) o metadatos dañados. Si los mounts aumentan masivamente, también merece la pena revisar fugas de contenedores (contenedores que no se eliminan, temporäre Builder que se llenan).

device-mapper/devicemapper: Thin-Pool, metadatos y trampas operativas

Textfreie Grafik eines device-mapper Thin-Pools mit getrennten Bereichen für Daten und Metadaten.
En devicemapper los metadatos son tan críticos como la capacidad.

device-mapper es un Linux-subsistema del kernel que virtualiza dispositivos de bloque. En el contexto de Docker, devicemapper suele implementarse como aprovisionamiento fino: datos y metadatos se gestionan en un Thin-Pool. Esto puede funcionar de forma estable, pero es sensible a una provisión incorrecta (Pool/Meta lleno), lagunas en el monitoreo y – muy especialmente – configuraciones loopback.

Importante: el loopback en devicemapper es casi siempre un antipatrón

En loopback los “virtual blockdevices” existen como archivos en un sistema de ficheros. Sobre ellos se construye luego devicemapper. Eso añade latencia, efectos de caché y modos de fallo. En pruebas está bien, pero en operación continua es una causa frecuente de problemas de rendimiento, picos de I/O y errores difíciles de explicar como “Device is busy”.

Leer el estado de devicemapper desde Docker

Shell
docker info | sed -n '/Storage Driver: devicemapper/,$p' | sed -n '1,120p'

Fíjese aquí en términos como Pool Name, Data file/Metadata file (indicio de loopback) así como Deferred Removal/Deferred Deletion.

Comprobar nivel de llenado del thin-pool y metadatos (lvs)

Si devicemapper se ejecuta sobre LVM (frecuente), los LVM-Thin-Pools son el núcleo. No solo importa la proporción de datos, sino también los metadatos (tablas de mapeo). Metadatos llenos significa: parada de escrituras, a menudo de forma abrupta.

Shell
# Übersicht über LVM Thin Pools und deren Auslastung
sudo lvs -a -o +devices,lv_size,data_percent,metadata_percent,segtype,lv_attr,origin,pool_lv

Interpretación: data_percent cercano al 100% es crítico. metadata_percent cercano al 100% suele ser aún más crítico, porque entonces incluso cambios “pequeños” fallan. Planifique monitorización/alertas para ambos valores.

Comprobación directa con dmsetup (si la información de LVM no basta)

Shell
sudo dmsetup status
sudo dmsetup ls --tree

Por qué ayuda: dmsetup muestra también estados que Docker no hace visibles (p. ej. acumulación de deferred deletion). Con un churn fuerte de contenedores, el “deferred deletion” puede crear la ilusión de que el espacio nunca se libera.

Patrones de fallo típicos con devicemapper

  • Pool/Meta lleno: Los contenedores no arrancan, los pulls fallan, “no space left” a pesar de que la partición del host parezca libre.
  • Latencia por loopback: timeouts esporádicos, iowait elevado, especialmente en compilaciones paralelas.
  • Inesperados “Device is busy”: operaciones de limpieza quedan bloqueadas cuando los devices aún están referenciados.

Detectar y evaluar específicamente las trampas de loopback

Loopback no es solo “más lento”: también altera el diagnóstico de fallos. Entonces hay dos niveles en los que puede escasear espacio: el sistema de ficheros que contiene los archivos loopback y el thin-pool encima. Además, la fragmentación y el journaling del sistema de ficheros pueden aumentar la latencia.

¿Realmente es loopback?

Shell
docker info | sed -n '/Storage Driver/,$p' | sed -n '1,160p'

# Loop-Devices auf dem Host sichtbar?
losetup -a || true
lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS | sed -n '1,120p'

Interpretación: Si ve muchos /dev/loop* y Docker usa devicemapper, la probabilidad de que intervenga loopback es alta. Con overlay2 loopback normalmente no es relevante (salvo que ejecute Docker sobre una capa de loopback, p. ej. entornos de laboratorio anidados).

Decisión: medida inmediata vs corrección sostenible

En un incidente el objetivo suele ser estabilizar: aliviar la presión sobre el sistema antes de migrar. A largo plazo casi siempre la solución es: eliminar loopback, estandarizar el storage driver y colocar los datos de Docker en un blockdevice adecuado.

Runbook: secuencia de pasos para troubleshooting reproducible de storage en contenedores

La siguiente secuencia de comprobaciones está diseñada para que pueda decidir rápidamente si se trata de limpieza, capacidad o de una migración estructural.

1) Aclarar el alcance: ¿Layer-Storage o Volume-Storage?

Shell
# Welche Container schreiben besonders viel? (Grobindikator über Log/IO nicht direkt sichtbar)
# Hilft, Kandidaten zu identifizieren.
docker ps --format 'table {{.Names}}t{{.Image}}t{{.Status}}'

# Volumes und Mounts eines auffälligen Containers anzeigen
C=<container_name_or_id>
docker inspect "$C" --format '{{json .Mounts}}' | jq .

Por qué ayuda: Si un problema se debe principalmente a volúmenes (p. ej. NFS, CIFS/SMB, iSCSI), el ajuste del Storage-Driver aporta poco. Al contrario, «image-pull lento» o «build atascado» suelen ser cuestiones de Layer-Storage.

2) Capacidad del host, inodos, principales causantes

Shell
DOCKER_DIR=$(docker info --format '{{.DockerRootDir}}')

df -Th "$DOCKER_DIR"
df -ih "$DOCKER_DIR"

# Größte Docker-Verzeichnisse
sudo du -x -d 1 -h "$DOCKER_DIR" 2>/dev/null | sort -h

3) Docker-Objekte: ¿cuánto ocupa realmente?

Shell
docker system df
docker system df -v | sed -n '1,200p'

Interpretación: Altos porcentajes «Reclaimable» indican potencial para limpieza. Atención: «Reclaimable» no equivale a «seguro de borrar». El borrado puede eliminar caches de build y aumentar la carga de pull.

4) Limpieza segura (con límites claros)

Si necesita liberar espacio a corto plazo, empiece de forma conservadora. Tenga planificado que los contenedores en ejecución no se eliminen, pero sí los recursos no utilizados.

Shell
# Unbenutzte Images, Container (stopped), Netzwerke, Build-Cache entfernen
# Vorsicht in CI-Umgebungen: Build-Cache-Verlust kann Builds verlangsamen.
docker system prune

# Aggressiver: auch unbenutzte Images entfernen
docker system prune -a

# Volumes nur entfernen, wenn Sie absolut sicher sind
docker volume ls
# docker volume prune

Por qué funciona: Muchos hosts «llenos» son simplemente resultado de falta de reglas de lifecycle. Cuándo falla: Cuando el cuello de botella no es la capacidad, sino los inodos, metadatos de thin-pool o latencia de I/O. O cuando el espacio no se libera de inmediato por eliminación diferida (devicemapper).

5) Hacer visible la latencia de E/S (perspectiva del host)

Para problemas de almacenamiento no solo es relevante «%util», sino sobre todo la latencia. Según la distribución hay distintas herramientas; iostat suele estar rápidamente disponible.

Shell
# Pakete ggf. installieren: sysstat
# sudo apt-get install -y sysstat  |  sudo yum install -y sysstat

iostat -x 1 10

# Grobe Prozesssicht, um I/O-Wait zu erkennen
vmstat 1 10

Interpretación: Alta await o svctm (según la versión) y utilización sostenida indican cuellos de botella en el dispositivo de bloque. Entonces, las medidas de limpieza suelen ser solo un tratamiento sintomático.

Migración: cambiar el Storage-Driver o desplazar DockerRootDir (con estrategia de retroceso)

Si el diagnóstico es claro (p. ej. devicemapper en modo loopback de forma continuada, XFS sin ftype, IOPS permanentemente insuficientes), un cambio estructural es más limpio que «más espacio». Para los operadores es importante: cambiar el Storage-Driver normalmente implica que las imágenes/layers locales existentes en DockerRootDir deben reconstruirse. Los datos persistentes deben estar en volúmenes; todo lo demás es reemplazable.

Preparación: ¿Qué debe respaldarse?

  • Definiciones de Compose/Stack, unidades systemd, archivos de entorno: para que pueda iniciar los contenedores de forma reproducible.
  • Accesos a registries (credenciales, mirrors): para que un repull funcione.
  • Volumes: dependiendo de la implementación, se ubican debajo de DockerRootDir o como montajes separados. Compruebe la ubicación real de almacenamiento.
  • Plan de inactividad (genérico, sin orquestador)

    Shell
    # 1) Laufende Container geordnet stoppen
    docker ps -q | xargs -r docker stop
    
    # 2) Docker-Dienst stoppen
    sudo systemctl stop docker
    
    # 3) Datenverzeichnis sichern (Rollback-Pfad)
    DOCKER_DIR=$(docker info --format '{{.DockerRootDir}}' 2>/dev/null || echo /var/lib/docker)
    
    # Hinweis: Bei großen Verzeichnissen dauert das. Alternativ: Snapshot auf Storage-Ebene.
    sudo tar -C "$(dirname "$DOCKER_DIR")" -cpf /root/docker-rootdir-backup.tar "$(basename "$DOCKER_DIR")"
    
    # 4) Neues Filesystem/Blockdevice mounten und als DockerRootDir konfigurieren
    # (Konkrete Mount-/mkfs-Schritte sind umgebungsspezifisch.)
    
    # 5) Docker mit neuer Konfiguration starten
    sudo systemctl start docker
    
    # 6) Images neu ziehen und Workloads starten
    # docker compose up -d  (falls genutzt)

    ¿Por qué un respaldo tar como ruta de reversión? No porque quiera restaurarlo „siempre“, sino porque le ofrece una opción si hay dependencias inesperadas en el antiguo root (p. ej., imágenes locales sin registry, datos olvidados en la capa de escritura). En entornos más grandes, un snapshot a nivel de almacenamiento (LVM, SAN, VM-Snapshot con precaución) suele ser más práctico.

    Establecer DockerRootDir mediante daemon.json

    Muchos equipos usan una partición dedicada para Docker para encapsular claramente espacio, inodos e IOPS. Esto se configura en /etc/docker/daemon.json.

    JSON
    {
      "data-root": "/var/lib/docker",
      "storage-driver": "overlay2"
    }

    Importante: No cambie storage-driver „a la ligera“ en un host con capas locales en producción sin planificar la reconstrucción. Y: compruebe tras una migración SELinux/contextos de AppArmor y opciones de montaje, porque de lo contrario pueden producirse errores secundarios (p. ej., Permission Denied en sistemas basados en etiquetas).

    Buenas prácticas para una operación estable (para que no vuelva a encontrarse en un incidente)

    1) La capacidad no es solo GB: inodos, metadatos, IOPS

    Planifique monitorización en al menos tres ejes: espacio ocupado, utilización de inodos y latencia de I/O. Especialmente los hosts de compilación con muchas capas necesitan reservas de inodos.

    2) Volumes bewusst platzieren und trennen

    Workloads intensivos en datos (bases de datos, repositorios de artefactos, logs) no deberían residir en la capa de escritura del contenedor. Use Volumes o Bind-Mounts en sistemas de archivos diseñados para ello. Eso reduce el copy-up y hace el rendimiento más predecible.

    3) Cleanup-Strategie definieren statt „manuell prune“

    El mantenimiento regular es mejor que borrar ad hoc durante un incidente. Defina reglas sobre cuánto tiempo deben permanecer en el host caches de compilación, dangling images y contenedores antiguos. En entornos CI, hosts de runners dedicados y un enfoque controlado de caché forman parte de la rutina operativa.

    4) Kernel-/Filesystem-Änderungen wie Deployments behandeln

    Una actualización de kernel puede cambiar el comportamiento de OverlayFS; una migración de sistema de archivos puede afectar a d_type/opciones. Pruebe estos cambios en un host de staging con builds representativos y cargas de I/O antes de desplegarlos a gran escala.

    Conclusión: los problemas de almacenamiento rara vez son „místicos“, pero a menudo tienen múltiples capas

    OverlayFS, device-mapper y loopback parecen detalles internos en el día a día – en un incidente, sin embargo, determinan la estabilidad, el rendimiento y la rapidez de su recuperación. Un Container-Storage-Troubleshooting limpio comienza con una línea base clara (Driver, Filesystem, DockerRootDir), separa problemas de Layer de problemas de Volume y verifica de forma sistemática los inodos, los metadatos del thin pool y la latencia de I/O. Si loopback o un sistema de ficheros inadecuado son la causa, una migración planificada suele ser más conveniente que una «limpieza» continua bajo presión.

    Si quiere estandarizar este asunto en su entorno, cree un Runbook breve con la secuencia de comprobaciones, los valores límite y una ruta de retroceso probada – así, el próximo incidente de almacenamiento será un caso de mantenimiento controlado.

    Para este tema también son importantes Overlayfs Docker y Device-Mapper Docker. El artículo contextualiza estos aspectos de forma comprensible y muestra en qué fijarse en el día a día.

    Weiterfuehrend

    Passende weitere Inhalte