Chapitre 410 minIntermédiaire
Images Docker : couches, étiquettes et registres
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- décomposer le nom complet d'une image : registre, espace, dépôt, étiquette ;
- distinguer une étiquette mobile d'une empreinte immuable ;
- expliquer le partage des couches et le piège du fichier supprimé ;
- choisir une image de base fiable et légère.
4 exercices corrigés1 schéma
Une image Docker est un empilement de couches en lecture seule, identifiée par un nom, une étiquette (tag) et une empreinte unique (digest), et stockée dans un registre comme Docker Hub. Ce chapitre explique comment se lit le nom d'une image, pourquoi latest est un piège, comment les couches sont partagées entre images et conteneurs, et comment choisir une image de base : officielle, slim, alpine ou durcie.
Lire le nom d'une image
Le nom complet d'une image a cette forme :
[registre/][espace/]dépôt[:étiquette][@sha256:empreinte]
Quand une partie manque, Docker la complète : le registre par défaut est Docker Hub (docker.io), l'espace des images officielles est library, l'étiquette par défaut est latest.
| Ce que tu écris | Ce que Docker comprend |
|---|---|
nginx | docker.io/library/nginx:latest |
nginx:1.30-alpine | docker.io/library/nginx:1.30-alpine |
grafana/grafana:12.2.0 | docker.io/grafana/grafana:12.2.0 (espace de l'éditeur) |
ghcr.io/sorbier/sorbier-frais:2.4.0 | Registre de GitHub, espace sorbier, dépôt sorbier-frais, étiquette 2.4.0 |
registre.ad.sorbier.example:5000/outils/sauvegarde:1.2 | Un registre privé interne, sur le port 5000 |
Étiquettes et empreintes
Une étiquette est un nom lisible, posé sur une image par celui qui la publie. Elle est mobile : l'éditeur peut la déplacer sur une nouvelle image. nginx:1.30-alpine désignait en avril 2026 la version 1.30.0, et en juillet la 1.30.4. latest n'est qu'une étiquette comme les autres, celle qu'on prend quand on n'en précise aucune : elle ne garantit pas d'être la plus récente, et surtout pas d'être stable.
Une empreinte (digest) est le hachage SHA-256 du contenu de l'image. Elle est immuable : si un seul octet change, l'empreinte change.
docker pull nginx:1.30-alpine
docker image inspect --format '{{index .RepoDigests 0}}' nginx:1.30-alpine
nginx@sha256:5c1b9e1f8c0f3a…
L'empreinte complète compte 64 caractères hexadécimaux ; elle est raccourcie ici.
| Référence | Stabilité | Usage |
|---|---|---|
nginx ou nginx:latest | Change à chaque publication, y compris de version majeure | Un essai, jamais un serveur |
nginx:1 | Suit toutes les versions 1.x | Rarement souhaitable |
nginx:1.30-alpine | Suit les correctifs de la 1.30 | Le bon compromis pour un service : correctifs de sécurité sans changement de fonctionnement |
nginx:1.30.4-alpine | Fixe par convention | Reproductibilité forte, mais il faut changer l'étiquette à chaque correctif |
nginx@sha256:… | Garantie par le registre | La référence la plus sûre, utilisée par la CI pour déployer exactement l'image testée |
Les couches d'une image
Chaque instruction du Dockerfile qui modifie les fichiers (RUN, COPY, ADD) ajoute une couche : la liste des fichiers ajoutés, modifiés ou supprimés par rapport à la couche du dessous. Les autres instructions (CMD, ENV, EXPOSE) ne changent que les métadonnées.
docker history nginx:1.30-alpine
L'historique liste chaque couche, la commande qui l'a produite et sa taille, de la plus récente à la plus ancienne.
Trois conséquences pratiques :
- Les couches sont partagées. Dix images construites sur
node:24-slimne stockent qu'une fois les couches denode:24-slim, et undocker pullne télécharge que les couches qui manquent (Already existspour les autres). - Un conteneur ajoute une couche inscriptible, propre à lui, au-dessus des couches de l'image. Quand il modifie un fichier de l'image, OverlayFS copie d'abord ce fichier dans la couche inscriptible (copy-on-write) : l'image n'est jamais modifiée.
- Supprimer un fichier dans une couche ne l'efface pas des couches précédentes. Il est simplement masqué. Un secret copié dans une couche puis supprimé dans la suivante reste lisible par quiconque récupère l'image.
Choisir une image de base
Sur Docker Hub, trois catégories d'images n'inspirent pas la même confiance :
| Catégorie | Repère | Confiance |
|---|---|---|
| Docker Official Images | Pas d'espace (nginx, mysql, node), badge Docker Official Image | Maintenues par une équipe dédiée avec les éditeurs, reconstruites à chaque correctif de sécurité |
| Verified Publisher | Espace de l'éditeur (grafana/grafana), badge Verified Publisher | Publiées par l'éditeur du logiciel lui-même |
| Images communautaires | Espace d'un particulier (jdupont/nginx-php) | Aucune garantie : contenu, mises à jour et intentions inconnus |
Pour une même image officielle, plusieurs variantes existent :
| Variante | Base | Taille relative | Pour |
|---|---|---|---|
node:24 | Debian complète, avec compilateurs | La plus lourde (plus d'un gigaoctet) | Construire, compiler des modules natifs |
node:24-slim | Debian réduite | Plusieurs fois plus légère | Faire tourner l'application : bon choix par défaut |
node:24-alpine | Alpine Linux et sa bibliothèque C musl | La plus légère des trois | Les images minimales, si l'application tolère musl |
distroless, scratch | Presque rien, ou rien du tout : pas de shell, pas de gestionnaire de paquets | Minimale | Un binaire autonome (Go, Rust) ; surface d'attaque minimale |
Docker Hardened Images (dhi.io) | Debian ou Alpine durcies, gratuites depuis décembre 2025 | Minimale | Des images réduites au strict nécessaire, reconstruites à chaque correctif |
Les registres et leurs limites
| Registre | Adresse | Particularité |
|---|---|---|
| Docker Hub | docker.io | Registre par défaut ; téléchargements limités sans compte |
| GitHub Container Registry | ghcr.io | Lié aux dépôts GitHub, idéal avec GitHub Actions |
| Registre de GitLab | registry.gitlab.com | Lié aux projets GitLab |
| Harbor | Auto-hébergé | Registre d'entreprise : comptes, projets, analyse de vulnérabilités, réplication |
registry:3 | Auto-hébergé | Le registre libre minimal (projet Distribution), à protéger soi-même (chapitre 12) |
Docker Hub limite les téléchargements depuis avril 2025 : sans compte, 10 téléchargements par heure par adresse IPv4 (ou par préfixe IPv6 /64) ; avec un compte gratuit, 100 par heure. Toute une salle de TP qui sort par la même adresse publique atteint vite la limite sans compte.
docker login # identifiants Docker Hub, ou un jeton d'accès personnel
docker logout
Pour transférer une image sans registre, vers une machine isolée d'Internet par exemple :
docker save -o sorbier-frais.tar ghcr.io/sorbier/sorbier-frais:2.4.0
docker load -i sorbier-frais.tar
Exercices
Exercice 1 sur 4 Décomposer des noms d'images Réussi À revoir
Pour chaque image, donne le registre, l'espace, le dépôt et l'étiquette réellement utilisés.
mysqlzabbix/zabbix-server-mysql:alpine-7.0-latestghcr.io/sorbier/sorbier-frais@sha256:9b2f…localhost:5000/outils/sauvegarde
Voir la réponseMasquer la réponse de l'exercice 1
- Registre
docker.io, espacelibrary(image officielle), dépôtmysql, étiquettelatest. - Registre
docker.io, espacezabbix(l'éditeur), dépôtzabbix-server-mysql, étiquettealpine-7.0-latest. - Registre
ghcr.io, espacesorbier, dépôtsorbier-frais, pas d'étiquette : l'image est désignée par son empreinte, ce qui la fixe définitivement. - Registre
localhost:5000(un nom suivi d'un port est lu comme un registre), espaceoutils, dépôtsauvegarde, étiquettelatest.
Tu avais trouvé ?
Exercice 2 sur 4 Le partage des couches Réussi À revoir
Sur srv-gestion, trois images sont construites sur node:24-slim, dont les couches pèsent 220 Mo au total. Les couches propres à chaque application pèsent 35 Mo, 60 Mo et 15 Mo. Quatre conteneurs tournent à partir de la première image, deux à partir de la deuxième. Combien d'espace disque les images occupent-elles, couches inscriptibles des conteneurs mises à part ? Combien faudrait-il si les couches n'étaient pas partagées entre images ?
Voir la réponseMasquer la réponse de l'exercice 2
Avec partage : la base une seule fois, plus les couches propres à chaque image : 220 + 35 + 60 + 15 = 330 Mo. Le nombre de conteneurs ne change rien : ils réutilisent les couches de leur image et n'ajoutent que leur couche inscriptible.
Sans partage : chaque image porterait sa propre copie de la base : (220 + 35) + (220 + 60) + (220 + 15) = 255 + 280 + 235 = 770 Mo, soit 440 Mo de plus.
Tu avais trouvé ?
Exercice 3 sur 4 La salle de TP bloquée Réussi À revoir
Une salle de 24 étudiants qui sortent sur Internet par la même adresse IPv4 publique lance en même temps docker pull de trois images différentes, sans compte Docker Hub. À partir de quel téléchargement les erreurs apparaissent-elles, et que propose l'administrateur ?
Voir la réponseMasquer la réponse de l'exercice 3
Sans compte, Docker Hub accepte 10 téléchargements par heure et par adresse IPv4. La salle en demande 24 × 3 = 72 : les 10 premiers passent, les erreurs commencent au onzième (toomanyrequests).
Solutions : que chaque étudiant se connecte avec un compte gratuit (docker login, 100 téléchargements par heure et par compte) ; installer un registre miroir (cache) dans le réseau de l'établissement, qui ne télécharge chaque image qu'une fois ; ou préparer les images à l'avance (docker save, puis docker load sur chaque poste).
Tu avais trouvé ?
Exercice 4 sur 4 Le secret effacé Réussi À revoir
Un développeur a écrit ces lignes dans un Dockerfile, puis publié l'image sur Docker Hub en se disant que le fichier est supprimé :
COPY .env /app/.env
RUN npm run migrate && rm /app/.env
A-t-il raison ? Que doit-il faire maintenant ?
Voir la réponseMasquer la réponse de l'exercice 4
Non. COPY .env crée une couche qui contient le fichier ; le rm de l'instruction suivante crée une autre couche qui le masque, sans l'effacer de la précédente. N'importe qui peut télécharger l'image, extraire ses couches (docker save, puis une archive tar) et lire .env.
Ce qu'il doit faire, dans l'ordre :
- considérer les secrets comme compromis et les changer tous (mot de passe de base, clés d'API) ;
- supprimer l'image et ses étiquettes du registre ;
- corriger le
Dockerfile: ajouter.envau.dockerignore, ne jamais copier de secret dans une image, et fournir la configuration au démarrage du conteneur ; les migrations se lancent au déploiement, pas à la construction.
Tu avais trouvé ?