Resolución de fallos en la construcción de Docker: Guía completa de solución de problemas
Depura fallos en la construcción de Docker causados por rutas incorrectas, paquetes faltantes, sorpresas de caché, problemas de red, permisos o espacio en disco.
Resolución de fallos en la construcción de Docker: Guía completa de solución de problemas
Los fallos en la construcción de Docker son más fáciles de solucionar cuando tratas la salida de la construcción como una transcripción. Docker te indica qué paso falló, qué comando se ejecutó y qué imprimió el comando. El trabajo útil es encontrar el primer error real, no la última línea que dice que la construcción falló.
Ejecuta la construcción con progreso simple cuando la salida predeterminada oculta demasiado:
docker build --progress=plain -t my-app:debug .
Busca el número del paso que falló, como #8, y la instrucción junto a él. Si la instrucción que falla es COPY, probablemente tengas un problema de contexto de construcción o de ruta. Si es RUN apt-get install, tienes un problema de paquete, red, repositorio o arquitectura. Si es RUN npm ci o pip install, lee el error del gestor de paquetes antes de cambiar la configuración de Docker.
COPY failed: el archivo no está en el contexto de construcción
Uno de los errores de construcción más comunes también es uno de los más simples:
COPY failed: file not found in build context or excluded by .dockerignore
Docker solo puede copiar archivos dentro del contexto de construcción, que suele ser el último argumento de docker build:
docker build -t my-app .
Aquí . es el contexto. Un Dockerfile en un subdirectorio no puede copiar ../secret.txt desde fuera de ese contexto. Docker bloquea intencionalmente esto porque las construcciones deben ser reproducibles desde su contexto.
Verifica tres cosas:
pwd
ls -la
docker build --progress=plain -f path/to/Dockerfile .
Si tu Dockerfile está en docker/Dockerfile pero la aplicación está en la raíz del repositorio, construye desde la raíz y apunta al Dockerfile con -f:
docker build -f docker/Dockerfile -t my-app .
También inspecciona .dockerignore. Puede excluir el archivo que intentas copiar. Esto suele ocurrir con dist, target, .env o archivos generados. Si el Dockerfile espera un archivo, no lo ignores a menos que el archivo se cree dentro de la construcción.
Fallos en la instalación de paquetes
Los fallos del gestor de paquetes suelen caer en unos pocos grupos: índices de paquetes obsoletos, nombres de paquetes incorrectos, repositorios faltantes, problemas de red o el uso de comandos de la distribución de Linux incorrecta.
Esto falla en Alpine porque Alpine no usa apt-get:
FROM alpine:3.20
RUN apt-get update && apt-get install -y curl
Usa el gestor de paquetes para la imagen base:
FROM alpine:3.20
RUN apk add --no-cache curl
Para imágenes Debian o Ubuntu, mantén la actualización y la instalación en la misma capa:
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates && rm -rf /var/lib/apt/lists/*
Si apt-get install dice que no puede localizar un paquete, confirma el nombre del paquete para esa versión de distribución. Los nombres de los paquetes difieren entre Debian, Ubuntu, Alpine, Fedora e imágenes específicas de lenguaje. Las imágenes mínimas también pueden carecer de herramientas que asumes que están presentes, como bash, curl, git, tar o ca-certificates.
Si las descargas HTTPS fallan con errores de certificado, instala los certificados CA antes de usar curl, wget, git sobre HTTPS, npm, pip o gestores de paquetes de lenguaje:
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*
Sorpresas de caché
La caché de Docker suele ser útil, pero puede hacer que una construcción rota parezca inconsistente. Si sospechas que la caché está obsoleta, ejecuta:
docker build --no-cache --progress=plain -t my-app:debug .
Si la construcción solo falla sin caché, es posible que hayas estado confiando en capas antiguas. Si solo falla con caché, verifica si un archivo generado o un archivo de bloqueo de dependencias cambió de una manera que Docker no ve donde esperas.
Para instalaciones de dependencias, copia los archivos de bloqueo antes del árbol fuente completo:
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
Para Python:
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
Este patrón no solo es más rápido. Hace que los fallos de construcción sean más fáciles de entender porque la instalación de dependencias depende de los archivos de dependencias, no de cada cambio en el código fuente.
Fallos de red y registro
Una construcción puede fallar antes de que se ejecute tu Dockerfile si Docker no puede extraer la imagen base:
docker pull python:3.12-slim
Si eso falla, primero soluciona el acceso al registro. Verifica la autenticación para registros privados, la configuración del proxy corporativo, DNS y las reglas del cortafuegos. Detrás de un proxy, el demonio de Docker necesita la configuración del proxy; establecer HTTP_PROXY solo en tu shell interactivo puede no ser suficiente.
Para descargas dentro de pasos RUN, prueba la URL desde el host y luego desde un contenedor temporal en la misma ruta de red:
curl -I https://example.com/file.tar.gz
docker run --rm curlimages/curl -I https://example.com/file.tar.gz
No dependas de scripts remotos no fijos si puedes evitarlo. Un Dockerfile que hace curl a install.sh desde una rama móvil puede romperse porque el script remoto cambió. Prefiere descargas versionadas y verificación de suma de comprobación para binarios:
RUN curl -fsSLo tool.tar.gz https://example.com/tool-1.2.3-linux-amd64.tar.gz && echo '<sha256> tool.tar.gz' | sha256sum -c - && tar -xzf tool.tar.gz -C /usr/local/bin && rm tool.tar.gz
Reemplaza <sha256> con la suma de comprobación real de la página de lanzamiento del proyecto.
Discrepancias de arquitectura
En Apple Silicon o flotas de CI mixtas, los fallos de construcción pueden deberse a la arquitectura. Una imagen o binario descargado puede ser amd64 mientras que el constructor es arm64, o viceversa. Los síntomas incluyen exec format error, paquetes faltantes para una arquitectura o binarios que fallan durante la construcción.
Verifica tu host y destino:
docker version
docker buildx ls
Construye para una plataforma específica cuando sea necesario:
docker buildx build --platform linux/amd64 -t my-app:amd64 .
Ten cuidado: las construcciones multiplataforma pueden ser más lentas cuando está involucrada la emulación. Para CI, los constructores nativos para cada plataforma suelen ser más rápidos y menos sorprendentes.
Errores de permisos durante la construcción
Los permisos fallan en las construcciones cuando los archivos se copian con propiedad inesperada, los scripts no son ejecutables o el Dockerfile cambia a un usuario no root antes de completar la configuración.
Si un script falla con permission denied, inspecciónalo antes de copiar suposiciones en el Dockerfile:
ls -l scripts/start.sh
Luego arréglalo ya sea en git o en la imagen:
COPY scripts/start.sh /usr/local/bin/start.sh
RUN chmod +x /usr/local/bin/start.sh
Si usas un usuario de tiempo de ejecución no root, crea directorios y establece la propiedad antes de cambiar de usuario:
RUN useradd -r -u 10001 appuser && mkdir -p /app/data && chown -R appuser:appuser /app
USER appuser
COPY --chown=appuser:appuser . . suele ser más limpio que copiar como root y ejecutar un chown recursivo amplio más tarde.
Espacio en disco y limpieza de caché de construcción
Las construcciones grandes pueden fallar porque el host de Docker se queda sin espacio en disco. Verifica el uso de Docker:
docker system df
Elimina la caché de construcción no utilizada cuando sea apropiado:
docker builder prune
Ten más cuidado con los comandos de limpieza amplios. docker system prune -a elimina imágenes no utilizadas, y eso puede forzar grandes re-extracciones o romper flujos de trabajo que dependen de imágenes locales. Úsalo cuando entiendas el impacto.
Si las construcciones llenan regularmente el disco, la mejor solución suele ser contextos de construcción más pequeños, construcciones de múltiples etapas y evitar archivos temporales enormes en las capas. Limpia los artefactos temporales en la misma instrucción RUN que los crea.
Depurar un paso RUN que falla de forma interactiva
Cuando una línea RUN larga falla, divídela temporalmente:
RUN apt-get update
RUN apt-get install -y --no-install-recommends packageA packageB
RUN some-command-that-fails
Una vez que encuentres el comando que falla, puedes combinar comandos relacionados nuevamente para una imagen más limpia.
Otro truco útil es detenerse en una etapa conocida como buena. Si tu Dockerfile tiene etapas, construye un objetivo:
docker build --target builder -t my-app-builder .
docker run --rm -it my-app-builder sh
Desde allí puedes inspeccionar archivos, ejecutar el comando que falla manualmente, verificar las variables de entorno y ver lo que realmente contiene el sistema de archivos.
Para imágenes sin shell, agrega una etapa de depuración temporal en lugar de contaminar la imagen de producción:
FROM builder AS debug
RUN apt-get update && apt-get install -y --no-install-recommends bash curl
Construye --target debug, investiga, luego elimina o ignora el objetivo de depuración cuando hayas terminado.
Mantén las construcciones predecibles
Una construcción Docker confiable es aburrida en el mejor sentido. Usa imágenes base versionadas. Mantén los archivos de bloqueo de dependencias en el control de código fuente. Evita latest en Dockerfiles de producción a menos que tu proceso reconstruya y pruebe intencionalmente contra etiquetas móviles. Mantén .dockerignore ajustado. Haz que las descargas de red sean versionadas y verificadas. Coloca el código fuente que cambia con frecuencia después de la instalación de dependencias.
Cuando una construcción falla, no reescribas todo el Dockerfile de una vez. Identifica la instrucción que falla, reproduce con registros simples, aísla el comando y corrige la causa real más pequeña. Ese enfoque es más rápido y te deja con un Dockerfile que la próxima persona aún puede entender.