Formation Docker

Images, conteneurs et volumes : le modèle mental en 30 minutes

Image, conteneur, volume et réseau : les quatre objets de Docker, manipulés avec une vraie base PostgreSQL dont les données survivent au conteneur.

Module 0 · Démarrer vite et sereinement

Leçon gratuite

Objectif du chapitre

Vous savez expliquer et manipuler les quatre objets de Docker : image, conteneur, volume et réseau. Vous lancez PostgreSQL dans un conteneur, le supprimez et retrouvez vos données. Durée : 30 min · 80 % pratique.

Prérequis : Installer Docker et vérifier son poste (chapitre 01) : tag dk01 posé.

IMAGE (lecture seule)node:22-alpinenpm ciCOPY . .npm run buildCONTENEURS (jetables)couche d’écrituremontour-1= image + couchecouche d’écrituremontour-2= image + coucheVolumepgdatadocker rm efface la couche d’écriture, jamais l’image ni le volume
Schéma Image, conteneur, volume

Comment lire le schéma

  • À gauche, l’image : une pile de couches en lecture seule (jaune). Chaque instruction du Dockerfile en ajoute une.
  • Au centre, deux conteneurs lancés depuis la même image. Chacun reçoit sa propre couche d’écriture (vert) : ce qu’il modifie reste chez lui et disparaît avec lui.
  • À droite, le volume (violet) vit en dehors des conteneurs. C’est là que vont les données à garder (base de données, fichiers envoyés).

Le modèle mental en quatre phrases

  1. Une image est un modèle en lecture seule, fait de couches empilées. Elle se construit avec un Dockerfile et se partage via un registre.
  2. Un conteneur est une image en cours d’exécution, plus une fine couche d’écriture. Il est jetable : le supprimer efface tout ce qui a été écrit dedans.
  3. Un volume est un espace de stockage géré par Docker, monté dans un conteneur. Il survit à la suppression du conteneur.
  4. Un réseau relie des conteneurs entre eux : sur un réseau commun, ils se joignent par leur nom.

C’est la réponse au défi du chapitre 01 : votre fichier était dans la couche d’écriture du conteneur, supprimée avec lui.

Étape 1 : explorer une image

docker pull node:22-alpine
docker image ls node
docker image history node:22-alpine   # les couches et leur taille
docker image inspect node:22-alpine --format '{{.Architecture}} {{.Config.User}}'

Notez la taille : environ 150 Mo pour Node sur Alpine, contre plus de 1 Go pour node:22. Cette différence pèsera sur chaque build et chaque déploiement de MonTour.

Les couches : ce qu’est vraiment une image

Une image n’est pas un gros fichier. C’est une pile de couches en lecture seule, chacune contenant uniquement les fichiers ajoutés, modifiés ou supprimés par une instruction du Dockerfile. Quand un conteneur démarre, Docker pose par-dessus une couche d’écriture vide, et Linux (pilote overlay2) présente l’ensemble comme un seul disque.

Une image node vue de l’intérieur (overlay2)FROM node:22-alpine≈ 45 MoWORKDIR /app0 oCOPY package*.json ./≈ 300 KoRUN npm ci≈ 250 MoCOPY . .≈ 2 Mocouche d’écriture du conteneurlogs, /tmp…lowerdir = couches de l’image · upperdir = couche d’écriture · merged = vue du conteneurChanger un fichier source n’invalide que COPY . . : npm ci reste en cache
Schéma Les couches et leur taille

Comment lire le schéma

  • Lisez de bas en haut : chaque instruction ajoute une couche par-dessus la précédente. La taille indique ce que la couche ajoute.
  • La couche rouge (npm ci) est la plus lourde : on veut la reconstruire le moins souvent possible.
  • En vert, la couche d’écriture du conteneur, ajoutée au lancement. Linux superpose le tout (overlay2) : le conteneur voit un seul système de fichiers.
  • C’est pourquoi on copie package*.json avant le reste du code : modifier une page ne relance pas npm ci.
  • Partage : dix images basées sur node:22-alpine ne stockent cette base qu’une fois. docker system df -v montre l’espace partagé.
  • Copie à l’écriture : si un conteneur modifie un fichier d’une couche de l’image, ce fichier est d’abord copié dans sa couche d’écriture. L’image ne change jamais.
  • Supprimer ne libère rien : un RUN rm dans une couche ultérieure cache le fichier, mais il reste dans la couche où il a été ajouté, donc dans l’image (et il fuit si c’était un secret).
  • Cache de build : Docker réutilise une couche tant que l’instruction et ses fichiers d’entrée n’ont pas changé. Dès qu’une couche change, toutes les suivantes sont reconstruites.

Lab : voir les couches de vos propres yeux

docker pull node:22-alpine
docker history node:22-alpine                          # une ligne par couche, avec sa taille
docker image inspect node:22-alpine --format '{{json .RootFS.Layers}}' | tr ',' '\n' | wc -l
docker image inspect node:22-alpine --format '{{json .GraphDriver.Data}}'    # LowerDir, UpperDir, MergedDir

# Copie à l'écriture : ce qu'un conteneur change
docker run -d --name essai node:22-alpine sleep 600
docker exec essai sh -c 'echo test > /tmp/a.txt && rm /usr/local/bin/npx'
docker diff essai                                      # A = ajouté, C = changé, D = supprimé
docker rm -f essai

# Espace partagé entre images
docker system df -v | head -20
Ce que vous observezPourquoi
docker history montre des couches de 0 o (ENV, WORKDIR, CMD)Ces instructions ne changent que les métadonnées, pas les fichiers
docker diff affiche A /tmp/a.txt et D /usr/local/bin/npxLes changements sont dans la couche d’écriture du conteneur, l’image est intacte
Dans un nouveau conteneur, npx est de nouveau làLa couche d’écriture a disparu avec le conteneur supprimé
La colonne SHARED SIZE de docker system df -v n’est pas nulleLes images de même base partagent leurs couches sur le disque

Pour aller plus loin

L’outil dive (docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock wagoodman/dive node:22-alpine) explore une image couche par couche et repère l’espace gaspillé. Les chapitres « Build multi-étapes » et « Cache de build » s’appuient directement sur ce modèle.

Étape 2 : le cycle de vie d’un conteneur

docker run -d --name essai node:22-alpine sleep 600
docker ps                       # il tourne
docker exec -it essai sh        # entrer dedans, puis : node -v ; exit
docker stop essai && docker ps -a   # arrêté, mais toujours là
docker rm essai                 # supprimé, couche d'écriture comprise

Pratique : PostgreSQL et un volume

Lancez une base avec un volume nommé, créez une table, supprimez le conteneur, relancez-en un nouveau sur le même volume : la table est toujours là.

docker volume create pgdata
docker run -d --name pg -e POSTGRES_PASSWORD=dev -v pgdata:/var/lib/postgresql/data postgres:17-alpine
docker exec -it pg psql -U postgres -c "create table tickets(id serial primary key, numero int);"
docker rm -f pg
docker run -d --name pg -e POSTGRES_PASSWORD=dev -v pgdata:/var/lib/postgresql/data postgres:17-alpine
docker exec -it pg psql -U postgres -c "\dt"   # la table tickets est là

Étape 3 : relier deux conteneurs par un réseau

docker network create montour_net
docker network connect montour_net pg
docker run --rm -it --network montour_net postgres:17-alpine \
  psql -h pg -U postgres -c "select 1"   # "pg" est résolu par son nom

Aucun port n’a été publié sur votre machine : la base n’est joignable que depuis le réseau montour_net. C’est exactement ce que vous ferez en production.

Réflexe sécurité

-p 5432:5432 publie la base sur toutes les interfaces de la machine, y compris le Wi-Fi d’un café ou l’IP publique d’un VPS. Docker contourne même certains pare-feu comme ufw. Publiez un port uniquement quand c’est nécessaire, et limitez-le à 127.0.0.1:5432:5432.

Étape 4 : faire le ménage

docker rm -f pg
docker network rm montour_net
docker volume rm pgdata
docker system df       # ce que Docker occupe sur le disque
docker system prune    # supprime ce qui est arrêté et inutilisé (lisez la question)

Le prompt reproductible

Même point de départ, même contexte, même résultat : partez du tag indiqué et collez ce prompt tel quel.

Point de départ : tag dk01 du dépôt MonTour.
Rôle : tu es un formateur Docker. Tu poses des questions, tu ne donnes
pas les réponses.
Tâche : pose-moi 5 questions courtes pour vérifier que je distingue image,
conteneur, volume et réseau, avec un exemple tiré de MonTour (base PostgreSQL,
application Next.js). Attends ma réponse à chaque question avant la suivante,
puis corrige en une ligne.
Contraintes : n'exécute aucune commande.

Qualité et sécurité : ce que vous vérifiez

  • Les données ne vivent jamais dans un conteneur : toujours un volume nommé pour une base.
  • Des tags précis : postgres:17-alpine, pas postgres:latest, pour que la même commande donne le même résultat dans six mois.
  • Le minimum de ports publiés, et sur 127.0.0.1 quand c’est local.
  • Sans IA : expliquez à voix haute ce que fait chaque option de la commande docker run de la base.

Outillage IA de ce chapitre

Connecteurs MCP : aucun.

Agents : aucun. L’assistant joue ici le rôle de formateur : il interroge, vous répondez.

Vérifier

  • ☐ Vous expliquez la différence entre image et conteneur en une phrase.
  • ☐ La table tickets a survécu à la suppression du conteneur grâce au volume.
  • ☐ Deux conteneurs se sont joints par leur nom sur montour_net.
  • ☐ docker ps -a et docker volume ls sont propres après le ménage.
  • ☐ Commit et tag dk02 posés (vos commandes dans docs/docker-memo.md).

Défi

Lancez deux conteneurs PostgreSQL sur le même volume en même temps. Que se passe-t-il, et pourquoi est-ce une très mauvaise idée en production ?

Pour aller plus loin

Suivez votre progression. Créez un compte gratuit pour cocher vos leçons et reprendre là où vous en étiez.

Créer un compte gratuit