Aller au contenu
Conteneurs & Orchestration medium

Comprendre les images Docker : structure et fonctionnement

12 min de lecture

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.

  • 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.

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. Schéma du système de couches Docker : chaque instruction Dockerfile crée une nouvelle layer empilée

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-slim ou nginx
  • Ou même scratch, qui signifie… aucune base du tout
Type d’imageExempleTaille typiqueShell inclusCas d’usage
Standardpython:3.12900+ Mo✅ OuiDev, debug
Slimpython:3.12-slim~150 Mo✅ OuiProduction générale
Alpinepython:3.12-alpine~50 Mo✅ Oui (busybox)Contraintes de taille
Distrolessgcr.io/distroless/python3~20 Mo❌ NonSécurité maximale
Chainguardcgr.dev/chainguard/python~30 Mo❌ NonSé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.

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-slim

Cette 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 ligne du Dockerfile, Docker va :

  1. Créer un conteneur temporaire
  2. Exécuter l’instruction (par exemple une commande RUN)
  3. Capturer la différence de fichiers
  4. La stocker comme une nouvelle couche (en tar.gz dans les blobs)

Prenons ce Dockerfile :

FROM python:3.11-slim
COPY . /app
RUN pip install -r /app/requirements.txt

Voici 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.

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 :

  1. Le fichier est copié dans la couche d’écriture du conteneur
  2. La modification s’applique sur cette copie
  3. 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.

Pour aller plus loin, on peut extraire une image Docker pour voir comment elle est structurée. Par exemple, prenons l’image nginx:alpine :

Fenêtre de terminal
docker save nginx:alpine -o nginx-alpine.tar
mkdir nginx-image && tar -xf nginx-alpine.tar -C nginx-image

Une 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.

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": ["nginx:alpine"]

Cela indique que l’image correspond au tag nginx:alpine. C’est le nom que vous verriez avec docker images.

"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.

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.

Cette image est donc composée de :

  • 8 couches, empilées dans un ordre précis
  • Un fichier de configuration global
  • Un tag nginx:alpine associé
  • 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.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

10 questions
15 min.
70% requis

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

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.