Introduction
Le marquage d’images Docker est une opération fondamentale dans les workflows de conteneurs. Chaque commande docker tag crée une nouvelle référence vers une image existante, mais lorsque vous marquez des milliers d’images sur plusieurs registres, les performances peuvent se dégrader. Ce ralentissement peut bloquer les pipelines CI/CD, retarder les déploiements et frustrer les développeurs. L’optimisation des performances du marquage d’images ne concerne pas seulement la vitesse ; il s’agit de garder votre workflow de développement prévisible et efficace.
Ce guide présente une approche pratique pour diagnostiquer et optimiser les performances de marquage des images Docker. Nous commençons par inventorier votre environnement Docker, puis nous appliquons des changements de configuration sûrs, nous vérifions les améliorations avec des contrôles mesurables et nous préparons des étapes de récupération en cas d’échec. Chaque section comprend des commandes concrètes et des sorties attendues pour que vous puissiez suivre dans votre propre environnement.
Un projet pilote étroit et mesurable est la meilleure première étape : choisissez un ensemble d’images représentatif, testez localement et mesurez avant de déployer les changements. Cette approche réduit les risques et vous donne confiance dans vos décisions de réglage.
Inventaire de la version et de l’environnement
Avant de changer quoi que ce soit, documentez votre version de Docker, votre pilote de stockage et la configuration du registre. Ces détails déterminent les options d’optimisation disponibles et vous aident à détecter les régressions plus tard.
Vérifier la version et les informations de Docker
Exécutez les commandes suivantes :
docker version
docker info
Extraits de sortie attendus :
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:07:41 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: 311b9ff
Built: Thu Oct 26 09:07:41 2023
OS/Arch: linux/amd64
Experimental: false
À partir de docker info, notez le pilote de stockage (par exemple, overlay2) et le pilote de cgroup. Par exemple :
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Using metacopy: false
Native Overlay Diff: true
userxattr: false
Ces détails affectent les opérations sur les couches d’images. La plupart des systèmes modernes utilisent overlay2, qui est efficace pour le marquage car le marquage ajoute seulement une référence de métadonnées ; les couches sous-jacentes ne sont pas copiées. La ligne Native Overlay Diff: true indique que le démon peut utiliser des opérations de diff natives du système de fichiers, ce qui peut accélérer les tâches liées aux couches. Si vous voyez overlay (l’ancien pilote) ou devicemapper, envisagez de planifier une migration vers overlay2 pour de meilleures performances et une meilleure stabilité.
Identifier le registre et le volume d’images
Listez vos images pour comprendre l’échelle du marquage :
docker images --format '{{.Repository}}:{{.Tag}}' | head -n 20
Si vous avez un registre privé, notez son type et sa version. Par exemple, si vous utilisez Docker Hub, vérifiez vos limites de taux ; l’interaction avec le registre peut être un goulot d’étranglement lors du tirage et du marquage de nombreuses images. Pour vérifier vos limites de taux de tirage actuelles (authentifié si possible) :
# Pour Docker Hub, inspectez les en-têtes de limite de taux après un tirage (nécessite curl)
TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/alpine:pull" | jq -r .token)
curl -I -H "Authorization: Bearer $TOKEN" https://registry-1.docker.io/v2/library/alpine/manifests/latest | grep -i ratelimit
La sortie attendue peut montrer les tirages restants et l’heure de réinitialisation :
ratelimit-limit: 100;w=21600
ratelimit-remaining: 82;w=21600
Si vous atteignez souvent ces limites, le tirage d’images pour le marquage devient lent ou échoue. Dans ce cas, un registre local ou un cache de tirage intermédiaire (décrit plus loin) est essentiel.
Mesure de performance de référence
Avant d’optimiser, établissez une référence. Utilisez la commande time pour mesurer le marquage d’un ensemble d’images. Créez une liste d’ID d’images et de balises à tester. Voici un petit exemple :
Supposons que vous avez 100 images dans votre démon Docker local. Pour mesurer le temps de marquage de chacune avec une nouvelle balise horodatée, exécutez :
time for img in $(docker images -q); do docker tag $img "test:$(date +%s%N)"; done
Sortie attendue (exemple) :
real 0m5.231s
user 0m0.102s
sys 0m0.064s
Enregistrez le temps real comme référence. Sur un ordinateur portable de développeur typique, marquer quelques centaines d’images peut prendre quelques secondes. Si cela prend des minutes, vous avez un problème de performance. Pour des mesures plus précises, utilisez /usr/bin/time -v pour capturer la mémoire et les changements de contexte, mais le time de base suffit pour la plupart des réglages.
Mesurez également l’utilisation du CPU du démon pendant le marquage pour voir si le goulot d’étranglement est dans l’espace utilisateur ou les E/S :
# Dans un autre terminal pendant que la boucle s’exécute
docker stats --no-stream --format '{{.Name}}: CPU {{.CPUPerc}}, Mem {{.MemUsage}}'
Cela montre si le processus du démon Docker sature un cœur de CPU. Si le CPU reste faible, le goulot d’étranglement est probablement la latence du disque ou du réseau.
Chemin de configuration sûr
Les performances de marquage dépendent de la configuration du démon Docker, du système de fichiers et du comportement du client. Nous explorerons des optimisations sûres et ciblées qui ne nécessitent pas de changements radicaux.
1. Activer BuildKit pour les opérations d’images
BuildKit améliore les performances de construction et peut également affecter le marquage lors de l’utilisation de fonctionnalités Docker plus récentes. Pour activer BuildKit pour toutes les commandes, définissez la variable d’environnement :
export DOCKER_BUILDKIT=1
Ou configurez-le dans les paramètres du démon Docker (/etc/docker/daemon.json) :
{
"features": {
"buildkit": true
}
}
Puis redémarrez Docker :
sudo systemctl restart docker
Vérifiez que BuildKit est actif :
docker buildx version
Sortie attendue :
github.com/docker/buildx v0.11.2 9872040
Note : BuildKit n’accélère pas directement docker tag, mais il assure une mécanique sous-jacente cohérente pour les constructions qui produisent des images à marquer. Si votre workflow inclut la construction d’images avant de les marquer, la mise en cache des couches de BuildKit peut économiser des minutes sur le côté construction, rendant le pipeline global plus rapide même si le marquage lui-même est inchangé.
2. Utiliser le marquage local au lieu des registres distants
Si vous tirez souvent depuis un registre distant avant de marquer, vous subissez la latence du réseau. Envisagez de copier les images vers un registre local ou d’utiliser un cache de tirage intermédiaire. Par exemple, exécutez un registre local :
docker run -d -p 5000:5000 --name registry registry:2
Puis marquez vos images fréquemment utilisées localement :
docker tag myapp:latest localhost:5000/myapp:latest
Poussez l’image vers le registre local une fois :
docker push localhost:5000/myapp:latest
Ensuite, lorsque vous devez créer de nouvelles balises, marquez à partir de la copie du registre local au lieu de tirer à chaque fois depuis un registre distant. Mesurez la différence :
time docker pull myapp:latest # tirage distant
time docker tag myapp:latest localhost:5000/myapp:latest
Sortie attendue (le tirage réseau peut prendre des secondes, le marquage local des millisecondes) :
real 0m2.145s # tirage
real 0m0.003s # marquage
Pour un cache de tirage intermédiaire, configurez Docker pour utiliser un miroir de registre dans /etc/docker/daemon.json :
{
"registry-mirrors": ["http://localhost:5000"]
}
Puis redémarrez Docker. Le premier tirage d’une image passe par le miroir et la met en cache ; les tirages suivants sont locaux. Cela peut considérablement accélérer les workflows qui tirent à plusieurs reprises les mêmes images de base pour le marquage.
3. Optimiser les paramètres du pilote de stockage Docker
Pour overlay2, vous pouvez activer le diff natif pour accélérer les opérations sur les couches. C’est sûr dans la plupart des environnements. Ajoutez à /etc/docker/daemon.json :
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
Après avoir redémarré Docker, vérifiez les options du pilote de stockage :
docker info | grep -A3 'Storage Driver'
Sortie attendue :
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
La ligne Native Overlay Diff: true confirme que l’optimisation est active. Note : Changer les options du pilote de stockage peut entraîner le re-téléchargement des images sur certains systèmes ; sauvegardez d’abord vos images avec docker save ou assurez-vous de pouvoir les re-tirer depuis un registre. De plus, si votre noyau ne prend pas en charge le diff natif, cette option est ignorée.
4. Limiter les opérations de marquage concurrentes
Si vous marquez de nombreuses images dans des scripts parallèles, le démon Docker peut devenir insensible en raison de la contention. Utilisez un sémaphore pour limiter la concurrence. Par exemple, avec GNU parallel :
seq 1 100 | parallel -j 10 docker tag image{} tagged:{}
Mesurez avec différents niveaux de concurrence pour trouver le point idéal. Un benchmark simple :
for j in 1 5 10 20; do
echo "Concurrence $j:"
time seq 1 100 | parallel -j $j docker tag image{} tagged:{}
done
La sortie attendue peut montrer une amélioration du temps total avec une concurrence modérée, mais une concurrence excessive peut dégrader en raison de la contention sur les verrous du démon. Par exemple, sur un système de test :
Concurrence 1:
real 0m8.415s
Concurrence 5:
real 0m3.102s
Concurrence 10:
real 0m2.877s
Concurrence 20:
real 0m4.190s
Dans ce cas, la concurrence 10 est la meilleure. En dessous de 10, le CPU reste inactif ; au-dessus de 10, le démon passe plus de temps à gérer les verrous qu’à travailler.
5. Utiliser des balises immuables avec parcimonie
Si vous créez de nombreuses balises uniques (par exemple, des balises horodatées), vous créez peut-être de nombreuses nouvelles références. Bien que le marquage lui-même soit bon marché, les opérations de métadonnées peuvent s’accumuler. Au lieu de milliers de balises uniques, envisagez d’utiliser un ensemble fixe de balises mutables par environnement (par exemple, dev, staging, prod). Re-marquer le même nom de balise écrase la référence et peut être plus rapide en raison de la mise en cache dans le démon.
Par exemple, au lieu de :
docker tag myapp:latest myapp:2024-01-15T120000Z
docker tag myapp:latest myapp:2024-01-15T120100Z
# ... des milliers de balises horodatées
Utilisez :
docker tag myapp:latest myapp:prod
Chaque fois que vous marquez avec myapp:prod, le démon met à jour la même référence, et les opérations suivantes impliquant cette balise sont favorables au cache. Si vous avez besoin d’auditabilité, envisagez de stocker les métadonnées de construction dans un système séparé plutôt que dans le nom de la balise.
6. Garder le cache local d’images propre
Un grand nombre d’images en suspens peut ralentir les commandes Docker qui énumèrent les images. Élaguez périodiquement :
docker image prune -f
Attention : N’élaguez que ce dont vous n’avez pas besoin. Vérifiez l’espace avant d’élaguer :
docker system df
Sortie attendue :
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 142 3 12.85GB 11.07GB (86%)
Containers 5 2 1.2GB 68.5MB (5%)
Local Volumes 10 1 2.4GB 1.9GB (79%)
Build Cache 0 0 0B 0B
Élaguer les images en suspens (celles sans balise) est sûr car elles ne sont référencées par aucun conteneur ou balise. Cependant, si vous avez des images marquées avec une balise temporaire que vous supprimez ensuite, elles deviennent en suspens. Planifiez l’élagage dans votre maintenance hebdomadaire, et utilisez docker image prune -a seulement si vous êtes certain de ne pas avoir besoin d’images non balisées pour les constructions futures.
Vérification et diagnostics
Après avoir appliqué les changements, mesurez à nouveau et comparez avec la référence. Utilisez le même benchmark de marquage.
Réexécuter le benchmark de référence
Exécutez la même boucle que précédemment :
time for img in $(docker images -q); do docker tag $img "test2:$(date +%s%N)"; done
Comparez le temps real. Une amélioration de 20 % est un bon signe. Si aucune amélioration, envisagez d’autres goulots d’étranglement comme la latence du réseau ou du stockage. Assurez-vous d’exécuter le benchmark plusieurs fois pour tenir compte de la variance, et ignorez la première exécution après un redémarrage du démon car les caches peuvent être froids.
Diagnostiquer avec les événements Docker
Activez les événements Docker pour voir ce qui se passe pendant le marquage :
docker events --filter event=tag --since 10m
Pendant le marquage dans un autre terminal, vous devriez voir des événements comme :
2024-01-15T12:00:00.123456789Z container tag image=sha256:abc... name=test2:123456789
Si les événements sont retardés, le démon peut être occupé ; vérifiez les journaux du démon.
Vérifier les journaux du démon pour les opérations lentes
journalctl -u docker -n 50 --no-pager
Recherchez des avertissements concernant des opérations de métadonnées lentes ou des problèmes de pilote de stockage. Par exemple, des messages comme msg="layer store: deleting" avec de longues durées peuvent indiquer une contention d’E/S. Si vous voyez des erreurs de délai d’attente répétées, envisagez d’augmenter les délais du démon ou de réduire les opérations concurrentes.
Utiliser strace sur la CLI Docker (avancé)
Pour une analyse au niveau des commandes, tracez les appels système pendant un marquage :
strace -c docker tag myimage:latest myimage:test
La sortie attendue montre le temps par appel système. Recherchez des appels excessifs fsync ou open. Si fsync domine, le goulot d’étranglement est la latence de vidage du disque. Vous pouvez réduire la fréquence de fsync en montant le répertoire de données Docker sur un système de fichiers avec une synchronisation plus rapide (par exemple, un SSD) ou en ajustant les options de montage du système de fichiers (par exemple, noatime).
Valider l’intégrité des balises
Assurez-vous que les balises pointent vers les bonnes images :
docker inspect myimage:test --format '{{.Id}}'
docker inspect myimage:latest --format '{{.Id}}'
Les deux doivent correspondre au même ID d’image. Si ce n’est pas le cas, vous pouvez avoir une condition de course dans votre script de marquage ou un registre mal configuré. Réexécutez la commande de marquage et vérifiez à nouveau.
Modes de défaillance et récupération
Même avec un réglage soigneux, les choses peuvent mal tourner. Voici les modes de défaillance courants et comment récupérer.
1. Le marquage échoue en raison d’une surcharge du démon
Symptôme : les commandes docker tag se bloquent ou renvoient Error response from daemon: context deadline exceeded.
Récupération :
- Vérifiez l’état du démon :
sudo systemctl status docker - Redémarrez Docker si nécessaire :
sudo systemctl restart docker - Annulez les changements de concurrence récents.
- Si le problème persiste, augmentez les paramètres de délai du démon dans
/etc/docker/daemon.json:
{
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 64000,
"Soft": 64000
}
}
}
Puis redémarrez Docker. Cela augmente les limites de descripteurs de fichiers, ce qui peut aider en cas de charge élevée.
2. Le changement de pilote de stockage casse la disponibilité des images
Si vous avez changé de pilote de stockage et que les images disparaissent, annulez le changement dans daemon.json et redémarrez Docker. Les images stockées sous l’ancien pilote restent généralement compatibles si vous revenez en arrière. Sinon, vous devrez peut-être les tirer à nouveau. Pour éviter cela, sauvegardez les images avant de changer les paramètres de stockage :
docker save -o all-images.tar $(docker images -q)
3. Registre local indisponible
Si vous êtes passé à un registre local et qu’il est en panne, le marquage vers lui échoue. Démarrez le registre ou ajustez vos cibles de marquage. Pour vérifier la santé du registre :
curl -I http://localhost:5000/v2/
Un registre sain renvoie HTTP 200. S’il échoue, redémarrez le conteneur du registre :
docker start registry
4. L’élagage a supprimé des images nécessaires
Si vous avez élagué des images et que vous en avez besoin plus tard, vous devez les reconstruire ou les tirer. Sauvegardez toujours les images critiques (par exemple, docker save -o backup.tar image:tag) avant d’élaguer. Alternativement, marquez les images importantes avec un préfixe protégé et utilisez un filtre d’élagage pour les éviter :
docker image prune -f --filter "label!=protected=true"
5. Dégradation des performances après les changements
Si les performances se dégradent, annulez un changement à la fois. Par exemple, supprimez BuildKit et testez. Gardez une sauvegarde versionnée de /etc/docker/daemon.json et de tous les scripts que vous modifiez. Utilisez git pour les configurations si possible, afin de pouvoir revenir avec git checkout.
Liste de contrôle de récupération
- Restaurez le
daemon.jsonprécédent (gardez une copie de sauvegarde) - Redémarrez le démon Docker
- Vérifiez les images et les balises avec
docker images - Réexécutez le benchmark de référence pour confirmer les performances précédentes
Pièges courants et comment les éviter
Plusieurs erreurs reviennent lors de l’optimisation des performances de marquage Docker. Les comprendre fait gagner du temps et évite les pannes.
1. Optimiser sans référence
Beaucoup d’ingénieurs changent les paramètres du démon à l’aveugle, puis ne peuvent pas dire si les performances se sont améliorées. Mesurez toujours avant et après avec le même benchmark. Utilisez un script qui enregistre la sortie de time dans un fichier pour comparaison :
( time for img in $(docker images -q); do docker tag $img "bench:$(date +%s%N)"; done ) 2> bench_before.txt
2. Ignorer les limites de taux du registre
Tirer des images pour le marquage depuis Docker Hub sans vérifier les limites de taux peut provoquer des échecs soudains en CI. Authentifiez vos tirages et surveillez les en-têtes de limite. Mieux encore, mettez en place un cache de tirage intermédiaire pour réduire les requêtes externes.
3. Trop paralléliser les opérations de marquage
Exécuter trop de commandes docker tag concurrentes peut épuiser les descripteurs de fichiers du démon ou provoquer une contention sur les verrous, rendant tout plus lent. Testez systématiquement les niveaux de concurrence, comme montré précédemment, et définissez une limite stricte dans vos scripts en utilisant xargs -P ou parallel -j.
4. Créer des balises uniques excessives
Générer une nouvelle balise pour chaque construction peut remplir le stockage de métadonnées du démon, ralentissant toute commande qui liste les images. Utilisez des balises mutables pour les étapes du cycle de vie et stockez les métadonnées de construction ailleurs. Si vous devez avoir des balises uniques, envisagez de les pousser vers un registre puis de supprimer périodiquement les copies locales.
5. Négliger l’hygiène du pilote de stockage
Avec le temps, les images en suspens et les couches obsolètes s’accumulent, ce qui ralentit docker images et docker tag. Planifiez un élagage régulier et des vérifications de disque. Surveillez l’utilisation de /var/lib/docker avec df -h et définissez des alertes lorsque l’utilisation dépasse 80 %.
6. Appliquer des changements globaux sans test
Modifier la configuration du démon affecte tous les conteneurs et images. Si une option de pilote de stockage est incompatible avec votre noyau, Docker peut ne pas démarrer. Testez toujours les changements dans un environnement de développement d’abord, et ayez un plan de retour en arrière.
Liste de contrôle des opérations
Utilisez cette liste de contrôle pour la gestion continue des performances de marquage. Attribuez chaque élément à un seul responsable pour garantir la responsabilité.
Vérifications quotidiennes/hebdomadaires
- Surveiller l’utilisation des ressources du démon Docker :
docker stats --no-stream(Responsable : ingénieur DevOps de garde, quotidien) - Vérifier l’espace disque :
df -h /var/lib/docker(Responsable : administrateur système, hebdomadaire) - Examiner les journaux du démon pour les erreurs :
journalctl -u docker -p err -b(Responsable : ingénieur DevOps de garde, quotidien)
Revue mensuelle d’optimisation
- Réexécuter le benchmark de marquage et comparer à la référence (Responsable : responsable performance, mensuel - examiner les résultats la première semaine)
- Analyser le nombre d’images et élaguer si nécessaire :
docker image prune -a -favec prudence (Responsable : ingénieur DevOps, mensuel) - Mettre à jour la version de Docker si de nouvelles améliorations de performance sont disponibles (Responsable : chef d’équipe plateforme, mensuel)
Gestion des changements
- Toujours tester les changements sur un environnement non production d’abord (Responsable : gestionnaire de version)
- Utiliser le contrôle de version pour
daemon.jsonet les scripts (Responsable : ingénieur DevOps) - Documenter tout changement de paramètre d’optimisation (Responsable : ingénieur qui a fait le changement, le jour même)
Modèle de référence de performance
| Métrique | Référence | Après optimisation | Changement |
|---|---|---|---|
| Temps réel pour marquer 100 images | 5,2 s | 3,8 s | -27 % |
| Débit de marquage concurrent | 20/s | 28/s | +40 % |
| CPU du démon pendant le marquage | 45 % | 30 % | -33 % |
Référence rapide des commandes clés
| Commande | Objectif |
|---|---|
docker info | Afficher la configuration du démon |
docker tag SOURCE TARGET | Créer une balise |
time docker tag ... | Mesurer le temps de marquage |
docker events --filter event=tag | Observer les événements de marquage |
docker image prune | Supprimer les images inutilisées |
Conclusion
L’optimisation des performances de marquage des images Docker est un moyen pratique de garder votre pipeline efficace. Commencez par un projet pilote étroit : inventorier votre environnement, mesurer la référence, puis appliquer des optimisations sûres comme l’activation de BuildKit, l’utilisation d’un registre local et la limitation de la concurrence. Vérifiez chaque changement avec le même benchmark et diagnostiquez avec des outils comme docker events et strace. Préparez-vous aux échecs en sauvegardant les configurations et les images, et ayez un plan de retour en arrière. En suivant la liste de contrôle des opérations, vous pouvez maintenir des performances de marquage optimales à mesure que votre nombre d’images augmente.
Les prochaines étapes clés sont :
- Exécuter le benchmark de référence sur votre configuration Docker actuelle.
- Appliquer un changement d’optimisation à la fois et mesurer son effet.
- Documenter les changements et établir une revue de performance régulière.
Avec ces pratiques, vous garderez le marquage des images Docker rapide et fiable, réduisant les frictions dans votre workflow de développement.