Introduction
Docker BuildKit est le moteur de construction par défaut dans les versions modernes de Docker. Il offre des builds parallèles plus rapides, une meilleure mise en cache et une sécurité renforcée. Cependant, il introduit également son propre lot de messages d'erreur et de modes de défaillance qui peuvent frustrer les développeurs, les ingénieurs DevOps et les équipes plateforme. Cet article est un guide pratique des erreurs BuildKit les plus courantes et des correctifs spécifiques qui les résolvent. Il couvre le diagnostic, la récupération et la prévention avec des commandes concrètes et des exemples concrets.
L'objectif est la sécurité opérationnelle : observer avant de modifier, limiter le rayon d'impact, utiliser des espaces réservés plutôt que des secrets, vérifier le résultat et documenter comment récupérer si l'état attendu n'est pas atteint. Que vous déboguiez un docker build échoué en CI ou que vous optimisiez un build local, les flux de travail ci-dessous vous aideront à passer rapidement du symptôme à la solution.
Inventaire des versions et de l'environnement
Avant de résoudre toute erreur BuildKit, confirmez ce avec quoi vous travaillez. Exécutez les commandes en lecture seule suivantes pour établir l'état actuel :
docker version
# Vérifiez les versions du client et du serveur. BuildKit est inclus dans la version du serveur (Engine).
# Exemple de sortie :
# Client: Docker Engine - Community
# Version: 24.0.7
# Server: Docker Engine - Community
# Engine:
# Version: 24.0.7
docker buildx version
# Affiche la version de buildx et le builder par défaut. Buildx est l'interface client de BuildKit.
# Exemple de sortie :
# github.com/docker/buildx v0.12.0 1234abcd
# Default build driver: docker
docker info --format '{{.Builder}}'
# Indique si BuildKit est le builder par défaut. La sortie devrait être "buildkit" s'il est activé.
Si BuildKit n'est pas activé, vous pouvez l'activer en définissant la variable d'environnement DOCKER_BUILDKIT=1 pour une seule commande, ou en mettant à jour le fichier de configuration du démon Docker (/etc/docker/daemon.json) avec "features": { "buildkit": true } et en redémarrant Docker. Cependant, pour les versions modernes de Docker (23.0+), BuildKit est activé par défaut via le driver docker dans buildx.
Lorsque des données ou un état sont impliqués, confirmez où les fichiers sont stockés avant de modifier les conteneurs. Un volume nommé tel que app_data:/var/lib/app est géré par Docker et est généralement plus facile à réutiliser entre les reconstructions de conteneurs. Un montage bind tel que ./data:/var/lib/app mappe directement un répertoire hôte et est utile pour le développement local, mais il peut exposer des problèmes de permissions, de portabilité et de sauvegarde si le même chemin n'existe pas sur une autre machine.
Un petit test local semblable à la production devrait inclure un test de redémarrage : arrêtez le conteneur, recréez-le et confirmez que l'application voit toujours les fichiers attendus. Si les données disparaissent, le service écrivait probablement dans le système de fichiers du conteneur au lieu d'un volume ou d'un montage.
Chemin de configuration sûr
Configurer BuildKit en toute sécurité signifie faire un changement à la fois et vérifier son effet. Une erreur courante est de modifier la configuration du démon ou le Dockerfile à plusieurs endroits simultanément, ce qui rend impossible d'attribuer une correction ou une régression.
Exemple : Ajuster les limites de ressources de BuildKit
Supposons que vos builds soient tués par le tueur OOM (out-of-memory). Vous pourriez vouloir augmenter la limite de mémoire de BuildKit. Au lieu de deviner, observez d'abord :
docker buildx inspect --bootstrap
# Cela démarre le builder et affiche sa configuration actuelle, y compris les ressources.
# Exemple de sortie :
# Platforms: linux/amd64, linux/arm64
# Driver: docker-container
# ...
Pour modifier les limites de ressources, vous créeriez un nouveau builder avec des limites ajustées, car la configuration du builder par défaut est fixe :
docker buildx create --name mybuilder --driver docker-container \
--driver-opt env.BUILDKIT_STEP_LOG_MAX_SIZE=10485760 \
--driver-opt env.BUILDKIT_STEP_LOG_MAX_SPEED=1048576
docker buildx use mybuilder
docker buildx inspect --bootstrap
# Vérifiez que le nouveau builder est actif et notez ses ressources
Après le changement, exécutez un build représentatif et surveillez l'utilisation de la mémoire :
docker buildx build --progress=plain -t myapp:test . 2>&1 | tee build.log
# La sortie en progression simple est plus facile à analyser pour les erreurs et les problèmes de mémoire.
Si le build réussit et que la mémoire reste dans les limites, le changement a été efficace. Sinon, revenez au builder précédent ou ajustez les valeurs progressivement.
Vérification et diagnostics
Lorsqu'un build échoue, BuildKit affiche souvent un journal d'erreurs verbeux. Utilisez --progress=plain et --no-cache pour obtenir une image complète sans artefacts en cache masquant le problème :
docker buildx build --progress=plain --no-cache -t myapp:debug . 2>&1 | tee build-debug.log
Ensuite, inspectez le journal pour des motifs d'erreur spécifiques. Voici quelques-uns courants et ce qu'ils indiquent :
failed to solve: executor failed running [...]: exit code: 100- une commande RUN a échoué ; le code de sortie et la sortie précédente montreront pourquoi.failed to compute cache key: ...- généralement un problème avec le contexte du Dockerfile ou un fichier manquant dans .dockerignore.error: failed to get status: rpc error: code = Unavailable desc = connection error- problème de connexion au démon BuildKit ; vérifiez si le builder est en cours d'exécution (docker buildx ls) et redémarrez si nécessaire.ERROR: unable to prepare context: path "..." not found- le chemin du contexte de build est incorrect ou vous exécutez la commande depuis le mauvais répertoire.
Pour approfondir un conteneur échoué spécifique, utilisez docker ps -a pour lister les conteneurs arrêtés et docker logs <container_id> pour voir la sortie complète de l'étape de build. Par exemple, si un test échoue pendant le build, le conteneur qui a exécuté le test sera dans un état arrêté, et ses journaux contiendront la sortie du test.
Modes de défaillance et récupération
Mode 1 : Démon BuildKit non démarré ou inaccessible
Symptôme : Vous voyez des erreurs comme Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? ou error: failed to get status: rpc error: code = Unavailable.
Cause : Le démon Docker ou un builder BuildKit spécifique n'est pas en cours d'exécution.
Correctif :
- Vérifiez l'état du démon Docker :
systemctl status docker # sur les systèmes systemd
# ou
service docker status
S'il n'est pas en cours d'exécution, démarrez-le : sudo systemctl start docker.
- Si vous utilisez un builder docker-container (séparé du démon), vérifiez son état :
docker buildx ls
# Cherchez l'état du builder, par exemple "running" ou "stopped"
S'il n'est pas en cours d'exécution, démarrez-le :
docker buildx create --name mybuilder --driver docker-container --use
docker buildx inspect --bootstrap
Mode 2 : Échecs de cache provoquant des builds lents ou des résultats incorrects
Symptôme : Les builds prennent plus de temps que prévu, ou vous remarquez des téléchargements répétés de dépendances malgré aucun changement.
Cause : Ordre inefficace des couches du Dockerfile ou invalidation incorrecte du cache.
Correctif : Optimisez l'ordre des couches du Dockerfile pour maximiser les succès de cache. Placez les instructions qui changent fréquemment (comme COPY . .) en dernier, et séparez l'installation des dépendances de la copie du code source :
# Mauvais : invalide le cache à tout changement de source
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
# Bon : séparer l'installation des dépendances
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
Utilisez également un fichier .dockerignore pour exclure les fichiers inutiles du contexte de build, ce qui réduit la taille du contexte et empêche les invalidations de cache inutiles.
Mode 3 : Échec du build avec exec format error sur les builds multi-architectures
Symptôme : Lors de la construction pour une architecture différente (par exemple arm64 sur un hôte amd64), le build échoue avec exec format error pendant une commande RUN.
Cause : La commande RUN tente d'exécuter un binaire pour l'architecture cible sur l'hôte, mais sans émulation appropriée ni configuration buildx.
Correctif : Utilisez le driver docker-container avec buildx, qui prend en charge les builds multi-architectures nativement :
docker buildx create --name multiarch --driver docker-container --use
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:multiarch .
Alternativement, si vous devez utiliser le driver docker par défaut, vous avez besoin d'une émulation QEMU enregistrée sur l'hôte.
Mode 4 : Transfert d'agent SSH ne fonctionnant pas pendant le build
Symptôme : Vous devez cloner des dépôts privés pendant un build, mais l'authentification SSH échoue.
Cause : Par défaut, le conteneur de build n'a pas accès à l'agent SSH de l'hôte.
Correctif : Utilisez l'option --ssh pour transférer l'agent SSH :
docker build --ssh default=$SSH_AUTH_SOCK -t myapp .
Dans le Dockerfile, installez le client SSH et utilisez-le dans une commande RUN :
FROM alpine
RUN apk add --no-cache openssh-client
RUN --mount=type=ssh git clone [email protected]:example/repo.git
Le montage --mount=type=ssh garantit que l'agent n'est disponible que pendant cette commande RUN.
Mode 5 : Secrets fuyant dans l'image
Symptôme : Vous passez des secrets via des build args ou des variables d'environnement, et ils se retrouvent dans les couches de l'image, posant un risque de sécurité.
Cause : Utiliser des build args pour des secrets est non sécurisé car ils sont conservés dans l'historique de l'image.
Correctif : Utilisez l'option --secret pour monter des secrets au moment du build sans les incorporer dans l'image :
docker build --secret id=mysecret,src=/path/to/secret.txt -t myapp .
Dans le Dockerfile :
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
Le secret n'est disponible que pendant cette commande RUN et n'est pas inclus dans l'image finale.
Mode 6 : Taille du contexte BuildKit trop grande
Symptôme : Le build échoue avec une erreur comme ERROR: failed to send context: context too large.
Cause : Le contexte de build (le répertoire et les fichiers envoyés au démon) dépasse une limite, souvent à cause de fichiers volumineux ou nombreux.
Correctif : Utilisez un fichier .dockerignore pour exclure les fichiers et répertoires inutiles. Par exemple :
.git
node_modules
*.log
Si le contexte doit être volumineux, envisagez d'utiliser un contexte distant ou un dockerfile spécifié via URL.
Pièges courants et comment les éviter
- Ne pas utiliser .dockerignore - Envoyer tout le répertoire du projet, y compris
.git,node_moduleset les artefacts de build, ralentit le transfert du contexte et peut provoquer des échecs de cache. Incluez toujours un fichier.dockerignoreavec au moins.gitet les répertoires lourds courants.
- S'appuyer sur la balise
latestdans les images de base - UtiliserFROM ubuntu:latestrend les builds non reproductibles car l'image de base change avec le temps. Épinglez des versions ou des digests spécifiques :FROM ubuntu:22.04.
- Ignorer les codes de sortie dans les commandes RUN - Une commande RUN peut échouer mais le build continue si la commande est un script shell qui renvoie un succès. Utilisez
set -edans les scripts ou enchaînez les commandes avec&&pour garantir que les échecs arrêtent le build.
- Mélanger des discordances d'architecture - Si votre hôte est amd64 et que vous essayez d'exécuter un binaire arm64 sans configuration appropriée, vous verrez
exec format error. Utilisez buildx avec le driver docker-container pour les builds multi-plateformes.
- Négliger le montage de cache BuildKit - BuildKit propose
--mount=type=cachepour accélérer les caches des gestionnaires de paquets. Ne pas l'utiliser peut ralentir les builds. Par exemple :
RUN --mount=type=cache,target=/var/cache/apt \
apt-get update && apt-get install -y ...
Liste de contrôle opérationnelle
Avant et après tout changement lié à BuildKit, parcourez cette liste pour vous assurer d'avoir observé, documenté et vérifié :
- [ ] Confirmer les versions de Docker et BuildKit :
docker versionetdocker buildx version. - [ ] Identifier le builder actif :
docker buildx ls. - [ ] Reproduire l'échec du build avec
--no-cache --progress=plainet capturer la sortie dans un fichier. - [ ] Vérifier les journaux du démon Docker pour les erreurs liées au build :
journalctl -u docker(systemd) ou/var/log/docker.log. - [ ] Examiner le Dockerfile pour l'ordre des couches et la gestion des secrets.
- [ ] S'assurer que
.dockerignoreest présent et exclut les fichiers inutiles. - [ ] Si vous utilisez SSH ou des secrets, vérifier que les options
--sshou--secretsont utilisées correctement et non exposées dans les couches de l'image. - [ ] Appliquer une seule correction à la fois et relancer le build pour vérifier.
- [ ] Documenter la correction et le plan de rollback.
Conclusion
Les erreurs Docker BuildKit sont souvent enracinées dans des problèmes de configuration, de structure du Dockerfile ou d'environnement. En rassemblant systématiquement les informations de version, en observant les modes de défaillance et en appliquant des correctifs ciblés avec vérification, vous pouvez réduire les échecs de build et améliorer les performances de build. N'oubliez pas d'utiliser les fonctionnalités natives de BuildKit comme les montages de cache, les secrets et le transfert SSH pour garder les builds rapides et sécurisés. Testez toujours les changements de manière contrôlée et documentez le chemin de récupération pour les incidents futurs.
Comme prochaine étape, choisissez une erreur courante que vous avez rencontrée, reproduisez-la avec les commandes de diagnostic de cet article et appliquez le correctif correspondant. Partagez vos découvertes avec votre équipe pour construire une connaissance collective du dépannage.