Une image Docker, c’est un modèle figé qui contient tout le nécessaire pour exécuter une application : code, dépendances, configuration et système de fichiers. Chaque image est construite couche par couche, et comprendre cette architecture est la clé pour créer des images optimisées, légères et rapides à builder.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre le système de couches et comment chaque instruction Dockerfile crée une nouvelle layer
- Explorer la structure interne d’une image (manifest.json, blobs, config)
- Maîtriser le cache de build pour accélérer vos pipelines CI/CD
- Analyser une image existante pour comprendre sa composition
Prérequis : avoir lu l’introduction à la conteneurisation et connaître les bases de Docker.
Comment est construite une image de conteneur ?
Section intitulée « Comment est construite une image de conteneur ? »Pour bien comprendre ce qu’on fait quand on écrit un Dockerfile, il faut
savoir ce qui se passe sous le capot. Construire une image Docker, ce n’est
pas juste enchaîner des commandes. C’est empiler des couches, chacune
résultant d’une instruction, à partir d’une image de base.
Tout commence par une image de base
Section intitulée « Tout commence par une image de base »L’image de base, c’est le point de départ. Elle peut être :
- Une distribution minimaliste comme
alpine - Une image préconfigurée comme
python:3.11-slimounginx - Ou même
scratch, qui signifie… aucune base du tout
| Type d’image | Exemple | Taille typique | Shell inclus | Cas d’usage |
|---|---|---|---|---|
| Standard | python:3.12 | 900+ Mo | ✅ Oui | Dev, debug |
| Slim | python:3.12-slim | ~150 Mo | ✅ Oui | Production générale |
| Alpine | python:3.12-alpine | ~50 Mo | ✅ Oui (busybox) | Contraintes de taille |
| Distroless | gcr.io/distroless/python3 | ~20 Mo | ❌ Non | Sécurité maximale |
| Chainguard | cgr.dev/chainguard/python | ~30 Mo | ❌ Non | Sécurité + zero CVE |
On construit une nouvelle image à partir de cette base
Section intitulée « On construit une nouvelle image à partir de cette base »Pour construire une image, on va ajouter des couches à cette image de base.
Chaque couche est le résultat d’une instruction dans le Dockerfile. Ces
instructions sont exécutées dans l’ordre et chaque instruction crée une
nouvelle couche.
Qu’est-ce qu’un Dockerfile ?
Section intitulée « Qu’est-ce qu’un Dockerfile ? »Un Dockerfile est un fichier texte qui définit une suite d’instructions qui vont permettre d’installer tous les éléments nécessaires à l’exécution de notre application.
Nous avons à notre disposition une dizaine d’instructions. Si peu ? Oui et c’est suffisant. À cela s’ajoute aussi la possibilité de mettre des commentaires.
Prenons un exemple simple :
FROM debian:bullseye-slimCette instruction dit à Docker : « télécharge cette image de base depuis un registre » (par défaut Docker Hub). C’est la première couche, sur laquelle on va construire tout le reste.
Chaque instruction crée une nouvelle couche
Section intitulée « Chaque instruction crée une nouvelle couche »À chaque ligne du Dockerfile, Docker va :
- Créer un conteneur temporaire
- Exécuter l’instruction (par exemple une commande
RUN) - Capturer la différence de fichiers
- La stocker comme une nouvelle couche (en tar.gz dans les blobs)
Prenons ce Dockerfile :
FROM python:3.11-slimCOPY . /appRUN pip install -r /app/requirements.txtVoici ce que fait Docker :
- 1re couche : image de base
python:3.11-slim - 2e couche : copie de vos fichiers dans
/app - 3e couche : installation des dépendances Python
Chaque couche est immutabilité et cacheable. Si on relance le build sans changer les fichiers copiés, Docker réutilisera les couches précédentes.
Content-Addressable Storage : la déduplication intelligente
Section intitulée « Content-Addressable Storage : la déduplication intelligente »Docker utilise un stockage adressable par contenu (Content-Addressable Storage). Chaque couche est identifiée par le hash SHA256 de son contenu, pas par son nom ou sa position. Concrètement :
- Si deux images partagent la même couche de base (ex:
alpine:3.19), Docker ne la stocke qu’une seule fois sur le disque - Lors d’un
docker pull, seules les couches manquantes sont téléchargées - La déduplication fonctionne aussi bien localement que sur les registries
Cette approche permet d’économiser énormément d’espace disque et de bande passante, surtout quand vous gérez plusieurs images basées sur les mêmes fondations.
Union Filesystem et Copy-on-Write
Section intitulée « Union Filesystem et Copy-on-Write »Pour assembler les couches en un système de fichiers cohérent, Docker utilise un Union Filesystem (généralement OverlayFS via le driver overlay2). Ce système :
- Superpose les couches read-only les unes sur les autres
- Présente une vue unifiée au conteneur (comme un seul système de fichiers)
- Utilise le mécanisme Copy-on-Write (CoW) pour les modifications
Quand un conteneur modifie un fichier existant dans une couche read-only :
- Le fichier est copié dans la couche d’écriture du conteneur
- La modification s’applique sur cette copie
- La couche originale reste intacte
C’est pourquoi les modifications dans un conteneur ne persistent pas après sa suppression, sauf si vous utilisez des volumes.
Comment une image est-elle structurée ?
Section intitulée « Comment une image est-elle structurée ? »Pour aller plus loin, on peut extraire une image Docker pour voir comment
elle est structurée. Par exemple, prenons l’image nginx:alpine :
docker save nginx:alpine -o nginx-alpine.tarmkdir nginx-image && tar -xf nginx-alpine.tar -C nginx-imageUne fois extraite, on y trouve :
Répertoireblobs/
Répertoiresha256/
- 08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350
- 0bf5be05f1809d9c54b50ef92deba77c37c22d875842d556edd4ad202b61d033
- 1ff4bb4faebcfb1f7e01144fa9904a570ab9bab88694457855feb6c6bba3fa07
- 252b6db79fae151ab547c0f86a873dc97274d8b61f3921158d480b4242fef957
- 252e179fd8212a3dc5b070bb4d702df97f20b4b52aa65a59cf61c4d85e138b55
- 4b6e8e243d7d2a5c7af927e2137d9426d039591b0b24a368c6f9d5a3e80132e1
- 540b632878f7db4231da7c610f2b3271efba724392e56da1569c1d94900870b4
- 60531a653758d21ddd46bf4c65e7beec5f0e35332409fccf4500e5a02dae1c53
- 80a2d6b308417d56c551f88704e46ab844d0db3db9ad7f2b5e283da916312e65
- 84e2259b5616d28e968f0f5162447105736880570791aadcd4be9dabd23a2bbc
- 8f3c313eb1240a3b86e0c76d0abda7a6fa7df30ad3151e98c4e3725a3fb710dc
- 9af9e76ea07fe05a1f7660b80ec2417bc3fe500991df4995b0adfa13aade20b6
- bbf43b514ee66a048d0b4cd55e455bf91481f1c04252e4c1564cf9fc7b3f0213
- c1761f3c364a963ec0ebd4d728cb6dd5aa24273f7dba0c3dd2fdb8411682ef0a
- c18897d5e3dd125d3d9f2ca7f361cb6b05cf7fad8ef9bc00548f3eb6f3def644
- c9ce8cb4e76a801ef89c226cb8657556e62e3bb962b3641b051bb25f13dd1a26
- f1f70b13aacc43849d4f4ab87a889304a4300210ecd32be5a55305486af5f1ea
- fabf3656cbba722e0a4e35f33a5d2b67eb772a6608e467014787d0a637d7ea55
- index.json
- manifest.json
- oci-layout
- repositories
manifest.json: décrit les couches et la configuration.repositories: indique le nom de l’image et son tag.- Un ou plusieurs dossiers avec des noms hashés : ce sont les couches,
chacune contenant :
layer.tar: la vraie couche de fichiers- des métadonnées (
VERSION,json, etc.)
Dans le format OCI Image Layout, chaque élément de l’image est identifié par un digest SHA256. Les fichiers qui sont dans blobs/sha256/, sont des blobs binaires compressés représentant soit :
- des couches de fichiers (layer.tar.gz)
- des manifests JSON
- des configurations de conteneur
Chaque fichier est nommé par le digest SHA256 de son contenu, ce qui garantit l’intégrité et la déduplication.
On peut alors voir les fichiers ajoutés/modifiés à cette étape. En répétant ça pour chaque couche, on comprend exactement ce que chaque commande du Dockerfile a produit.
C’est aussi ce que fait automatiquement l’outil
dive : il
lit les couches d’une image et affiche ce qui a été ajouté ou modifié, couche
par couche.
Description du fichier manifest.json
Section intitulée « Description du fichier manifest.json »Ce fichier manifest.json est un élément clé d’une image Docker exportée au
format OCI. Il décrit la composition complète de l’image, notamment :
- le fichier de configuration (
Config) - les couches de fichiers (
Layers) - les informations sur chaque blob (
LayerSources) - les tags associés à l’image (
RepoTags)
Notre exemple de manifest.json :
[ { "Config": "blobs/sha256/1ff4bb4faebcfb1f7e01144fa9904a570ab9bab88694457855feb6c6bba3fa07", "RepoTags": [ "nginx:alpine" ], "Layers": [ "blobs/sha256/08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350", "blobs/sha256/c1761f3c364a963ec0ebd4d728cb6dd5aa24273f7dba0c3dd2fdb8411682ef0a", "blobs/sha256/8f3c313eb1240a3b86e0c76d0abda7a6fa7df30ad3151e98c4e3725a3fb710dc", "blobs/sha256/c9ce8cb4e76a801ef89c226cb8657556e62e3bb962b3641b051bb25f13dd1a26", "blobs/sha256/252b6db79fae151ab547c0f86a873dc97274d8b61f3921158d480b4242fef957", "blobs/sha256/f1f70b13aacc43849d4f4ab87a889304a4300210ecd32be5a55305486af5f1ea", "blobs/sha256/9af9e76ea07fe05a1f7660b80ec2417bc3fe500991df4995b0adfa13aade20b6", "blobs/sha256/c18897d5e3dd125d3d9f2ca7f361cb6b05cf7fad8ef9bc00548f3eb6f3def644" ], "LayerSources": { "sha256:08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350": { "mediaType": "application/vnd.oci.image.layer.v1.tar", "size": 8120832, "digest": "sha256:08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350" }, "sha256:252b6db79fae151ab547c0f86a873dc97274d8b61f3921158d480b4242fef957": { "mediaType": "application/vnd.oci.image.layer.v1.tar", "size": 2560, "digest": "sha256:252b6db79fae151ab547c0f86a873dc97274d8b61f3921158d480b4242fef957" }, "sha256:8f3c313eb1240a3b86e0c76d0abda7a6fa7df30ad3151e98c4e3725a3fb710dc": { "mediaType": "application/vnd.oci.image.layer.v1.tar", "size": 3584, "digest": "sha256:8f3c313eb1240a3b86e0c76d0abda7a6fa7df30ad3151e98c4e3725a3fb710dc" }, "sha256:9af9e76ea07fe05a1f7660b80ec2417bc3fe500991df4995b0adfa13aade20b6": { "mediaType": "application/vnd.oci.image.layer.v1.tar", "size": 7168, "digest": "sha256:9af9e76ea07fe05a1f7660b80ec2417bc3fe500991df4995b0adfa13aade20b6" }, "sha256:c1761f3c364a963ec0ebd4d728cb6dd5aa24273f7dba0c3dd2fdb8411682ef0a": { "mediaType": "application/vnd.oci.image.layer.v1.tar", "size": 4504064, "digest": "sha256:c1761f3c364a963ec0ebd4d728cb6dd5aa24273f7dba0c3dd2fdb8411682ef0a" }, "sha256:c18897d5e3dd125d3d9f2ca7f361cb6b05cf7fad8ef9bc00548f3eb6f3def644": { "mediaType": "application/vnd.oci.image.layer.v1.tar", "size": 36649472, "digest": "sha256:c18897d5e3dd125d3d9f2ca7f361cb6b05cf7fad8ef9bc00548f3eb6f3def644" }, "sha256:c9ce8cb4e76a801ef89c226cb8657556e62e3bb962b3641b051bb25f13dd1a26": { "mediaType": "application/vnd.oci.image.layer.v1.tar", "size": 4608, "digest": "sha256:c9ce8cb4e76a801ef89c226cb8657556e62e3bb962b3641b051bb25f13dd1a26" }, "sha256:f1f70b13aacc43849d4f4ab87a889304a4300210ecd32be5a55305486af5f1ea": { "mediaType": "application/vnd.oci.image.layer.v1.tar", "size": 5120, "digest": "sha256:f1f70b13aacc43849d4f4ab87a889304a4300210ecd32be5a55305486af5f1ea" } } }]```
Voici une explication détaillée de chacune des parties de ce fichier `manifest.json` :
### `"Config"` : métadonnées de l’image
```json"Config": "blobs/sha256/1ff4bb4faebcfb1f7e01144fa9904a570ab9bab88694457855feb6c6bba3fa07"Ce fichier (un blob JSON) contient toutes les instructions du Dockerfile
compilées : la commande par défaut (CMD), les variables d’environnement, les
volumes, les labels, etc. C’est le cerveau du conteneur.
"RepoTags" : nom et version de l’image
Section intitulée « "RepoTags" : nom et version de l’image »"RepoTags": ["nginx:alpine"]Cela indique que l’image correspond au tag nginx:alpine. C’est le nom que vous
verriez avec docker images.
"Layers" : couches empilées de l’image
Section intitulée « "Layers" : couches empilées de l’image »"Layers": [ "blobs/sha256/08000c18d16da...", "blobs/sha256/c1761f3c364a...", ...]Chaque fichier listé ici correspond à une couche de fichiers
(layer.tar) ajoutée lors du docker build. Elles sont appliquées dans
l’ordre, de la base vers le haut. Le contenu de ces archives représente les
ajouts ou suppressions de fichiers à chaque étape du build.
"LayerSources" : métadonnées des couches
Section intitulée « "LayerSources" : métadonnées des couches »Cette section associe à chaque digest :
- le type MIME du fichier (
mediaType) - la taille en octets
- le digest SHA256, qui sert d’identifiant unique
Exemple :
"sha256:c18897d5e3dd125...": { "mediaType": "application/vnd.oci.image.layer.v1.tar", "size": 36649472, "digest": "sha256:c18897d5e3dd125..."}Cela indique que la couche contient une archive .tar, non compressée ici, de
36 Mo environ.
En résumé
Section intitulée « En résumé »Cette image est donc composée de :
- 8 couches, empilées dans un ordre précis
- Un fichier de configuration global
- Un tag
nginx:alpineassocié - Des métadonnées bien définies pour chaque élément
Et techniquement, Docker ou un autre moteur (comme containerd) reconstruit le
système de fichiers final en appliquant ces couches les unes après les autres,
sur la base des instructions du Config.
À retenir
Section intitulée « À retenir »Testez vos connaissances
Section intitulée « Testez vos connaissances »Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications