Résolution des échecs de construction Docker : un guide de dépannage complet
Déboguez les échecs de construction Docker causés par des chemins incorrects, des paquets manquants, des surprises de cache, des problèmes réseau, des permissions ou de l'espace disque.
Résolution des échecs de construction Docker : un guide de dépannage complet
Les échecs de construction Docker sont plus faciles à corriger lorsque vous traitez la sortie de construction comme un transcript. Docker vous indique quelle étape a échoué, quelle commande a été exécutée et ce que la commande a affiché. Le travail utile consiste à trouver la première véritable erreur, pas la dernière ligne qui indique que la construction a échoué.
Exécutez la construction avec une progression simple lorsque la sortie par défaut cache trop d'informations :
docker build --progress=plain -t my-app:debug .
Recherchez le numéro de l'étape qui a échoué, par exemple #8, et l'instruction à côté. Si l'instruction qui a échoué est COPY, vous avez probablement un problème de contexte de construction ou de chemin. Si c'est RUN apt-get install, vous avez un problème de paquet, de réseau, de dépôt ou d'architecture. Si c'est RUN npm ci ou pip install, lisez l'erreur du gestionnaire de paquets avant de modifier les paramètres Docker.
COPY failed : le fichier n'est pas dans le contexte de construction
L'une des erreurs de construction les plus courantes est aussi l'une des plus simples :
COPY failed: file not found in build context or excluded by .dockerignore
Docker ne peut copier que les fichiers situés dans le contexte de construction, qui est généralement le dernier argument de docker build :
docker build -t my-app .
Ici, . est le contexte. Un Dockerfile dans un sous-répertoire ne peut pas copier ../secret.txt depuis l'extérieur de ce contexte. Docker bloque intentionnellement cela car les constructions doivent être reproductibles à partir de leur contexte.
Vérifiez trois choses :
pwd
ls -la
docker build --progress=plain -f path/to/Dockerfile .
Si votre Dockerfile se trouve dans docker/Dockerfile mais que l'application est à la racine du dépôt, construisez à partir de la racine et pointez vers le Dockerfile avec -f :
docker build -f docker/Dockerfile -t my-app .
Inspectez également .dockerignore. Il peut exclure le fichier que vous essayez de copier. Cela arrive souvent avec dist, target, .env ou les fichiers générés. Si le Dockerfile attend un fichier, ne l'ignorez pas à moins que le fichier ne soit créé à l'intérieur de la construction.
Échecs d'installation de paquets
Les échecs de gestionnaire de paquets se répartissent généralement en quelques catégories : index de paquets obsolètes, noms de paquets incorrects, dépôts manquants, problèmes réseau ou utilisation de commandes provenant d'une distribution Linux incorrecte.
Cela échoue sur Alpine car Alpine n'utilise pas apt-get :
FROM alpine:3.20
RUN apt-get update && apt-get install -y curl
Utilisez le gestionnaire de paquets pour l'image de base :
FROM alpine:3.20
RUN apk add --no-cache curl
Pour les images Debian ou Ubuntu, gardez la mise à jour et l'installation dans la même couche :
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates && rm -rf /var/lib/apt/lists/*
Si apt-get install indique qu'il ne peut pas localiser un paquet, confirmez le nom du paquet pour cette version de distribution. Les noms de paquets diffèrent entre Debian, Ubuntu, Alpine, Fedora et les images spécifiques à un langage. Les images minimales peuvent également manquer d'outils que vous supposez présents, comme bash, curl, git, tar ou ca-certificates.
Si les téléchargements HTTPS échouent avec des erreurs de certificat, installez les certificats CA avant d'utiliser curl, wget, git via HTTPS, npm, pip ou les gestionnaires de paquets de langage :
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*
Surprises de cache
Le cache de Docker est généralement utile, mais il peut rendre une construction défectueuse incohérente. Si vous soupçonnez un cache obsolète, exécutez :
docker build --no-cache --progress=plain -t my-app:debug .
Si la construction échoue uniquement sans cache, vous avez peut-être compté sur d'anciennes couches. Si elle échoue uniquement avec le cache, vérifiez si un fichier généré ou un fichier de verrouillage de dépendance a changé d'une manière que Docker ne voit pas là où vous l'attendez.
Pour les installations de dépendances, copiez les fichiers de verrouillage avant l'arborescence source complète :
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
Pour Python :
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
Ce modèle n'est pas seulement plus rapide. Il rend les échecs de construction plus faciles à comprendre car l'installation des dépendances dépend des fichiers de dépendance, pas de chaque modification source.
Échecs réseau et de registre
Une construction peut échouer avant même que votre Dockerfile ne s'exécute si Docker ne peut pas tirer l'image de base :
docker pull python:3.12-slim
Si cela échoue, corrigez d'abord l'accès au registre. Vérifiez l'authentification pour les registres privés, les paramètres de proxy d'entreprise, le DNS et les règles de pare-feu. Derrière un proxy, le démon Docker a besoin d'une configuration de proxy ; définir HTTP_PROXY uniquement dans votre shell interactif peut ne pas suffire.
Pour les téléchargements à l'intérieur des étapes RUN, testez l'URL depuis l'hôte, puis depuis un conteneur temporaire sur le même chemin réseau :
curl -I https://example.com/file.tar.gz
docker run --rm curlimages/curl -I https://example.com/file.tar.gz
Évitez de dépendre de scripts distants non épinglés si possible. Un Dockerfile qui curl install.sh depuis une branche mouvante peut casser parce que le script distant a changé. Préférez les téléchargements versionnés et la vérification des sommes de contrôle pour les binaires :
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
Remplacez <sha256> par la somme de contrôle réelle de la page de version du projet.
Incompatibilités d'architecture
Sur Apple Silicon ou des flottes CI mixtes, les échecs de construction peuvent provenir de l'architecture. Une image ou un binaire téléchargé peut être amd64 alors que le constructeur est arm64, ou vice versa. Les symptômes incluent exec format error, des paquets manquants pour une architecture, ou des binaires qui échouent pendant la construction.
Vérifiez votre hôte et votre cible :
docker version
docker buildx ls
Construisez pour une plateforme spécifique si nécessaire :
docker buildx build --platform linux/amd64 -t my-app:amd64 .
Soyez prudent : les constructions multiplateformes peuvent être plus lentes lorsque l'émulation est impliquée. Pour la CI, les constructeurs natifs pour chaque plateforme sont souvent plus rapides et moins surprenants.
Erreurs de permission pendant la construction
Les permissions échouent dans les constructions lorsque des fichiers sont copiés avec une propriété inattendue, que les scripts ne sont pas exécutables, ou que le Dockerfile passe à un utilisateur non root avant la fin de la configuration.
Si un script échoue avec permission denied, inspectez-le avant de copier des hypothèses dans le Dockerfile :
ls -l scripts/start.sh
Corrigez-le ensuite soit dans git, soit dans l'image :
COPY scripts/start.sh /usr/local/bin/start.sh
RUN chmod +x /usr/local/bin/start.sh
Si vous utilisez un utilisateur d'exécution non root, créez les répertoires et définissez la propriété avant de changer d'utilisateur :
RUN useradd -r -u 10001 appuser && mkdir -p /app/data && chown -R appuser:appuser /app
USER appuser
COPY --chown=appuser:appuser . . est souvent plus propre que de copier en tant que root et d'exécuter un chown récursif large plus tard.
Espace disque et nettoyage du cache de construction
Les grandes constructions peuvent échouer parce que l'hôte Docker manque d'espace disque. Vérifiez l'utilisation de Docker :
docker system df
Supprimez le cache de construction inutilisé si nécessaire :
docker builder prune
Soyez plus prudent avec les commandes de nettoyage larges. docker system prune -a supprime les images inutilisées, ce qui peut forcer de gros retéléchargements ou casser des workflows qui dépendent d'images locales. Utilisez-le lorsque vous comprenez l'impact.
Si les constructions remplissent régulièrement le disque, la meilleure solution est généralement des contextes de construction plus petits, des constructions multi-étapes et l'évitement de gros fichiers temporaires dans les couches. Nettoyez les artefacts temporaires dans la même instruction RUN qui les crée.
Déboguer une étape RUN défaillante de manière interactive
Lorsqu'une longue ligne RUN échoue, divisez-la temporairement :
RUN apt-get update
RUN apt-get install -y --no-install-recommends packageA packageB
RUN some-command-that-fails
Une fois que vous avez trouvé la commande défaillante, vous pouvez combiner à nouveau les commandes connexes pour une image plus propre.
Une autre astuce utile est de s'arrêter à une étape connue pour être bonne. Si votre Dockerfile a des étapes, construisez une cible :
docker build --target builder -t my-app-builder .
docker run --rm -it my-app-builder sh
À partir de là, vous pouvez inspecter les fichiers, exécuter la commande défaillante à la main, vérifier les variables d'environnement et voir ce que le système de fichiers contient réellement.
Pour les images sans shell, ajoutez une étape de débogage temporaire plutôt que de polluer l'image de production :
FROM builder AS debug
RUN apt-get update && apt-get install -y --no-install-recommends bash curl
Construisez avec --target debug, enquêtez, puis supprimez ou ignorez la cible de débogage une fois terminé.
Gardez les constructions prévisibles
Une construction Docker fiable est ennuyeuse dans le meilleur sens du terme. Utilisez des images de base versionnées. Gardez les fichiers de verrouillage des dépendances dans le contrôle de source. Évitez latest dans les Dockerfiles de production, sauf si votre processus reconstruit et teste intentionnellement contre des balises mouvantes. Gardez .dockerignore serré. Rendez les téléchargements réseau versionnés et vérifiés. Placez le code source qui change fréquemment après l'installation des dépendances.
Lorsqu'une construction échoue, ne réécrivez pas tout le Dockerfile à la fois. Identifiez l'instruction défaillante, reproduisez avec des logs simples, isolez la commande et corrigez la plus petite cause réelle. Cette approche est plus rapide et vous laisse avec un Dockerfile que la prochaine personne pourra encore comprendre.