## Introduction La mise en cache des couches Docker est une pierre angulaire de la construction efficace d'images de conteneurs. Lorsqu'elle est correctement configurée, elle peut réduire les temps de construction de quelques minutes à quelques secondes, économiser la bande passante réseau et améliorer la productivité des développeurs. Cependant, en environnement de production, la mise en cache des couches introduit des considérations opérationnelles : la taille du cache peut croître de manière illimitée, les couches obsolètes peuvent entraîner un comportement inattendu, et les erreurs de configuration peuvent provoquer des échecs de construction ou des problèmes de sécurité. Cet article fournit une liste de contrôle opérationnelle pratique pour gérer la mise en cache des couches Docker en production, avec des commandes concrètes, des extraits de configuration et des étapes de vérification. Que vous soyez développeur responsable de la construction d'images ou ingénieur DevOps gérant une infrastructure de construction partagée, ce guide vous aidera à établir un processus reproductible pour inventorier votre environnement, configurer la mise en cache en toute sécurité, diagnostiquer les défauts de cache, récupérer des pannes et maintenir la santé du cache au fil du temps. Nous nous concentrons sur BuildKit, le moteur de construction moderne de Docker, et sa fonctionnalité de montage de cache, qui offre un contrôle granulaire par rapport à la mise en cache héritée des couches d'image. À la fin de cet article, vous serez en mesure de : - Évaluer votre environnement de construction Docker actuel et identifier les risques liés au cache. - Implémenter des montages de cache et une mise en cache de registre en toute sécurité. - Vérifier que la mise en cache fonctionne et diagnostiquer pourquoi une étape de construction a raté le cache. - Récupérer des pannes courantes telles que l'épuisement de l'espace disque, les dépendances obsolètes et la corruption du cache. - Établir une routine de vérifications et de tâches de maintenance pour garder les constructions rapides et fiables. ## Inventaire des versions et de l'environnement Avant de faire des changements, il vous faut une image claire de votre environnement Docker actuel. Cela inclut la version de Docker, le pilote de stockage, le moteur de construction (hérité vs BuildKit) et la configuration du démon Docker. Des versions incohérentes entre les membres de l'équipe ou les exécuteurs CI peuvent entraîner des comportements de mise en cache différents ; l'inventaire est donc une première étape critique. Commencez par vérifier les versions du client et du serveur Docker : ```bash docker version ``` La sortie attendue montrera des sections séparées pour le client et le serveur, par exemple : ``` Client: Docker Engine - Community Version: 24.0.7 API version: 1.43 Go version: go1.20.10 Git commit: afdd53b Built: Thu Oct 26 09:08:17 2023 OS/Arch: linux/amd64 Context: default Server: Docker Engine - Community Engine: Version: 24.0.7 API version: 1.43 (minimum version 1.12) Go version: go1.20.10 Git commit: 17bb9da Built: Thu Oct 26 09:08:17 2023 OS/Arch: linux/amd64 containerd: containerd.io 1.6.26 runc: 1.1.10 ... ``` Notez la version du moteur, la version de l'API, et si le client et le serveur sont alignés. BuildKit est le constructeur par défaut pour Docker Engine 23.0 et plus, mais vous devez confirmer qu'il est actif. Vérifiez les informations du constructeur : ```bash docker buildx ls ``` La sortie attendue pourrait ressembler à : ``` NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS default * docker default default running 23.0.6 linux/amd64, linux/arm64 ``` La colonne "BUILDKIT" montre la version de BuildKit. Si vous voyez une erreur ou aucune version de BuildKit, vous devrez peut-être activer BuildKit ou mettre à niveau Docker. Vous pouvez également inspecter le fichier de configuration du démon Docker, généralement situé à `/etc/docker/daemon.json` sur Linux. Par exemple, pour voir si BuildKit est explicitement activé ou désactivé : ```bash sudo cat /etc/docker/daemon.json ``` Si le fichier contient `"features": {"buildkit": false}`, BuildKit est désactivé. Pour des pratiques de mise en cache modernes, vous voulez généralement que BuildKit soit activé. Pour l'activer, vous pouvez soit définir la variable d'environnement `DOCKER_BUILDKIT=1` pour chaque commande de construction (héritée), soit vous fier à la valeur par défaut du démon. Notez que modifier la configuration du démon nécessite un redémarrage du service Docker : ```bash sudo systemctl restart docker ``` Ensuite, vérifiez le pilote de stockage, qui affecte la manière dont les couches sont stockées et gérées : ```bash docker info --format '{{.Driver}}' ``` Les sorties courantes sont `overlay2`, `btrfs` ou `devicemapper`. Pour la plupart des systèmes Linux de production, `overlay2` est recommandé car il est efficace et bien pris en charge. Si vous voyez `vfs` ou `devicemapper`, envisagez de migrer vers `overlay2` pour de meilleures performances. Vous pouvez changer le pilote de stockage en éditant `/etc/docker/daemon.json` et en ajoutant ou modifiant la clé `storage-driver`, mais cela nécessite de sauvegarder les images et les conteneurs et constitue une opération plus lourde. Pour cet article, nous supposons `overlay2`. Enfin, cartographiez où les constructions ont lieu. Les constructions sont-elles exécutées sur les ordinateurs portables des développeurs, un serveur CI partagé ou un cluster de construction dédié ? Chaque emplacement peut avoir sa propre installation Docker et son propre cache. La cohérence est essentielle : essayez de standardiser sur la même version de Docker et de BuildKit dans tous les environnements de construction pour éviter les incompatibilités de cache. Documentez cet inventaire dans un emplacement central, comme un wiki ou un référentiel de gestion de configuration. **Responsable et cadence de révision :** Attribuez un responsable unique, tel que le responsable de l'équipe DevOps (par exemple, « Jordan Lee, responsable de l'ingénierie de plateforme »), pour maintenir cet inventaire. Révisez-le mensuellement ou chaque fois qu'un nouvel environnement de construction est ajouté. ## Chemin de configuration sûr Une fois l'environnement inventorié, vous pouvez configurer la mise en cache des couches en fonction de votre charge de travail. Le chemin sûr consiste à apporter des modifications progressives et limitées, puis à les tester minutieusement avant de les déployer en production. L'outil principal pour une mise en cache granulaire dans BuildKit est le montage de cache dans les Dockerfiles. Les montages de cache vous permettent de conserver des répertoires utilisés par les gestionnaires de paquets ou les artefacts de construction entre les builds, même si la couche d'image résultante change. Cela est particulièrement utile pour les dépendances qui sont téléchargées mais ne font pas partie de l'image finale, comme `node_modules` pour les constructions frontales ou les caches de modules Go. Voici un extrait de Dockerfile qui utilise un montage de cache pour les dépendances npm : ```dockerfile # syntax=docker/dockerfile:1.4 FROM node:18-alpine AS dependencies WORKDIR /app COPY package.json package-lock.json ./ RUN --mount=type=cache, target=/root/.npm \ npm ci FROM node:18-alpine AS builder WORKDIR /app COPY --from=dependencies /app/node_modules ./node_modules COPY . . RUN npm run build FROM nginx:alpine COPY --from=builder /app/dist /usr/share/nginx/html ``` Dans cette construction multi-étapes, la première étape installe les dépendances en utilisant un montage de cache pour le répertoire de cache npm (`/root/.npm`). Le montage de cache persiste entre les constructions sur le même hôte, donc les exécutions suivantes de `npm ci` réutiliseront les paquets téléchargés, réduisant considérablement le temps d'installation. Le répertoire `node_modules` est ensuite copié dans l'étape de construction, mais le montage de cache lui-même ne fait pas partie de l'image finale. Cela permet de garder l'image finale petite et propre. Pour utiliser les montages de cache en toute sécurité, assurez-vous que tous les développeurs et systèmes CI ont BuildKit activé. Si vous utilisez une ancienne version de Docker ou le constructeur hérité, les montages de cache seront ignorés et le drapeau `--mount` provoquera une erreur. Ainsi, avant d'adopter les montages de cache, vérifiez que vos environnements de construction les prennent en charge. Pour un déploiement sûr, commencez par un seul projet ou une seule image, ajoutez des montages de cache pour l'étape de dépendance la plus longue, et mesurez l'amélioration du temps de construction. Si cela fonctionne bien, étendez à d'autres projets. Une autre configuration importante est la politique de collecte des déchets de BuildKit. BuildKit utilise un cache disque pour les étapes de construction et peut accumuler de grandes quantités de données. Par défaut, la collecte des déchets de BuildKit s'exécute automatiquement lorsque l'utilisation du disque dépasse un seuil, mais vous pouvez la personnaliser. La configuration est stockée dans le fichier de configuration du démon BuildKit, généralement à `/etc/buildkit/buildkitd.toml` si vous exécutez BuildKit en tant que démon séparé, ou intégrée dans la configuration du démon Docker si vous utilisez le BuildKit intégré. Pour définir des limites explicites, vous pouvez exécuter un conteneur BuildKit dédié avec des drapeaux. Par exemple : ```bash docker run -d --name buildkitd --privileged moby/buildkit:latest --addr tcp://0.0.0.0:1234 ``` Cependant, la plupart des configurations de production utilisent le BuildKit intégré du démon Docker, qui gère automatiquement la collecte des déchets. Il est tout de même sage de surveiller l'utilisation du disque et de purger manuellement les caches au besoin, comme nous le verrons plus tard. Un chemin de configuration sûr inclut également l'utilisation d'un fichier `.dockerignore` pour exclure les fichiers inutiles du contexte de construction. Un contexte de construction plus petit signifie une création de couche plus rapide et des constructions plus favorables au cache. Par exemple : ``` .git node_modules *.log ``` Cela évite que de grands répertoires soient envoyés au démon Docker et garantit que les modifications dans ces répertoires n'invalident pas inutilement les couches. **Responsable et cadence de révision :** Le responsable de l'équipe de plateforme (« Jordan Lee ») doit approuver tous les changements de configuration de cache. Révisez la configuration tous les trimestres ou après toute mise à niveau majeure de Docker. ## Vérification et diagnostic Après avoir configuré la mise en cache, vous devez vérifier qu'elle fonctionne comme prévu. Le moyen le plus direct est d'exécuter une construction et d'observer l'utilisation du cache. Utilisez le drapeau `--progress=plain` pour voir la sortie détaillée, y compris si les couches ont été tirées du cache ou reconstruites : ```bash docker build --progress=plain -t myapp:test . ``` Recherchez des lignes comme `CACHED` dans la sortie : ``` #5 [dependencies 3/4] RUN --mount=type=cache, target=/root/.npm npm ci #5 CACHED ``` Si vous voyez `CACHED` pour une étape, le cache a été utilisé. Si vous voyez la commande s'exécuter, le cache a été manqué pour cette étape. Pour obtenir des informations plus granulaires, vous pouvez utiliser les exportations de métadonnées de BuildKit. Ajoutez `--build-arg BUILDKIT_INLINE_CACHE=1` pour intégrer les métadonnées de cache dans l'image, ou utilisez `--cache-from` et `--cache-to` pour tirer et pousser les couches de cache vers un registre. Par exemple, pour tirer le cache d'un registre : ```bash docker buildx build --cache-from=type=registry,ref=myrepo/myapp:cache --cache-to=type=registry,ref=myrepo/myapp:cache,mode=max -t myapp:latest . ``` Ensuite, pour vérifier que le cache est tiré, exécutez une construction sans apporter de modifications. Elle devrait se terminer presque instantanément et signaler toutes les étapes comme `CACHED`. Vous pouvez également inspecter les métadonnées du cache BuildKit en utilisant `docker buildx du` (sur le bureau) ou en interrogeant l'API BuildKit si vous avez un démon séparé. Pour Docker Desktop, `docker buildx du` montre l'utilisation du disque par constructeur. Diagnostiquer les défauts de cache est une tâche courante. Lorsqu'une étape est reconstruite de manière inattendue, demandez : Pourquoi le cache a-t-il été manqué ? Les raisons les plus courantes sont : - L'instruction du Dockerfile a changé. - Les fichiers copiés dans le contexte de construction ont changé. - Les arguments de construction ou les variables d'environnement ont changé. - Le cache a été purgé ou effacé. - Le cache n'est pas partagé entre les environnements de construction (par exemple, chaque exécuteur CI a son propre cache local). Pour identifier la cause, comparez le Dockerfile et les fichiers pertinents. Utilisez `docker history` pour voir les couches d'une image existante et identifier à quel moment le cache a divergé : ```bash docker history myapp:latest ``` Cela montre chaque couche et sa taille, mais pas l'état du cache. Pour tester le comportement de cache d'une instruction spécifique isolément, vous pouvez diviser la construction en commandes `docker build` séparées en utilisant des constructions multi-étapes ou des images temporaires. Par exemple, construisez l'étape des dépendances seule : ```bash docker build --target dependencies -t myapp:deps . ``` Ensuite, construisez l'image complète et voyez si elle réutilise le cache de cette étape. Si vous utilisez un cache de registre, assurez-vous que l'image de cache est correctement poussée et tirée. Vous pouvez lister les balises dans le registre pour confirmer que l'image de cache existe. Vérifiez également la connectivité réseau et l'authentification, car les échecs de cache de registre se manifestent souvent par des défauts de cache sans erreurs évidentes. **Responsable et cadence de révision :** Le développeur responsable de la construction (par exemple, « Sam Rivera, développeur backend senior ») doit vérifier le comportement du cache après tout changement de Dockerfile. Pour les pipelines CI, l'ingénieur DevOps (« Jordan Lee ») examine les journaux de cache chaque semaine. ## Modes de défaillance et récupération Même avec une configuration soignée, des défaillances peuvent survenir. Voici les modes de défaillance courants et comment s'en remettre. ### Épuisement de l'espace disque Les caches de construction peuvent croître rapidement, surtout avec de grandes dépendances et des constructions fréquentes. Lorsque le disque se remplit, les constructions échouent avec des erreurs « no space left on device ». Pour récupérer, identifiez d'abord ce qui consomme de l'espace : ```bash docker system df ``` Cela montre l'espace utilisé par les images, les conteneurs, les volumes et le cache de construction. Pour voir l'utilisation détaillée du cache par constructeur, utilisez : ```bash docker buildx du ``` Ensuite, purgez le cache de construction : ```bash docker builder prune ``` Cela supprime tout le cache de construction inutilisé. Pour un nettoyage plus contrôlé, vous pouvez spécifier des filtres comme `--keep-storage=10GB` pour conserver une certaine quantité. Par exemple : ```bash docker builder prune --keep-storage=10GB ``` Cela supprimera le cache plus ancien que nécessaire pour rester sous 10 Go. Notez que la purge entraînera des défauts de cache lors des constructions suivantes jusqu'à ce qu'il soit reconstruit. ### Cache obsolète entraînant des dépendances périmées Comme les couches sont mises en cache, un changement dans une image de base ou une version de dépendance peut ne pas être pris en compte si la clé de cache ne change pas. Par exemple, si vous utilisez une balise flottante comme `FROM node:18`, l'image de base peut être mise à jour sur le registre, mais votre cache local a l'ancienne version. Pour forcer la reconstruction d'une étape spécifique, utilisez `--no-cache` pour cette construction, ou modifiez un argument de construction qui affecte l'étape. Vous pouvez également épingler les images de base par digest pour garantir la reproductibilité et éviter les mises à jour accidentelles. ### Corruption du cache Dans de rares cas, les métadonnées du cache peuvent être corrompues, entraînant des erreurs de construction ou des couches incohérentes. Si vous soupçonnez une corruption, effacez complètement le cache et reconstruisez : ```bash docker builder prune -af ``` Le drapeau `-a` supprime tout le cache, pas seulement les éléments en suspens. C'est une option nucléaire mais qui résout souvent les problèmes mystérieux. ### Défauts de cache en CI en raison d'un cache non partagé Si chaque exécuteur CI a son propre cache local, les constructions sur différents exécuteurs manqueront le cache. Pour partager le cache, utilisez le cache de registre comme décrit précédemment, ou configurez un démon BuildKit partagé accessible à tous les exécuteurs. La récupération ici est architecturale : assurez-vous que toutes les constructions utilisent la même source et destination de cache. ### Retour en arrière Si un changement de configuration de cache cause des problèmes, vous pouvez revenir en arrière en annulant les modifications du Dockerfile ou les paramètres BuildKit. Par exemple, si vous avez ajouté un montage de cache et qu'il cause des problèmes, supprimez la ligne `--mount` et reconstruisez. Comme le changement est dans le contrôle de version, le retour en arrière est simple. **Responsable et cadence de révision :** L'ingénieur de garde (rôle tournant) gère la récupération immédiate. Le responsable de l'équipe DevOps (« Jordan Lee ») examine les rapports d'incident mensuellement pour identifier les problèmes récurrents. ## Pièges courants et comment les éviter Au-delà des défaillances manifestes, il existe des pièges qui peuvent silencieusement dégrader les performances de mise en cache ou causer des problèmes subtils. Voici les plus courants, pourquoi ils surviennent et comment les éviter ou s'en remettre. ### Commandes COPY trop larges Si vous copiez tout le répertoire du projet tôt dans le Dockerfile, tout changement dans n'importe quel fichier invalide toutes les couches suivantes. Cela force la réinstallation des dépendances même si seul le code source a changé. **Pourquoi cela arrive :** Les développeurs commencent souvent par `COPY . .` comme première instruction par simplicité, sans réaliser les implications pour le cache. **Comment éviter :** Structurez le Dockerfile pour copier d'abord les fichiers de manifeste des dépendances, installer les dépendances, puis copier le reste du code. Utilisez des étapes séparées si nécessaire. Par exemple : ```dockerfile COPY package.json package-lock.json ./ RUN npm ci COPY . . ``` ### Ignorer .dockerignore Sans fichier `.dockerignore`, de grands répertoires comme `.git` ou `node_modules` sont envoyés dans le contexte de construction, augmentant le temps de construction et la taille du cache. Les fichiers inutiles peuvent également invalider le cache s'ils changent. **Pourquoi cela arrive :** C'est facile à négliger, surtout lorsqu'on démarre un nouveau projet. **Comment éviter :** Incluez toujours un fichier `.dockerignore` à la racine du projet. Commencez par des motifs courants comme `.git`, `node_modules`, `*.log`, et adaptez à votre projet. ### Utiliser des balises flottantes pour les images de base Les images de base étiquetées `latest` ou `18` peuvent changer au fil du temps. Si votre cache détient l'ancienne version, vous construisez peut-être sur des dépendances obsolètes sans vous en rendre compte. **Pourquoi cela arrive :** Les balises flottantes sont pratiques pour le développement, et les équipes peuvent ne pas les épingler lors du passage en production. **Comment éviter :** Épinglez les images de base par digest dans les Dockerfiles de production. Par exemple : ```dockerfile FROM node:18-alpine@sha256:... ``` Utilisez un travail planifié pour mettre à jour régulièrement le digest après les tests. ### Négliger les montages de cache pour les gestionnaires de paquets Les gestionnaires de paquets comme npm, pip ou go téléchargent les dépendances dans un répertoire de cache. Sans montage de cache, ces téléchargements sont répétés à chaque construction, même si la liste des dépendances n'a pas changé. **Pourquoi cela arrive :** Les montages de cache sont une fonctionnalité de BuildKit que de nombreux développeurs ignorent ou oublient d'utiliser. **Comment éviter :** Identifiez le répertoire de cache de votre gestionnaire de paquets et ajoutez un montage de cache dans l'instruction RUN qui installe les dépendances. Pour npm, c'est `/root/.npm` ; pour pip, c'est `/root/.cache/pip` ; pour go, c'est `/go/pkg/mod`. ### Ne pas partager le cache en CI Si chaque exécuteur CI a son propre cache local, les constructions sur différents exécuteurs manqueront fréquemment le cache, ralentissant le pipeline. **Pourquoi cela arrive :** Les systèmes CI commencent souvent avec des paramètres par défaut où chaque exécuteur est isolé, et les équipes peuvent ne pas implémenter la mise en cache de registre. **Comment éviter :** Configurez la mise en cache de registre avec `--cache-from` et `--cache-to` comme décrit précédemment. Sinon, utilisez un démon BuildKit centralisé. **Responsable et cadence de révision :** Le responsable de l'équipe DevOps (« Jordan Lee ») doit auditer ces pièges trimestriellement, et les développeurs doivent signaler tout défaut de cache rencontré dans un journal partagé. ## Liste de contrôle opérationnelle Pour que la mise en cache des couches Docker fonctionne bien en production, intégrez les tâches suivantes dans votre routine opérationnelle. ### Vérifications quotidiennes - Surveillez l'utilisation du disque sur les hôtes de construction. Utilisez `df -h` et `docker system df` pour vérifier les tendances de croissance. - Vérifiez les journaux de construction pour des défauts de cache ou des erreurs inattendus. Si une construction qui devrait être mise en cache est reconstruite, enquêtez rapidement. - Assurez-vous que les exécuteurs CI ont des poussées et des tirages de cache réussis ; les poussées échouées peuvent être des signes avant-coureurs de problèmes de registre. **Responsable :** Ingénieur DevOps de garde (tournant). Examinez les résultats lors du stand-up quotidien. ### Tâches hebdomadaires ou bihebdomadaires - Examinez et purgez le cache de construction si nécessaire. Utilisez `docker builder prune --keep-storage=` pour définir une taille de cache raisonnable en fonction de votre charge de travail. - Inspectez la cohérence des versions Docker entre les environnements. Exécutez `docker version` sur un échantillon d'hôtes et comparez. - Vérifiez que les images de base sont à jour. Reconstruisez périodiquement avec `--pull` pour mettre à jour les images de base, ou utilisez un travail planifié pour tirer de nouvelles images de base et déclencher des constructions. **Responsable :** Le responsable de l'équipe DevOps (« Jordan Lee ») effectue ces vérifications ou délègue à un membre de l'équipe. Consignez les résultats dans le canal des opérations. ### Tâches mensuelles ou à la demande - Auditez les changements de Dockerfile pour des pratiques favorables au cache. Assurez-vous que `.dockerignore` est maintenu et que les montages de cache sont utilisés lorsque cela est approprié. - Testez le comportement du cache après les mises à niveau de Docker ou BuildKit. Exécutez une construction contrôlée avant et après la mise à niveau pour mesurer les taux de succès du cache. - Documentez tout changement de configuration de mise en cache dans votre runbook opérationnel. **Responsable :** L'équipe d'ingénierie de plateforme (dirigée par « Jordan Lee ») tient une réunion de révision mensuelle. ### Tableau de maintenance | Tâche | Fréquence | Commande ou action | Résultat attendu | |------|-----------|-------------------|-----------------| | Vérifier l'utilisation du disque | Quotidienne | `df -h` | L'espace libre reste au-dessus du seuil | | Vérifier l'utilisation du disque Docker | Quotidienne | `docker system df` | Taille du cache de construction dans les limites | | Purger le cache de construction | Hebdomadaire | `docker builder prune --keep-storage=10GB` | Cache réduit, aucune erreur | | Vérifier la version du constructeur | Hebdomadaire | `docker buildx ls` | Tous les constructeurs signalent la même version de BuildKit | | Mettre à jour les images de base | Mensuelle | `docker build --pull` | Dernière image de base tirée | ### Liste de contrôle de configuration Avant d'apporter des modifications de configuration de mise en cache, passez en revue cette liste : - Sauvegardez le Dockerfile actuel et les scripts de construction associés. - Testez les changements dans un environnement de développement. - Mesurez le temps de construction avant et après. - Vérifiez les succès de cache avec `--progress=plain`. - Déployez progressivement sur un projet d'abord. - Documentez le changement et sa justification. **Responsable :** Le développeur proposant le changement (par exemple, « Sam Rivera ») remplit cette liste et la soumet à l'examen de « Jordan Lee ». ## Conclusion La mise en cache des couches Docker est un outil puissant pour réduire les temps de construction et améliorer l'efficacité, mais elle nécessite une gestion délibérée en production. En suivant la liste de contrôle opérationnelle de cet article, vous pouvez éviter les pièges courants tels que les défauts de cache, l'épuisement du disque et les dépendances obsolètes. Commencez par un inventaire complet de l'environnement, configurez la mise en cache en toute sécurité à l'aide de montages de cache et de mise en cache de registre, vérifiez le comportement du cache avec des commandes observables et préparez-vous aux défaillances avec des étapes de récupération claires. Enfin, intégrez des vérifications et une maintenance régulières dans la routine de votre équipe. Avec ces pratiques, vous maintiendrez vos constructions Docker rapides, fiables et sous contrôle. Pour aller plus loin, explorez la documentation officielle de Docker sur BuildKit et la gestion du cache. Expérimentez avec les montages de cache dans un projet non critique pour gagner en confiance avant une adoption plus large. Un processus opérationnel clair, comme décrit ici, réduira les retouches et maintiendra votre pipeline de construction en bon état de marche.