Docker : de la première image à la production
Quitter

Leçon 1 sur 15 · Fondamentaux

Pourquoi les conteneurs ?

30 min Debian 12

Objectif

Comprendre le problème que Docker résout, ce qu'est réellement un conteneur, et maîtriser le vocabulaire de base : image, conteneur, registre, Docker Engine et OCI. Cette leçon ne demande aucune installation.

L'essentiel

  • Un conteneur est un processus Linux isolé : il a sa propre vue des fichiers, du réseau et des autres processus, mais il utilise le noyau de la machine hôte.
  • Une image est un modèle figé et en lecture seule (le programme, ses bibliothèques, sa configuration). On lance autant de conteneurs qu'on veut à partir d'une même image.
  • Un registre stocke et distribue les images (Docker Hub, GitHub Container Registry, un registre privé).
  • Docker Engine est le moteur : un démon (dockerd) qui construit les images et fait tourner les conteneurs, piloté par la commande docker.
  • La norme OCI (Open Container Initiative) décrit le format des images et la façon de les exécuter : une image construite avec Docker tourne aussi avec Podman, containerd ou Kubernetes.

Le problème : « ça marche sur ma machine »

Imaginez une boutique en ligne à Casablanca. Le développeur a écrit son site avec Node.js 22 sur son portable Ubuntu. Le serveur de production, lui, tourne sous Debian avec Node.js 18. Le jour de la mise en ligne, une bibliothèque refuse de démarrer : la version de Node ne correspond pas, un paquet système manque, une variable d'environnement a été oubliée.

Ce problème a trois causes classiques :

  1. Les dépendances diffèrent d'une machine à l'autre (versions du langage, bibliothèques système).
  2. La configuration n'est pas écrite noir sur blanc : elle est dans la tête de quelqu'un.
  3. Plusieurs applications sur le même serveur se gênent (l'une veut PHP 8.1, l'autre PHP 8.3).

Un conteneur règle ces trois points : l'application voyage avec tout ce dont elle a besoin, la recette est écrite dans un fichier (le Dockerfile), et chaque application a son propre espace, isolé des autres.

Machine virtuelle contre conteneur

Une machine virtuelle (VM) simule un ordinateur complet : processeur virtuel, disque virtuel, et surtout son propre système d'exploitation avec son propre noyau. Un hyperviseur (VMware, KVM, Hyper-V, VirtualBox) partage le matériel entre les VM.

Un conteneur ne simule rien. C'est un processus normal du système hôte, que le noyau Linux enferme grâce à deux mécanismes :

  • les espaces de noms (namespaces) : le processus a sa propre liste de processus (pid), son propre réseau (net), ses propres points de montage (mnt), son propre nom d'hôte (uts), ses propres utilisateurs (user)… ;
  • les groupes de contrôle (cgroups) : le noyau limite la mémoire, le processeur et les entrées-sorties que ce processus peut consommer.
Critère Machine virtuelle Conteneur
Ce qui est isolé une machine entière un processus (et ses enfants)
Noyau un noyau par VM le noyau de l'hôte, partagé
Taille typique plusieurs Go quelques Mo à quelques centaines de Mo
Démarrage dizaines de secondes une fraction de seconde
Systèmes possibles n'importe lequel (Windows dans Linux…) des programmes Linux sur un noyau Linux
Densité quelques VM par serveur des dizaines ou centaines de conteneurs
Isolation très forte (matériel virtuel) bonne, mais dépend du noyau partagé

Les deux ne s'opposent pas. Dans la vraie vie, on loue très souvent une VM (un VPS chez un hébergeur) et on y fait tourner des conteneurs.

Sur Windows et macOS, il n'y a pas de noyau Linux. Docker Desktop démarre donc une petite VM Linux en arrière-plan, et vos conteneurs tournent dedans.

Vous pouvez voir les espaces de noms de n'importe quel processus Linux, même sans Docker :

ls /proc/self/ns          # espaces de noms du shell courant
# cgroup ipc mnt net pid pid_for_children time time_for_children user uts
cat /proc/self/cgroup     # le groupe de contrôle auquel il appartient
uname -r                  # le noyau : le MÊME pour l'hôte et tous ses conteneurs

Image et conteneur

La meilleure image mentale est celle de la classe et de l'objet en programmation, ou du moule et du gâteau :

  • l'image est le moule : immuable, versionnée, partageable ;
  • le conteneur est un gâteau sorti du moule : une instance vivante, qui démarre, travaille, s'arrête et peut être supprimée.

Une image est faite de couches empilées. Chaque couche ajoute, modifie ou supprime des fichiers par rapport à la précédente. Quand un conteneur démarre, Docker pose au-dessus une couche fine en écriture, propre à ce conteneur. Conséquence capitale :

Tout ce qu'un conteneur écrit dans sa couche disparaît quand on le supprime. Les données à garder (base de données, fichiers envoyés par les clients) vont dans un volume. Vous le verrez en détail au niveau 2.

Deux principes guident l'usage des images :

  1. Une image ne se modifie pas. Pour changer quelque chose, on construit une nouvelle image (nouvelle version), on supprime l'ancien conteneur et on en lance un neuf.
  2. Un conteneur, un rôle. Le site web dans un conteneur, la base PostgreSQL dans un autre, le cache Redis dans un troisième. On les assemble ensuite avec Docker Compose.

Les registres et le nom d'une image

Un registre est un serveur qui stocke des images, un peu comme un dépôt de paquets apt ou dnf. Docker Hub est le registre public par défaut. Il en existe beaucoup d'autres : GitHub Container Registry (ghcr.io), les registres des clouds (Amazon ECR, Azure ACR, Google), GitLab, Harbor… et vous pouvez héberger le vôtre.

Le nom complet d'une image a trois parties :

ghcr.io / startup-rabat/api : 2.3.1
└──┬──┘   └──────┬────────┘   └─┬─┘
registre       dépôt         étiquette (tag)

Quand une partie manque, Docker la complète :

Vous écrivez Docker comprend
nginx docker.io/library/nginx:latest
nginx:1.27-alpine docker.io/library/nginx:1.27-alpine
ecole/inscriptions:2.0 docker.io/ecole/inscriptions:2.0
ghcr.io/startup-rabat/api:2.3.1 (nom déjà complet)
  • Sans registre : docker.io (Docker Hub).
  • Sans espace de nom : library/, le dossier des images officielles (nginx, postgres, redis, node, debian…).
  • Sans étiquette : latest. Ce mot ne veut pas dire « la plus récente » : c'est juste l'étiquette par défaut. En production, écrivez toujours une étiquette précise.

Docker Engine : qui fait quoi ?

Quand vous tapez docker run nginx, plusieurs programmes travaillent ensemble :

vous ──> docker (client, la commande)
            │  API REST sur /var/run/docker.sock
            ▼
         dockerd (le démon Docker Engine)
            │  images, réseaux, volumes, construction (BuildKit)
            ▼
         containerd ──> runc ──> le processus isolé (votre conteneur)
  • docker est le client : il n'exécute rien lui-même, il envoie des ordres à l'API du démon. Il peut même parler à un démon distant.
  • dockerd est le démon : il gère les images, les réseaux, les volumes et la construction.
  • containerd gère le cycle de vie des conteneurs, et runc crée concrètement le processus isolé (espaces de noms, cgroups).

Le démon tourne en root. Celui qui peut parler à son socket peut donc tout faire sur la machine : vous verrez au niveau 4 ce que cela implique pour la sécurité.

OCI : une norme ouverte

En 2015, Docker et d'autres acteurs ont créé l'Open Container Initiative. Elle publie des spécifications ouvertes :

  • image-spec : le format d'une image (couches, manifeste, configuration) ;
  • runtime-spec : comment exécuter un conteneur à partir d'une image décompressée ;
  • distribution-spec : comment un registre reçoit et distribue des images.

Grâce à elle, vous n'êtes pas prisonnier d'un outil. Une image construite par Docker sur le portable d'un développeur peut être lancée par Podman sur un serveur Red Hat, par containerd dans Kubernetes, ou stockée dans n'importe quel registre compatible.

Pourquoi Docker est devenu incontournable

Besoin Ce que Docker apporte
Installer une application en 5 minutes docker run d'une image officielle, rien à compiler
Reproduire un bug de production lancer exactement la même image en local
Déployer souvent et sans risque nouvelle image = nouvelle version ; retour arrière = relancer l'ancienne
Faire cohabiter des versions PHP 8.1 et PHP 8.3 dans deux conteneurs, sur le même serveur
Intégration continue (CI) chaque test démarre dans un environnement propre et identique
Préparer Kubernetes Kubernetes orchestre… des conteneurs construits comme ici

Exemple concret : l'application d'inscription d'une école à Fès se compose d'un site web, d'une API et d'une base PostgreSQL. Avec Docker, un nouveau développeur clone le projet, tape une seule commande (docker compose up), et dispose en une minute de la même pile que la production.

Un peu d'histoire

Les conteneurs ne sont pas nés avec Docker. Ils sont le fruit de vingt ans d'ajouts au monde Unix et au noyau Linux :

Année Étape
1979 chroot sous Unix : un processus voit un autre dossier comme racine /
2000 les jails de FreeBSD : isolation des processus et du réseau
2004 les zones de Solaris
2002-2013 les espaces de noms arrivent un à un dans le noyau Linux ; les cgroups (proposés par Google) y entrent en 2008
2008 LXC : premiers conteneurs Linux complets, mais difficiles à utiliser
2013 Docker : une commande simple, des images en couches, un registre public
2015 création de l'OCI ; Kubernetes 1.0
2017-2019 Docker confie containerd à la fondation CNCF ; arrivée de Podman (Red Hat)
aujourd'hui Docker Engine, Podman, containerd et Kubernetes partagent le même format d'image

La vraie révolution de Docker n'a pas été technique : elle a été d'emballer ces mécanismes dans un outil simple, avec un format d'image portable et un registre où tout le monde partage ses images.

Docker, Podman, containerd : qui utiliser ?

Vous croiserez plusieurs outils. Ils parlent tous le même langage d'images :

Outil Ce que c'est Où on le rencontre
Docker Engine démon + client docker, Compose, BuildKit serveurs Debian/Ubuntu, VPS, postes de développeurs
Docker Desktop application graphique avec une VM Linux intégrée postes Windows et macOS
Podman même commandes que Docker, sans démon, rootless natif RHEL, Rocky, AlmaLinux, Fedora
containerd moteur bas niveau, sans interface conviviale sous Docker, et dans la plupart des clusters Kubernetes

Ce cours utilise Docker Engine et Docker Compose, les plus répandus chez les hébergeurs et dans les offres d'emploi. Tout ce que vous apprenez sur les images et les Dockerfile vaut aussi pour Podman.

Le vocabulaire en un coup d'œil

Mot Définition courte
Image modèle en lecture seule, fait de couches, identifié par un nom et une étiquette
Conteneur instance en marche d'une image, avec une couche en écriture jetable
Couche (layer) ensemble de modifications de fichiers, partagé entre images
Registre serveur qui stocke et distribue des images
Dépôt (repository) ensemble des versions d'une même image dans un registre
Étiquette (tag) nom d'une version : 1.27, 1.27-alpine, latest
Dockerfile recette texte qui décrit comment construire une image
Volume espace de stockage géré par Docker, qui survit aux conteneurs
Compose outil qui décrit et lance plusieurs conteneurs avec un fichier YAML

Pièges fréquents

  • Croire qu'un conteneur est une petite VM : il n'a pas son propre noyau. Un module noyau ou un réglage sysctl global se fait sur l'hôte.
  • Stocker des données importantes dans le conteneur : elles disparaissent à la suppression. Utilisez un volume.
  • Utiliser latest en production : vous ne savez plus quelle version tourne, et un simple redémarrage peut changer de version.
  • Prendre n'importe quelle image sur Docker Hub : préférez les images officielles ou d'éditeurs vérifiés, comme vous choisiriez un dépôt de paquets de confiance.

Lab

Docker n'est pas installé dans le lab : ce n'est pas nécessaire ici. Vous allez :

  1. lister les espaces de noms d'un processus, la brique de base d'un conteneur ;
  2. relever la version du noyau, celui que partageraient tous les conteneurs de la machine ;
  3. écrire le nom complet d'une image officielle ;
  4. décomposer le nom d'une image hébergée sur GitHub Container Registry.

Quiz

  1. Quelle est la différence fondamentale entre une machine virtuelle et un conteneur ?
  2. Quels sont les deux mécanismes du noyau Linux qui isolent et limitent un conteneur ?
  3. Que devient un fichier écrit par un conteneur dans sa propre couche quand on supprime ce conteneur ?
  4. Quel nom complet Docker utilise-t-il pour redis ?
  5. À quoi sert la norme OCI pour une entreprise ?
Réponses
  1. La VM embarque son propre système et son propre noyau sur du matériel virtuel ; le conteneur est un processus de l'hôte, isolé, qui partage le noyau de l'hôte.
  2. Les espaces de noms (namespaces) pour l'isolation, et les groupes de contrôle (cgroups) pour limiter les ressources.
  3. Il est perdu. Les données à conserver doivent aller dans un volume ou un dossier monté.
  4. docker.io/library/redis:latest.
  5. Elle garantit qu'une image n'est pas liée à un outil : construite avec Docker, elle peut tourner avec Podman, containerd ou Kubernetes, et être stockée dans tout registre compatible.

Sources de référence : documentation officielle Docker (docs.docker.com, licence Apache 2.0). Texte original MasterLinux.

Exercices pratiques

À réaliser sur Debian 12

Le terminal en ligne ouvre bientôt. En attendant, réalisez ces tâches sur votre machine Linux, une machine virtuelle ou WSL.

  1. Un conteneur n'est qu'un processus isolé par des « espaces de noms » du noyau. Créez le dossier /srv/decouverte et enregistrez dans /srv/decouverte/espaces.txt la liste des espaces de noms de votre shell (le contenu du dossier /proc/self/ns, un nom par ligne)
    Afficher un indicemkdir -p /srv/decouverte puis ls /proc/self/ns > /srv/decouverte/espaces.txt
    Voir la solution
    mkdir -p /srv/decouverte && ls /proc/self/ns > /srv/decouverte/espaces.txt

    Vérification : f=/srv/decouverte/espaces.txt; grep -qx pid $f && grep -qx net $f && grep -qx mnt $f && grep -qx uts $f

  2. Tous les conteneurs d'une machine partagent le même noyau. Écrivez la version du noyau qui fait tourner ce lab (une seule ligne) dans /srv/decouverte/noyau.txt
    Afficher un indiceLa commande uname -r affiche la version du noyau ; redirigez-la avec >
    Voir la solution
    mkdir -p /srv/decouverte && uname -r > /srv/decouverte/noyau.txt

    Vérification : test -s /srv/decouverte/noyau.txt && test "$(cat /srv/decouverte/noyau.txt)" = "$(uname -r)"

  3. Un développeur de la boutique en ligne de Casablanca a écrit « nginx:1.27-alpine ». Écrivez dans /srv/decouverte/nom-complet.txt le nom COMPLET de cette image, tel que Docker le comprend (registre/dépôt:étiquette), sur une seule ligne
    Afficher un indiceSans registre, Docker utilise docker.io ; une image officielle vit dans le dépôt library/
    Voir la solution
    echo 'docker.io/library/nginx:1.27-alpine' > /srv/decouverte/nom-complet.txt

    Vérification : grep -qx 'docker.io/library/nginx:1.27-alpine' /srv/decouverte/nom-complet.txt

  4. La startup de Rabat publie son API sous ghcr.io/startup-rabat/api:2.3.1. Décomposez ce nom dans /srv/decouverte/reference.txt, sur trois lignes exactement : registre=..., depot=..., etiquette=...
    Afficher un indiceLe registre est avant le premier /, l'étiquette après le dernier : ; le reste est le dépôt. Utilisez printf '%s\n' ligne1 ligne2 ligne3
    Voir la solution
    printf '%s\n' 'registre=ghcr.io' 'depot=startup-rabat/api' 'etiquette=2.3.1' > /srv/decouverte/reference.txt

    Vérification : f=/srv/decouverte/reference.txt; test "$(wc -l < $f)" -eq 3 && grep -qx 'registre=ghcr.io' $f && grep -qx 'depot=startup-rabat/api' $f && grep -qx 'etiquette=2.3.1' $f

Références officielles consultées : What is a container? — https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/ ; What is an image? — https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-an-image/ ; What is a registry? — https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-registry/ ; What is Docker? — https://docs.docker.com/get-started/docker-overview/ ; Docker Engine — https://docs.docker.com/engine/. Texte original MasterLinux.