Introduction
Un laboratoire local BuildKit vous offre un espace sûr pour tester vos builds Docker avant qu'ils n'atteignent la CI ou la production. Au lieu de résoudre un build défaillant dans un pipeline partagé, vous pouvez le reproduire sur votre propre machine, inspecter le cache, tester des modifications de configuration et vérifier le résultat. Ce guide s'adresse aux développeurs, consultants DevOps et équipes techniques de startups qui ont besoin d'un flux de travail pratique et reproductible.
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 la reprise si l'état attendu n'est pas atteint. Chaque section inclut des commandes concrètes, les sorties attendues et les signaux d'échec.
Inventaire des versions et de l'environnement
Commencez par confirmer ce qui est installé et le mode utilisé par Docker. BuildKit peut fonctionner comme builder par défaut ou comme instance de builder distincte. Connaître la version et la topologie vous aide à reproduire le comportement et à éviter les surprises lors des changements de machine.
Vérifiez la version de Docker et l'état de BuildKit :
docker version --format '{{.Server.Version}}'
Exemple de sortie attendue : 24.0.7. Si la version du serveur est absente ou très ancienne, mettez d'abord Docker à niveau.
Vérifiez si BuildKit est activé pour le builder par défaut :
docker buildx version
Exemple de sortie attendue : github.com/docker/buildx v0.12.1. Si vous voyez docker: 'buildx' is not a docker command, installez le plugin buildx.
Listez les builders disponibles et identifiez le builder actuel :
docker buildx ls
Cherchez la ligne avec * dans la colonne CURRENT. Exemple :
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
default * docker
default default running 23.0.6 linux/amd64, linux/arm64
Si le builder par défaut n'utilise pas le driver docker-container, vous risquez de passer à côté de fonctionnalités avancées comme les builds multi-plateformes. Pour un laboratoire local, le driver docker par défaut convient pour un travail mono-plateforme, mais le driver docker-container offre plus d'isolation et de contrôle.
Prérequis :
- Docker Engine 20.10 ou version ultérieure (recommandé 24.x).
- Plugin Buildx version 0.10 ou ultérieure.
- Au moins 4 Go de RAM pour les builds multi-étapes ; davantage si vous utilisez des montages de cache.
Observation avant intervention : Exécutez docker system df pour voir l'utilisation du disque par les images, les conteneurs et le cache de build. Notez les valeurs. Cela vous indique si votre cache de build consomme un espace inattendu et fournit une base pour un nettoyage ultérieur.
Plus petit changement justifié : Si BuildKit n'est pas actif, définissez la variable d'environnement dans votre session shell plutôt que de modifier la configuration globale :
export DOCKER_BUILDKIT=1
Cela limite le changement à votre session de laboratoire actuelle. Vérifiez avec :
docker buildx debug info 2>&1 | head -20
Vous devriez voir des lignes incluant BuildKit version et Builder: default.
Vérification de l'emplacement des données : Pour tout service qui écrit des données persistantes, confirmez où les fichiers sont stockés avant de modifier les conteneurs. Comparez un volume nommé :
# fragment docker-compose.yml
services:
app:
image: myapp:latest
volumes:
- app_data:/var/lib/app
volumes:
app_data:
avec un montage bind :
volumes:
- ./data:/var/lib/app
Un volume nommé est géré par Docker et survit à la recréation du conteneur. Un montage bind mappe directement un répertoire hôte et est pratique pour le développement, mais peut causer des problèmes de permissions ou de portabilité si le même chemin n'existe pas sur une autre machine.
Test de redémarrage : Pour vérifier la persistance, créez un conteneur qui écrit un fichier, puis supprimez-le et recréez-le :
# Premier lancement
docker run -d --name datatest --mount type=volume,source=app_data,target=/var/lib/app alpine sh -c 'echo hello > /var/lib/app/test.txt; sleep 1000'
# Supprimez après quelques secondes
docker rm -f datatest
# Recréez avec le même volume
docker run -d --name datatest2 --mount type=volume,source=app_data,target=/var/lib/app alpine sleep 1000
# Vérifiez que le fichier existe
docker exec datatest2 cat /var/lib/app/test.txt
Sortie attendue : hello. Si le fichier est absent, le conteneur d'origine écrivait probablement dans sa propre couche inscriptible, pas dans le volume.
Chemin de configuration sûr
Les modifications de configuration doivent être délimitées, documentées et réversibles. Pour un laboratoire BuildKit, les ajustements les plus courants sont les options du driver du builder, les miroirs de registre et les options cache-to/cache-from pour la mise en cache distante.
Observation en lecture seule d'abord : Vérifiez la configuration actuelle du builder :
docker buildx inspect --bootstrap
Exemple de sortie :
Name: default
Driver: docker
Nodes:
Name: default
Endpoint: docker
Status: running
Buildkit: v0.12.3
Platforms: linux/amd64, linux/arm64
Notez le driver et la version de BuildKit. Si le driver est docker, vous ne pouvez pas utiliser directement --cache-to=type=registry avec le builder par défaut ; vous avez besoin d'un builder docker-container.
Exemple de changement délimité : Créez un nouveau builder utilisant le driver docker-container pour tester la mise en cache distante :
docker buildx create --name labbuilder --driver docker-container --use
Vérifiez que le builder actuel a changé :
docker buildx ls
Recherchez * à côté de labbuilder. Si vous faites une erreur, revenez en arrière avec :
docker buildx use default
Configuration du miroir de registre : Si vous avez besoin de tirer des images de base plus rapidement, ajoutez un miroir dans la configuration du démon Docker. Sur Linux, éditez /etc/docker/daemon.json :
{
"registry-mirrors": ["https://mirror.example.com"]
}
Puis redémarrez Docker :
sudo systemctl restart docker
Règle de sécurité : Ne mettez jamais d'identifiants dans un Dockerfile ou un argument de build. Utilisez les secrets BuildKit :
# Dockerfile
# syntax=docker/dockerfile:1
FROM alpine
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
Passez le secret au moment du build :
echo "my-api-key" > secret.txt
docker build --secret id=mysecret,src=secret.txt -t myapp:secrettest .
Le secret n'est disponible que pendant l'instruction RUN et n'est pas stocké dans les couches de l'image. Après le build, supprimez le fichier local secret.txt.
Propriétaire de la configuration et cadence de revue : Attribuez à une personne la responsabilité des modifications de configuration de build. Par exemple, « Priya Shah, responsable ingénierie » examine et approuve les modifications chaque lundi lors de la réunion d'équipe. Chaque modification doit inclure un plan de retour en arrière.
Vérification et diagnostics
Après toute modification, vérifiez que BuildKit fonctionne comme prévu. Utilisez un petit projet d'exemple pour tester les fonctionnalités clés : builds multi-étapes, montages de cache et arguments de build.
Structure du projet d'exemple :
lab/
├── Dockerfile
├── app.py
└── requirements.txt
app.py :
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello BuildKit'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt :
flask==2.3.2
Dockerfile :
# syntax=docker/dockerfile:1
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
Build avec BuildKit :
docker build -t labapp:test .
Observez la sortie. Avec BuildKit, vous voyez des étapes comme #1 [internal] load build definition, #2 [internal] load .dockerignore, et #3 [builder 1/3] FROM docker.io/library/python:3.11-slim. Le build se termine par writing image sha256:... et naming to docker.io/library/labapp:test.
Vérifiez le cache de build : Relancez le build immédiatement :
docker build -t labapp:test .
La sortie attendue inclut CACHED pour chaque étape. Si une étape affiche RUN au lieu de CACHED, le cache a été invalidé. Examinez pourquoi : soit un fichier a changé, soit une commande utilise une valeur non déterministe comme RUN date.
Exécutez et testez le conteneur :
docker run -d -p 5000:5000 --name labapp_container labapp:test
curl http://localhost:5000
Attendu : Hello BuildKit. Vérifiez les journaux :
docker logs labapp_container --tail 20
Recherchez la ligne de démarrage de Flask Running on http://0.0.0.0:5000. Si le conteneur se termine immédiatement, diagnostiquez avec docker inspect labapp_container --format '{{.State.ExitCode}} {{.State.Error}}'.
Équivalent Compose : Si vous utilisez Docker Compose, exécutez :
docker compose ps
docker compose logs -f app
Pour déboguer à l'intérieur du conteneur sans modifier l'image :
docker compose exec app sh
Modes d'échec et reprise
BuildKit introduit de nouveaux modes d'échec différents de l'ancien builder. Reconnaissez les plus courants et ayez une procédure de reprise.
Échec 1 : Le build échoue avec « failed to solve: failed to read dockerfile »
Pourquoi cela arrive : Le Dockerfile n'est pas à l'emplacement attendu, ou vous construisez à partir d'un contexte différent de celui prévu.
Comment éviter : Exécutez toujours docker build -f <chemin-vers-dockerfile> <contexte> explicitement. Par exemple :
docker build -f ./docker/Dockerfile .
Reprise : Vérifiez le chemin du fichier et le contexte. Exécutez ls -la pour confirmer que le Dockerfile existe. Si l'erreur mentionne failed to read dockerfile: open /var/lib/docker/tmp/buildkit-mount.../Dockerfile: no such file or directory, le contexte peut manquer le fichier. Ajustez le chemin.
Échec 2 : Le build se bloque ou est extrêmement lent pendant l'installation des paquets
Pourquoi cela arrive : Pas de cache de build pour l'étape d'installation des paquets, ou vous utilisez un miroir réseau lent.
Comment éviter : Utilisez des montages de cache pour les gestionnaires de paquets. Pour Python pip, modifiez le Dockerfile :
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
Reprise : Si le build est bloqué, appuyez sur Ctrl+C et ajoutez le montage de cache. Vérifiez également votre réseau et la configuration du miroir de registre.
Échec 3 : « ERROR: BuildKit is enabled but the buildx component is missing or broken »
Pourquoi cela arrive : Le plugin buildx n'est pas installé ou n'est pas dans le PATH.
Comment éviter : Installez buildx selon la documentation officielle de Docker. Vérifiez avec docker buildx version.
Reprise : Réinstallez buildx ou utilisez temporairement l'ancien builder en désactivant DOCKER_BUILDKIT :
unset DOCKER_BUILDKIT
Mais cela désactive les fonctionnalités de BuildKit.
Échec 4 : L'import/export du cache distant échoue
Pourquoi cela arrive : Vous utilisez le driver docker avec --cache-to=type=registry, ce qui nécessite un driver docker-container.
Comment éviter : Créez et utilisez un builder dédié :
docker buildx create --name labbuilder --driver docker-container --use
Reprise : Passez au bon builder ou supprimez les options de cache si non nécessaires.
Échec 5 : Fuite de secrets dans les couches de l'image
Pourquoi cela arrive : Vous avez utilisé des arguments de build (ARG) pour les secrets, ou écrit des secrets dans un fichier pendant le build sans nettoyage.
Comment éviter : Utilisez toujours --mount=type=secret et ne stockez jamais de secrets dans le Dockerfile ou les variables d'environnement de l'image finale. Analysez votre image à la recherche de secrets après le build :
docker scan labapp:test
Ou utilisez un outil comme trivy :
trivy image labapp:test
Reprise : Si un secret a fui, faites pivoter le secret immédiatement et reconstruisez l'image avec une gestion appropriée des secrets. Ne poussez pas l'image compromise vers un registre.
Propriétaire de la reprise : Le propriétaire de la configuration de build (par exemple, Priya Shah) est responsable d'approuver toute solution de contournement et de planifier une correction permanente dans un délai d'une semaine.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle avant et après chaque modification du laboratoire de build. Chaque élément a une commande concrète et un résultat attendu.
| Vérification | Commande | Résultat attendu | Propriétaire | Fréquence de revue |
|---|---|---|---|---|
| Version de Docker | docker version --format '{{.Server.Version}}' | Sortie d'une version >= 20.10 | Responsable DevOps | Mensuelle |
| BuildKit actif | docker buildx debug info | Affiche la version de BuildKit, aucune erreur | Responsable DevOps | Hebdomadaire |
| Driver du builder | docker buildx ls | Builder actuel marqué avec *, driver docker ou docker-container | Responsable ingénierie | Hebdomadaire |
| Utilisation du disque | docker system df | Cache de build inférieur à 10 Go pour le laboratoire | Responsable DevOps | Hebdomadaire |
| Santé des conteneurs | docker ps | Tous les conteneurs attendus en cours d'exécution, pas de boucle de redémarrage | Propriétaire de l'application | Quotidienne |
| Erreurs de journaux | docker logs <container> --tail 100 | Aucune erreur fatale dans les 100 dernières lignes | Propriétaire de l'application | Quotidienne |
| Persistance du volume | Test de redémarrage comme décrit | Les données survivent à la recréation du conteneur | Propriétaire de l'application | Mensuelle |
| Taux de succès du cache de build | docker build -t labapp:test . | Les étapes affichent CACHED | Responsable ingénierie | À chaque build |
| Hygiène des secrets | docker history labapp:test --no-trunc | Aucune valeur de secret visible | Propriétaire de la sécurité | Hebdomadaire |
| Plan de retour en arrière | Documenté dans le runbook | Étapes de retour en arrière testées et valides | Responsable ingénierie | À chaque changement |
Avant tout changement :
- Exécutez
docker system dfet notez les chiffres. - Exécutez
docker buildx lset notez le builder actuel. - Assurez-vous qu'aucun conteneur de type production n'utilise actuellement les mêmes volumes que vous prévoyez de modifier.
Après tout changement :
- Relancez le build et vérifiez les étapes
CACHED. - Exécutez le conteneur et testez avec
curlou équivalent. - Vérifiez
docker logspour des avertissements inattendus. - Mettez à jour le runbook avec le changement et le retour en arrière.
Pièges courants et comment les éviter
Piège 1 : Utiliser COPY . . sans fichier .dockerignore.
Pourquoi : Le contexte de build devient énorme, ralentit le build et peut inclure des fichiers sensibles comme .env ou des identifiants.
Comment éviter : Créez un fichier .dockerignore à la racine du projet :
.git
.env
*.log
node_modules
__pycache__
Vérifiez la taille du contexte avec :
docker build --progress=plain -t labapp:test . 2>&1 | grep -i "context"
Recherchez une ligne comme #1 transferring context: 1.2MB. Si elle fait des dizaines de Mo, revoyez le fichier d'ignore.
Piège 2 : Ne pas épingler les versions des images de base.
Pourquoi : Utiliser latest conduit à des builds non reproductibles et des échecs surprises lorsque l'image de base est mise à jour.
Comment éviter : Épinglez à la fois l'image et le condensat :
FROM python:3.11.3-slim@sha256:...
Ou au moins une version mineure spécifique.
Piège 3 : Ignorer les avertissements de build sur les fonctionnalités dépréciées.
Pourquoi : Des avertissements comme DEPRECATED: The legacy builder is deprecated indiquent que vous utilisez peut-être une syntaxe obsolète ou que vous manquez des fonctionnalités BuildKit.
Comment éviter : Lisez toujours la sortie complète du build. Utilisez --progress=plain pour voir tous les avertissements. Corrigez-les avant qu'ils ne deviennent des erreurs.
Piège 4 : Exécuter les builds en tant que root dans le conteneur.
Pourquoi : L'image finale s'exécute en root par défaut, ce qui est un risque de sécurité en production.
Comment éviter : Ajoutez un utilisateur non-root dans le Dockerfile :
RUN useradd -m appuser
USER appuser
Vérifiez avec docker run --rm labapp:test id -u qui devrait afficher 1000 ou l'UID que vous avez défini.
Piège 5 : Ne pas nettoyer les anciens builders et instances.
Pourquoi : Les builders inutilisés et le cache gonflent votre système et créent de la confusion.
Comment éviter : Listez et supprimez périodiquement :
docker buildx ls
docker buildx rm labbuilder
Élaguez aussi le cache de build :
docker builder prune
Confirmez l'espace libéré avec docker system df.
Conclusion
Un laboratoire local Docker BuildKit n'a de valeur que si chaque recommandation est limitée à une version, observable et réversible. Copier des commandes sans vérifier les prérequis et la sortie attendue n'est pas une procédure opérationnelle. Ce guide vous donne une manière structurée d'observer, de modifier, de vérifier et de récupérer.
Commencez par une vérification à faible risque : vérifiez votre builder actuel avec docker buildx ls, enregistrez l'état, exécutez un petit build et comparez le résultat avec la sortie CACHED attendue. Passez ensuite à des fonctionnalités plus avancées comme la mise en cache distante et les secrets seulement après que les bases fonctionnent de manière fiable.
Rendez les échecs visibles, protégez les valeurs sensibles, limitez les modifications à la ressource prévue et définissez la vérification de reprise avant qu'un incident ne force la décision.