Ce chapitre est gratuit
Pas besoin de compte pour le lire. Decouvre le contenu et decide si le programme est fait pour toi.
Voir les autres chapitres“Ça marche sur ma machine” — Le problème que Docker résout
T’as déjà entendu cette phrase ? Si t’as bossé en équipe sur un projet, forcément. Un développeur code une application, ça marche nickel chez lui. Il envoie le code à un collègue, ou il le déploie sur un serveur, et… ça plante.
Pourquoi ? Parce que sa machine a Python 3.11 et le serveur a Python 3.9. Parce qu’il a installé une bibliothèque manuellement qu’il a oubliée. Parce que son système est macOS et le serveur est Linux. Les environnements sont différents, donc rien ne se passe comme prévu.
Ce problème porte un nom dans l’industrie : le “works on my machine” syndrome. Et il coûte cher — en temps, en bugs, en nuits blanches avant un déploiement.
Docker est né en 2013 précisément pour résoudre ce problème. L’idée est simple mais puissante : empaqueter une application avec TOUT son environnement dans une boîte portable qui fonctionne de manière identique partout.
L’analogie de l’appartement meublé
Imagine que tu déménages. Tu as deux options :
-
Option traditionnelle (sans Docker) : tu arrives dans un appartement vide. Tu dois acheter les meubles, monter la cuisine, installer les rideaux, brancher les appareils. Si l’appart est plus petit que prévu, tes meubles ne rentrent pas. Si la prise électrique est différente, ton frigo ne marche pas. Galère.
-
Option Docker : tu arrives avec un conteneur maritime qui contient TON appartement complet — meubles, cuisine, électroménager, déco. Tu poses le conteneur, tu ouvres la porte, et tout est exactement comme chez toi. Peu importe que tu sois à Paris, Tokyo ou New York.
Un conteneur Docker, c’est exactement ça : ton application, avec TOUTES ses dépendances, empaquetée dans une boîte portable qui fonctionne de la même façon partout.
🧠 À retenir — Un conteneur Docker n’est pas juste un outil de déploiement. C’est un contrat de reproductibilité : ce qui tourne dans le conteneur tournera de la même façon, quel que soit l’hôte.
Pourquoi c’est révolutionnaire ?
Avant Docker (2013), déployer une application ressemblait à ça :
- Installer le bon OS sur le serveur
- Installer le bon langage (bonne version !)
- Installer toutes les dépendances une par une
- Configurer le réseau, les ports, les droits…
- Écrire des scripts d’installation fragiles
- Prier pour que ça marche 🙏
- Recommencer quand ça casse au prochain déploiement
Avec Docker :
- Tu décris tout dans un fichier (
Dockerfile) - Tu construis une image
- Tu lances un conteneur
- Ça marche. Point.
Et si tu dois déployer sur 10 serveurs ? Même image, même résultat, 10 fois. Pas de surprise.
🔥 Cas réel en entreprise — Chez Spotify, avant Docker, le déploiement d’un nouveau service prenait plusieurs jours de configuration manuelle. Après l’adoption de Docker et Kubernetes, un nouveau microservice peut être déployé en production en moins d’une heure. Netflix, Google, Airbnb — toutes les grandes entreprises tech utilisent des conteneurs aujourd’hui.
Docker au quotidien
Voici des situations concrètes où Docker change la donne au quotidien :
-
Développement local — “Installe PostgreSQL, Redis et Elasticsearch pour tester.” Sans Docker : 2 heures d’installation et de configuration. Avec Docker :
docker compose upet c’est prêt en 30 secondes. -
CI/CD — Tes tests tournent dans un conteneur propre à chaque build. Zéro pollution entre les exécutions. Si le test passe dans le conteneur, il passera en prod.
-
Production — Ton app tourne dans le même conteneur en dev, en staging et en prod. Fini les “ça marchait en staging”. L’image est la même, le comportement est le même.
-
Onboarding — Un nouveau développeur arrive dans l’équipe ? Il clone le repo, lance
docker compose up, et il a tout l’environnement de développement en 2 minutes. Pas besoin de suivre un README de 3 pages avec 47 étapes. -
Expérimentation — Tu veux tester une nouvelle base de données ? Un nouveau framework ? Lance un conteneur, teste, supprime. Rien n’est installé sur ta machine.
💡 Tip DevOps — Docker n’est pas réservé à la production. C’est un outil de développement avant tout. Si tu ne l’utilises que pour déployer, tu passes à côté de 80% de sa valeur.
Prérequis
Avant de plonger dans le vif du sujet, assure-toi d’avoir ces bases :
- Connaître les bases de Linux — naviguer dans un terminal, commandes de base (
ls,cd,cat,grep) - Comprendre ce qu’est un processus — un programme en cours d’exécution sur ta machine
- Comprendre ce qu’est un système de fichiers — l’arborescence de répertoires et fichiers
- Docker installé sur ta machine — on vérifie ça ensemble plus bas dans ce chapitre
⚠️ Attention — Si tu n’es pas à l’aise avec le terminal Linux, je te recommande fortement de consolider ces bases avant de continuer. Docker est un outil en ligne de commande — tu vas vivre dans le terminal.
Ce que tu vas apprendre
À la fin de ce chapitre, tu sauras :
- 🐳 Ce qu’est un conteneur et en quoi il diffère fondamentalement d’une machine virtuelle
- 🏗️ Comment Docker est architecturé (daemon, CLI, images, conteneurs, registries)
- 🖼️ Ce qu’est une image Docker, comment elle est construite en couches, et d’où elle vient
- ▶️ Lancer, arrêter et gérer tes premiers conteneurs
- 🔍 Inspecter les conteneurs pour comprendre ce qui se passe à l’intérieur
- 🌐 Les bases du réseau Docker et de l’exposition de ports
Introduction — Docker, la révolution silencieuse
Docker a transformé la façon dont on développe, teste et déploie des applications. Lancé en 2013 par Solomon Hykes lors de la PyCon, Docker a démocratisé une technologie qui existait déjà sous une forme brute dans le noyau Linux (les namespaces et les cgroups) mais qui était complexe à utiliser.
Avant Docker, les conteneurs Linux existaient déjà — LXC (Linux Containers) permettait de créer des environnements isolés. Mais LXC était complexe, mal documenté et nécessitait une expertise système approfondie. L’innovation de Docker n’est pas le conteneur en lui-même, mais l’expérience développeur : une CLI simple, un format d’image standardisé (le Dockerfile), et un registry centralisé (Docker Hub).
Aujourd’hui, Docker est devenu un standard de l’industrie. Même si d’autres runtimes de conteneurs existent (Podman, containerd, CRI-O), l’écosystème Docker reste la référence. Quand quelqu’un dit “conteneur”, il pense Docker.
🔥 Cas réel en entreprise — PayPal a migré de machines virtuelles vers des conteneurs Docker et a réduit ses coûts d’infrastructure de 50%, tout en doublant le nombre de déploiements par jour. La raison ? Les conteneurs consomment moins de ressources et démarrent instantanément.
Qu’est-ce qu’un conteneur ?
Avant de lancer ta première commande Docker, il faut comprendre ce qu’est réellement un conteneur. Pas la version marketing — la vraie définition technique.
Définition technique
Un conteneur est un processus isolé qui s’exécute sur le système hôte. Ce n’est pas une machine virtuelle miniature. Ce n’est pas un OS dans un OS. C’est un processus Linux standard, mais avec des limites et des frontières définies par le noyau.
Ces frontières sont créées par deux mécanismes du noyau Linux :
-
Namespaces — ils isolent ce que le conteneur peut voir. Chaque conteneur a sa propre vision du système : son propre arbre de processus (PID namespace), son propre réseau (Network namespace), son propre système de fichiers (Mount namespace), son propre hostname (UTS namespace), etc.
-
Cgroups (Control Groups) — ils limitent ce que le conteneur peut utiliser. Tu peux limiter la mémoire, le CPU, les I/O disque, le réseau. Un conteneur ne peut pas consommer plus que ce qu’on lui a alloué.
Concrètement, quand tu lances un conteneur, voici ce qui se passe :
- Docker demande au noyau Linux de créer de nouveaux namespaces
- Le processus de ton application est lancé dans ces namespaces
- Des cgroups sont configurés pour limiter les ressources
- Un système de fichiers est monté à partir de l’image Docker
- Le réseau est configuré (interface virtuelle, IP, routes)
Le résultat : ton application croit être seule sur la machine. Elle voit son propre /, ses propres processus, son propre réseau. Mais en réalité, c’est juste un processus parmi d’autres sur l’hôte.
🧠 À retenir — Un conteneur n’est PAS une mini-VM. C’est un processus isolé grâce aux namespaces et cgroups du noyau Linux. C’est pour ça qu’il démarre en millisecondes — il n’y a pas d’OS à booter.
Les propriétés fondamentales d’un conteneur
Un conteneur Docker possède quatre propriétés clés :
1. Isolé — Il voit son propre système de fichiers, ses propres interfaces réseau, ses propres processus. Il ne peut pas interférer avec les autres conteneurs ou avec l’hôte (sauf si tu le permets explicitement).
2. Léger — Il ne contient pas de système d’exploitation complet. Il partage le noyau de l’hôte et n’embarque que les couches applicatives nécessaires. Un conteneur Alpine Linux fait 5 Mo. Un conteneur Ubuntu fait ~78 Mo. Comparer ça à une VM Ubuntu de 2-4 Go.
3. Éphémère — Un conteneur peut être créé, détruit et recréé en quelques secondes. C’est un objet jetable. Les données importantes ne doivent jamais vivre dans le conteneur lui-même — on utilise des volumes pour ça (on verra dans un prochain chapitre).
4. Portable — Une image Docker tourne de la même façon sur ton MacBook, sur un serveur Ubuntu en datacenter, sur une instance AWS, ou dans un cluster Kubernetes. C’est la promesse fondamentale de Docker.
💡 Tip DevOps — Pense aux conteneurs comme des processus jetables. Ne stocke jamais de données critiques dans un conteneur. Utilise des volumes Docker ou des services externes (base de données, stockage objet). Si ton conteneur ne peut pas être détruit et recréé sans perte, ton architecture a un problème.
Comment le conteneur voit-il son monde ?
Pour bien comprendre l’isolation, regardons ce qu’un conteneur perçoit de son environnement.
Quand tu es sur ta machine hôte et que tu lances ps aux, tu vois des centaines de processus — ton navigateur, ton éditeur, des services système, etc.
Mais quand tu entres dans un conteneur et que tu lances ps aux, tu ne vois que les processus du conteneur. Souvent un seul : le processus principal de ton application.
De même, quand tu fais ls / dans un conteneur, tu vois un système de fichiers complet (/bin, /etc, /usr, etc.) — mais c’est celui de l’image Docker, pas celui de ta machine hôte.
C’est comme si le conteneur vivait dans sa propre petite bulle, inconscient du monde extérieur.
⚠️ Attention — L’isolation des conteneurs est au niveau du processus, pas au niveau matériel comme une VM. Un conteneur partage le noyau de l’hôte. Si un attaquant arrive à s’échapper du conteneur (container escape), il a accès au noyau de l’hôte. C’est pour ça qu’en production, on ne lance jamais de conteneurs avec les privilèges root sans bonne raison.
Conteneurs vs Machines Virtuelles — Le grand débat
C’est la question que tout le monde se pose en découvrant Docker. Les deux technologies permettent d’isoler des applications, mais elles fonctionnent à des niveaux fondamentalement différents. Comprendre cette distinction est essentiel pour faire les bons choix d’architecture.
Machine Virtuelle (VM) — Un ordinateur dans un ordinateur
Une VM émule un ordinateur complet : matériel virtuel, système d’exploitation invité, noyau dédié. C’est comme avoir un ordinateur dans un ordinateur.
L’architecture d’une VM ressemble à ça (de bas en haut) :
- Infrastructure — le serveur physique (CPU, RAM, disque, réseau)
- Host OS — le système d’exploitation de l’hôte
- Hyperviseur — le logiciel qui crée et gère les VMs (VMware, VirtualBox, KVM, Hyper-V)
- Guest OS — chaque VM a son propre système d’exploitation complet
- Bibliothèques — chaque VM installe ses propres dépendances
- Application — ton app tourne tout en haut de la pile
graph TB
subgraph VM["Machine Virtuelle"]
App1[App A] --> Bins1[Libs]
Bins1 --> GuestOS1[Guest OS]
GuestOS1 --> Kernel1[Kernel]
App2[App B] --> Bins2[Libs]
Bins2 --> GuestOS2[Guest OS]
GuestOS2 --> Kernel2[Kernel]
App3[App C] --> Bins3[Libs]
Bins3 --> GuestOS3[Guest OS]
GuestOS3 --> Kernel3[Kernel]
Kernel1 --> Hyp[Hypervisor]
Kernel2 --> Hyp
Kernel3 --> Hyp
Hyp --> HostOS1[Host OS]
HostOS1 --> Infra1[Infrastructure]
end
style VM fill:#0a0e17,stroke:#f87171,color:#f1f5f9
style Hyp fill:#7f1d1d,stroke:#dc2626,color:#f1f5f9
style HostOS1 fill:#1a2332,stroke:#3b82f6,color:#f1f5f9
style Infra1 fill:#1a2332,stroke:#a855f7,color:#f1f5f9
style App1 fill:#1a2332,stroke:#22c55e,color:#f1f5f9
style App2 fill:#1a2332,stroke:#22c55e,color:#f1f5f9
style App3 fill:#1a2332,stroke:#22c55e,color:#f1f5f9
Le point crucial : chaque VM possède son propre noyau et son propre OS complet. C’est ce qui la rend lourde. Une VM Ubuntu pèse facilement 2 à 4 Go rien que pour l’OS, et met 30 secondes à une minute pour démarrer — le temps de booter un OS complet.
Conteneur — Un processus, pas une machine
Un conteneur est radicalement différent. Il partage le noyau de l’hôte et n’embarque que l’application et ses dépendances — pas de système d’exploitation complet, pas de noyau dédié.
L’architecture d’un conteneur (de bas en haut) :
- Infrastructure — le serveur physique
- Host OS + Kernel — un seul OS, un seul noyau, partagé par tous les conteneurs
- Docker Engine — le moteur qui gère l’isolation (namespaces, cgroups)
- Bibliothèques — chaque conteneur a ses propres dépendances
- Application — ton app
graph TB
subgraph Container["Conteneur Docker"]
AppC1[App A] --> BinsC1[Libs]
AppC2[App B] --> BinsC2[Libs]
AppC3[App C] --> BinsC3[Libs]
BinsC1 --> DockerE[Docker Engine]
BinsC2 --> DockerE
BinsC3 --> DockerE
DockerE --> HostOS2[Host OS + Kernel]
HostOS2 --> Infra2[Infrastructure]
end
style Container fill:#0a0e17,stroke:#22c55e,color:#f1f5f9
style DockerE fill:#1e3a5f,stroke:#3b82f6,color:#f1f5f9
style HostOS2 fill:#1a2332,stroke:#3b82f6,color:#f1f5f9
style Infra2 fill:#1a2332,stroke:#a855f7,color:#f1f5f9
style AppC1 fill:#1a2332,stroke:#22c55e,color:#f1f5f9
style AppC2 fill:#1a2332,stroke:#22c55e,color:#f1f5f9
style AppC3 fill:#1a2332,stroke:#22c55e,color:#f1f5f9
Pas de noyau dupliqué, pas d’OS invité. Un conteneur pèse quelques mégaoctets et démarre en moins d’une seconde.
Comparaison détaillée
| Critère | Machine Virtuelle | Conteneur Docker |
|---|---|---|
| Isolation | Complète (noyau séparé) | Processus (noyau partagé) |
| Taille typique | 2-10 Go | 10-500 Mo |
| Démarrage | 30s à 2 min | < 1 seconde |
| Performance | Overhead de virtualisation (~5-15%) | Quasi-native (~1-2%) |
| Densité | 10-20 VMs par serveur | 100-1000 conteneurs par serveur |
| Portabilité | Limitée (images lourdes, liées à l’hyperviseur) | Excellente (standard OCI) |
| Sécurité | Forte (isolation matérielle) | Bonne (mais surface d’attaque noyau) |
| Cas d’usage | Isolation forte, OS différents | Microservices, CI/CD, scaling |
Quand utiliser quoi ?
Utilise des conteneurs quand :
- Tu déploies des applications en microservices
- Tu as besoin de scaler rapidement (10 à 1000 instances)
- Tu veux de la reproductibilité (dev = staging = prod)
- Tu fais du CI/CD et tu veux des builds propres
Utilise des VMs quand :
- Tu as besoin d’une isolation forte (multi-tenant, sécurité critique)
- Tu dois faire tourner des OS différents (Windows + Linux sur le même hôte)
- Tu exécutes des applications legacy qui ont besoin d’un OS complet
- Tu as des contraintes de compliance qui imposent l’isolation matérielle
🧠 À retenir — Les conteneurs ne remplacent pas les VMs. Ce sont des outils complémentaires. En production, la configuration la plus courante est de faire tourner des conteneurs à l’intérieur de VMs pour combiner les avantages des deux : isolation matérielle de la VM + légèreté et portabilité des conteneurs.
🔥 Cas réel en entreprise — Chez la plupart des cloud providers (AWS, GCP, Azure), quand tu utilises un service de conteneurs managé (ECS, GKE, AKS), tes conteneurs tournent dans des VMs. AWS Fargate, par exemple, crée une micro-VM par tâche pour une isolation renforcée. Tu utilises les conteneurs sans même te soucier des VMs — mais elles sont bien là, en dessous.
L’architecture Docker en détail
Docker n’est pas un outil monolithique. C’est un ensemble de composants qui collaborent selon une architecture client-serveur. Comprendre ces composants est essentiel pour débugger quand quelque chose ne fonctionne pas — et en Docker, savoir où chercher fait toute la différence.
graph TB
subgraph Client
CLI[Docker CLI]
end
subgraph Host["Docker Host"]
Daemon[Docker Daemon]
Daemon --> C1[Containers]
Daemon --> Img[Images]
Daemon --> Net[Networks]
Daemon --> Vol[Volumes]
end
subgraph Registry["Docker Hub / Registry"]
Repo1[nginx:latest]
Repo2[python:3.11]
Repo3[postgres:16]
end
CLI -->|"docker run"| Daemon
Daemon -->|pull| Registry
style CLI fill:#1e3a5f,stroke:#3b82f6,color:#f1f5f9
style Daemon fill:#7f1d1d,stroke:#dc2626,color:#f1f5f9
style C1 fill:#1a2332,stroke:#22c55e,color:#f1f5f9
style Img fill:#1a2332,stroke:#f59e0b,color:#f1f5f9
style Net fill:#1a2332,stroke:#3b82f6,color:#f1f5f9
style Vol fill:#1a2332,stroke:#a855f7,color:#f1f5f9
style Repo1 fill:#1a2332,stroke:#a855f7,color:#f1f5f9
style Repo2 fill:#1a2332,stroke:#a855f7,color:#f1f5f9
style Repo3 fill:#1a2332,stroke:#a855f7,color:#f1f5f9
Docker CLI — Ton interface avec Docker
Le CLI (Command Line Interface) est l’outil que tu utilises au quotidien. C’est le client dans l’architecture client-serveur. Quand tu tapes docker run, docker build ou docker ps, tu interagis avec le CLI.
Le CLI ne fait rien par lui-même. Il traduit tes commandes en appels à l’API REST du Docker Daemon. C’est un simple client HTTP qui envoie des requêtes au daemon et affiche les réponses.
Ce détail est important : le CLI et le daemon n’ont pas besoin d’être sur la même machine. Tu peux configurer ton CLI pour parler à un daemon Docker distant — c’est comme ça que fonctionnent les outils de déploiement.
💡 Tip DevOps — Tu peux utiliser la variable d’environnement DOCKER_HOST pour pointer ton CLI vers un daemon distant : export DOCKER_HOST=tcp://mon-serveur:2376. C’est très utile pour gérer des serveurs Docker à distance sans SSH.
Docker Daemon (dockerd) — Le moteur
Le daemon (dockerd) est le cerveau de Docker. C’est un processus en arrière-plan (un service systemd sur Linux) qui gère tout :
- Créer et gérer les conteneurs — démarrage, arrêt, suppression, redémarrage
- Construire les images — lecture du Dockerfile, exécution des instructions, création des couches
- Gérer le réseau — création de réseaux virtuels, attribution d’IPs, routage
- Gérer le stockage — volumes, bind mounts, drivers de stockage
- Communiquer avec les registries — pull et push d’images
Le daemon écoute par défaut sur un socket Unix (/var/run/docker.sock). C’est par ce socket que le CLI communique avec lui.
⚠️ Attention — Le socket Docker (/var/run/docker.sock) est un point d’accès critique. Quiconque a accès à ce socket a un contrôle total sur Docker — et potentiellement sur la machine hôte. Ne monte jamais ce socket dans un conteneur en production sans une très bonne raison et des mesures de sécurité appropriées.
Sous le capot : containerd et runc
Le daemon Docker ne crée pas les conteneurs directement. Il délègue à des composants plus bas niveau :
-
containerd — un daemon de gestion de conteneurs qui supervise le cycle de vie des conteneurs (création, exécution, arrêt). C’est un projet de la CNCF (Cloud Native Computing Foundation), utilisé aussi par Kubernetes.
-
runc — l’outil qui crée réellement le conteneur au niveau du noyau Linux. C’est lui qui configure les namespaces, les cgroups et lance le processus du conteneur. C’est l’implémentation de référence de la spécification OCI (Open Container Initiative).
La chaîne complète ressemble à ça :
Docker CLI → Docker Daemon → containerd → runc → ton conteneur
🧠 À retenir — Docker est en réalité un écosystème de composants. Le CLI parle au Daemon, qui délègue à containerd, qui utilise runc pour créer les conteneurs. Chaque couche a son rôle. C’est pour ça que des alternatives comme Podman peuvent exister : elles remplacent certaines couches tout en restant compatibles.
Les images Docker — Le plan de construction
Avant de parler de conteneurs en cours d’exécution, il faut comprendre ce qui les précède : les images. C’est un concept fondamental qui perturbe souvent les débutants.
Qu’est-ce qu’une image ?
Une image Docker est un modèle en lecture seule qui contient tout ce qu’il faut pour exécuter une application :
- Le code source ou le binaire de ton application
- Le runtime (Python, Node.js, Java, Go…)
- Les bibliothèques et dépendances système
- Les variables d’environnement
- Les fichiers de configuration
- Les métadonnées (port exposé, commande de démarrage, etc.)
Pense à l’image comme un snapshot de ton application et de son environnement à un instant T. C’est un objet immuable — une fois créée, une image ne change jamais.
La relation image ↔ conteneur
La métaphore la plus simple :
- L’image est le plan de la maison (ou le moule à gâteau)
- Le conteneur est la maison construite (ou le gâteau)
À partir d’un même plan, tu peux construire autant de maisons que tu veux. De même, à partir d’une même image, tu peux lancer autant de conteneurs que tu veux — chacun indépendant, avec son propre état.
Le système de couches (layers)
C’est là que ça devient intéressant. Une image Docker n’est pas un gros blob monolithique. Elle est construite en couches successives (layers), un peu comme un mille-feuille.
Chaque instruction dans un Dockerfile crée une nouvelle couche :
FROM ubuntu:22.04 # Couche 1 : l'OS de base (~78 Mo)
RUN apt-get update # Couche 2 : mise à jour des paquets
RUN apt-get install -y python3 # Couche 3 : installation de Python
COPY app.py /app/ # Couche 4 : copie du code
CMD ["python3", "/app/app.py"] # Métadonnée (pas de nouvelle couche)
Ce système de couches offre trois avantages majeurs :
1. Partage entre images — Si tu as 5 images basées sur ubuntu:22.04, la couche de base n’est stockée qu’une seule fois sur le disque. Les 5 images la partagent.
2. Cache de build — Quand tu reconstruis une image, Docker ne réexécute que les couches qui ont changé (et celles qui suivent). Si tu modifies ton app.py, seule la couche COPY et les suivantes sont reconstruites — pas l’installation de Python.
3. Transfert optimisé — Quand tu push ou pull une image, seules les couches manquantes sont transférées. Si le registry a déjà la couche ubuntu:22.04, elle n’est pas re-téléchargée.
💡 Tip DevOps — L’ordre des instructions dans ton Dockerfile a un impact direct sur les performances de build. Place les instructions qui changent rarement en haut (installation de dépendances) et celles qui changent souvent en bas (copie du code). Tu exploiteras mieux le cache.
Les registries — Où vivent les images
Un registry est un dépôt d’images Docker. C’est l’équivalent de GitHub/GitLab pour le code source, mais pour les images Docker.
Docker Hub est le registry public par défaut. C’est là que tu trouves les images officielles :
nginx— le serveur web le plus utilisé au mondepostgres— la base de données PostgreSQLpython— le runtime Pythonnode— le runtime Node.jsredis— le cache/message broker Redisalpine— une distribution Linux ultra-légère (5 Mo !)
Tu peux aussi utiliser des registries privés pour stocker tes propres images :
- GitHub Container Registry (ghcr.io) — intégré à GitHub
- GitLab Container Registry — intégré à GitLab
- AWS ECR — le registry d’Amazon
- Google Artifact Registry — le registry de Google
- Harbor — solution open source auto-hébergée
⚠️ Attention — Ne tire jamais une image Docker en production sans vérifier sa provenance. Les images officielles (marquées “Docker Official Image” sur Docker Hub) sont vérifiées et maintenues. Les images communautaires peuvent contenir du code malveillant. En entreprise, utilise toujours un registry privé avec un scanning de vulnérabilités.
🔥 Cas réel en entreprise — En 2020, des chercheurs en sécurité ont trouvé des images Docker malveillantes sur Docker Hub qui minaient de la cryptomonnaie. Certaines avaient été téléchargées des millions de fois. C’est pour ça que les entreprises sérieuses utilisent des registries privés avec des politiques de scanning automatique (Trivy, Snyk, Clair).
Installation de Docker
Maintenant que tu comprends les concepts, passons à la pratique. L’installation varie selon ton système d’exploitation.
Sur Ubuntu (22.04 / 24.04)
On installe Docker Engine depuis les dépôts officiels de Docker — pas la version des dépôts Ubuntu (docker.io), qui est souvent en retard de plusieurs versions.
Pourquoi cette distinction est importante ? Les dépôts Ubuntu fournissent une version de Docker qui peut avoir 6 mois à un an de retard. Les dépôts officiels Docker fournissent la dernière version stable, avec les dernières fonctionnalités et les correctifs de sécurité.
Voici la procédure complète, étape par étape :
# 1. Mettre à jour les paquets et installer les prérequis
# Ces paquets permettent à apt d'utiliser des dépôts HTTPS
sudo apt update
sudo apt install -y ca-certificates curl gnupg
# 2. Ajouter la clé GPG officielle de Docker
# Cela garantit que les paquets que tu installes viennent bien de Docker
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# 3. Ajouter le dépôt Docker aux sources apt
# La commande détecte automatiquement ton architecture et ta version d'Ubuntu
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 4. Installer Docker Engine et ses composants
# docker-ce : le daemon Docker
# docker-ce-cli : le client en ligne de commande
# containerd.io : le runtime de conteneurs
# docker-buildx-plugin : le builder nouvelle génération
# docker-compose-plugin : Docker Compose v2 (en plugin)
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
# 5. Ajouter ton utilisateur au groupe docker
# Sans ça, tu devras utiliser sudo pour chaque commande Docker
sudo usermod -aG docker $USER
⚠️ Attention — Après l’ajout au groupe docker, tu dois te déconnecter et te reconnecter pour que le changement prenne effet. Ou exécute newgrp docker dans ton terminal actuel. Sinon, tu auras des erreurs de permission.
💡 Tip DevOps — Ajouter un utilisateur au groupe docker lui donne un accès root effectif sur la machine (il peut monter n’importe quel répertoire dans un conteneur root). Sur un serveur de production, limite l’accès au groupe docker aux seuls utilisateurs qui en ont besoin.
Sur macOS
Sur macOS, Docker ne tourne pas nativement — le noyau Linux n’est pas disponible, et Docker a besoin des namespaces et cgroups Linux. La solution : créer une VM Linux légère en arrière-plan.
Tu as deux options principales :
Option 1 : Docker Desktop (le plus simple)
- Télécharge Docker Desktop depuis docker.com/products/docker-desktop
- Ouvre le fichier
.dmget glisse Docker dans Applications - Lance Docker Desktop depuis le Launchpad
- Attends que l’icône de la baleine dans la barre de menu soit stable
Docker Desktop inclut le CLI, le daemon, Docker Compose, une interface graphique et des extensions. C’est la solution tout-en-un.
Option 2 : Colima (léger et open source)
Si tu préfères un outil en ligne de commande sans interface graphique — et sans les limitations de licence de Docker Desktop pour les grandes entreprises :
# Installation via Homebrew
brew install colima docker docker-compose
# Démarrage de la VM Linux
colima start
# Vérification
docker ps
Colima crée une VM Lima avec Docker installé et configure automatiquement ton CLI pour communiquer avec elle.
🔥 Cas réel en entreprise — Depuis 2022, Docker Desktop est payant pour les entreprises de plus de 250 employés ou plus de 10 millions de dollars de chiffre d’affaires annuel. C’est pourquoi beaucoup de grandes entreprises ont migré vers Colima ou Rancher Desktop. Si tu travailles en startup ou en solo, Docker Desktop reste gratuit.
Sur Windows
Sur Windows, tu as aussi deux options :
- Docker Desktop avec WSL2 — la solution recommandée. WSL2 (Windows Subsystem for Linux 2) fournit un vrai noyau Linux dans Windows, ce qui donne des performances quasi-natives.
- Docker dans une VM Linux — si tu utilises déjà Vagrant, VirtualBox ou Hyper-V.
Vérifier l’installation
Quelle que soit ta plateforme, lance ces deux commandes pour vérifier que tout fonctionne :
La première commande vérifie que le CLI est installé et te donne la version :
docker --version
Tu devrais voir quelque chose comme Docker version 27.x.x, build xxxxxxx.
La deuxième commande vérifie que le daemon est en marche et que le cycle complet fonctionne (pull d’image + création de conteneur + exécution) :
docker run hello-world
Cette commande fait beaucoup de choses en coulisses :
- Le CLI envoie la commande au daemon
- Le daemon cherche l’image
hello-worldlocalement — il ne la trouve pas - Le daemon la télécharge (pull) depuis Docker Hub
- Le daemon crée un conteneur à partir de l’image
- Le conteneur s’exécute, affiche un message, et s’arrête
Si tu vois le message « Hello from Docker! », tout est en place. 🎉
🧠 À retenir — La commande docker run est la commande la plus importante de Docker. Elle combine en réalité trois opérations : docker pull (télécharger l’image), docker create (créer le conteneur) et docker start (démarrer le conteneur). On décortiquera tout ça dans le prochain chapitre.
Tes premières commandes Docker
Maintenant que Docker est installé, explorons les commandes essentielles. On va procéder pas à pas, en expliquant chaque commande avant de la lancer.
Lancer un conteneur interactif
La première chose qu’on veut faire, c’est entrer dans un conteneur pour voir à quoi ça ressemble de l’intérieur. On va lancer un conteneur Ubuntu avec un shell interactif.
Avant de taper la commande, comprenons chaque option :
docker run— crée et démarre un conteneur-it— deux flags combinés :-i(interactif, garde l’entrée standard ouverte) et-t(alloue un pseudo-terminal pour avoir un prompt)ubuntu— le nom de l’image à utiliserbash— la commande à exécuter dans le conteneur
docker run -it ubuntu bash
Tu te retrouves dans un shell à l’intérieur du conteneur. Le prompt change — tu es maintenant root@<container_id>. Explore un peu :
# Regarde la version de l'OS dans le conteneur
cat /etc/os-release
# Liste les processus — il n'y a quasiment rien !
ps aux
# Regarde le système de fichiers — c'est un Linux complet
ls /
# Quitte le conteneur
exit
💡 Tip DevOps — Quand tu fais exit dans un conteneur interactif, le conteneur s’arrête. C’est parce que le processus principal (bash) se termine. Un conteneur vit tant que son processus principal vit. C’est un concept fondamental.
Lancer un conteneur en arrière-plan
La plupart du temps, tu ne veux pas être attaché au conteneur. Tu veux le lancer en arrière-plan (mode détaché) et le laisser tourner.
L’option -d (detach) fait exactement ça. Et l’option -p permet de mapper un port de l’hôte vers un port du conteneur, pour pouvoir accéder au service depuis l’extérieur.
# Lance un serveur web Nginx en arrière-plan
# -d : mode détaché (arrière-plan)
# -p 8080:80 : redirige le port 8080 de l'hôte vers le port 80 du conteneur
# --name mon-nginx : donne un nom au conteneur (plus facile que l'ID)
docker run -d -p 8080:80 --name mon-nginx nginx
Docker affiche l’ID du conteneur et te rend la main. Nginx tourne maintenant en arrière-plan. Tu peux le vérifier en ouvrant http://localhost:8080 dans ton navigateur — tu verras la page d’accueil de Nginx.
Lister les conteneurs
Pour voir ce qui tourne sur ta machine :
# Voir les conteneurs en cours d'exécution
docker ps
# Voir TOUS les conteneurs (y compris ceux arrêtés)
docker ps -a
La commande docker ps affiche un tableau avec des colonnes essentielles :
- CONTAINER ID — l’identifiant unique (les 12 premiers caractères)
- IMAGE — l’image utilisée
- COMMAND — la commande exécutée dans le conteneur
- CREATED — quand le conteneur a été créé
- STATUS — l’état actuel (Up, Exited, etc.)
- PORTS — les ports mappés
- NAMES — le nom du conteneur
Arrêter et supprimer un conteneur
Pour arrêter un conteneur en cours d’exécution, tu envoies un signal d’arrêt propre :
# Arrêter un conteneur (envoie SIGTERM, attend 10s, puis SIGKILL)
docker stop mon-nginx
# Vérifier qu'il est arrêté
docker ps -a
Un conteneur arrêté existe encore — il est juste en état “Exited”. Pour le supprimer définitivement :
# Supprimer un conteneur arrêté
docker rm mon-nginx
# Ou forcer la suppression d'un conteneur en cours d'exécution
docker rm -f mon-nginx
⚠️ Attention — docker rm -f envoie un SIGKILL immédiat au conteneur, sans lui laisser le temps de s’arrêter proprement. Utilise docker stop suivi de docker rm en production pour permettre à ton application de se terminer gracieusement (fermer les connexions DB, finir les requêtes en cours, etc.).
Gérer les images
Les images téléchargées occupent de l’espace disque. Voici comment les gérer :
# Lister les images stockées localement
docker images
# Télécharger une image sans lancer de conteneur
docker pull python:3.12
# Supprimer une image
docker rmi python:3.12
# Supprimer toutes les images non utilisées (attention !)
docker image prune -a
Inspecter un conteneur
Quand quelque chose ne fonctionne pas, tu as besoin de comprendre ce qui se passe à l’intérieur du conteneur. Docker fournit plusieurs commandes d’inspection.
Pour voir les logs du conteneur (ce que le processus écrit sur stdout et stderr) :
# Voir les logs
docker logs mon-nginx
# Suivre les logs en temps réel (comme tail -f)
docker logs -f mon-nginx
# Voir les 50 dernières lignes
docker logs --tail 50 mon-nginx
Pour exécuter une commande dans un conteneur en cours d’exécution :
# Ouvrir un shell dans un conteneur qui tourne
docker exec -it mon-nginx bash
# Exécuter une commande ponctuelle
docker exec mon-nginx cat /etc/nginx/nginx.conf
Pour voir les métadonnées détaillées d’un conteneur :
# Inspecter un conteneur (JSON détaillé)
docker inspect mon-nginx
# Extraire une info spécifique (l'adresse IP par exemple)
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mon-nginx
💡 Tip DevOps — docker exec est ton meilleur ami pour le debug. Quand un conteneur ne se comporte pas comme prévu, entre dedans avec docker exec -it <conteneur> sh (ou bash si disponible) et explore. Vérifie les fichiers de config, les variables d’environnement (env), les processus (ps aux), les connexions réseau (netstat -tlnp).
Les bases du réseau Docker
Quand tu lances un conteneur avec -p 8080:80, tu fais du port mapping. Mais que se passe-t-il réellement sous le capot ?
Le réseau par défaut : bridge
Quand Docker s’installe, il crée automatiquement un réseau virtuel appelé bridge. Tous les conteneurs se connectent à ce réseau par défaut.
Chaque conteneur reçoit une adresse IP privée sur ce réseau (typiquement dans la plage 172.17.0.x). Les conteneurs peuvent communiquer entre eux via ces IPs — mais pas avec le monde extérieur, sauf si tu exposes des ports.
Le port mapping expliqué
Quand tu écris -p 8080:80, tu dis à Docker :
“Quand quelqu’un se connecte au port 8080 de la machine hôte, redirige le trafic vers le port 80 du conteneur.”
La syntaxe est toujours : -p <port_hôte>:<port_conteneur>
Tu peux aussi restreindre l’écoute à une interface spécifique :
# Écouter uniquement sur localhost (pas accessible depuis l'extérieur)
docker run -d -p 127.0.0.1:8080:80 nginx
# Écouter sur toutes les interfaces (par défaut)
docker run -d -p 0.0.0.0:8080:80 nginx
⚠️ Attention — Par défaut, -p 8080:80 écoute sur toutes les interfaces (0.0.0.0), ce qui signifie que le port est accessible depuis le réseau. Sur un serveur de production, Docker modifie directement les règles iptables et peut bypasser ton firewall UFW ! C’est un piège classique. Utilise toujours 127.0.0.1: si le service ne doit être accessible qu’en local.
🔥 Cas réel en entreprise — Des centaines de bases de données de production ont été exposées accidentellement à Internet parce que les admins ne savaient pas que Docker bypass UFW. Ils pensaient que leur firewall les protégeait, mais Docker écrivait ses propres règles iptables. Si tu utilises UFW, cherche “docker ufw fix” et configure Docker pour ne pas modifier iptables automatiquement.
Communication entre conteneurs
Sur le réseau bridge par défaut, les conteneurs peuvent se parler par IP, mais pas par nom. Pour avoir la résolution DNS entre conteneurs (se parler par nom), il faut créer un réseau personnalisé :
# Créer un réseau
docker network create mon-reseau
# Lancer des conteneurs sur ce réseau
docker run -d --name web --network mon-reseau nginx
docker run -d --name db --network mon-reseau postgres:16
# Depuis le conteneur "web", tu peux faire ping db
docker exec web ping db
Sur un réseau personnalisé, Docker fournit un DNS intégré : chaque conteneur est accessible par son nom. C’est la base de la communication entre services dans Docker Compose (qu’on verra dans un prochain chapitre).
🧠 À retenir — Utilise toujours des réseaux personnalisés en production, jamais le réseau bridge par défaut. Les réseaux personnalisés offrent l’isolation, la résolution DNS et un meilleur contrôle sur qui peut parler à qui.
Le cycle de vie d’un conteneur
Comprendre le cycle de vie d’un conteneur est essentiel pour le gérer correctement. Un conteneur passe par plusieurs états :
Created → Running → Paused → Running → Stopped → Removed
Created — Le conteneur existe mais n’a pas encore démarré. C’est l’état après docker create.
Running — Le processus principal est en cours d’exécution. C’est l’état normal.
Paused — Le conteneur est gelé (tous ses processus sont suspendus via les cgroups). Utile pour faire un snapshot ou libérer temporairement du CPU.
Stopped (Exited) — Le processus principal s’est terminé. Le conteneur existe encore avec son système de fichiers, ses logs, ses métadonnées. Tu peux le relancer avec docker start.
Removed — Le conteneur est supprimé définitivement. Toutes ses données (sauf les volumes) sont perdues.
# Cycle de vie complet
docker create --name demo nginx # Created
docker start demo # Running
docker pause demo # Paused
docker unpause demo # Running
docker stop demo # Stopped
docker start demo # Running (encore)
docker rm -f demo # Removed
💡 Tip DevOps — Pour les environnements de développement, utilise le flag --rm avec docker run pour supprimer automatiquement le conteneur quand il s’arrête : docker run --rm -it ubuntu bash. Ça évite d’accumuler des conteneurs arrêtés qui occupent de l’espace.
Nettoyage — Garder ton système propre
Avec le temps, Docker accumule des conteneurs arrêtés, des images inutilisées, des réseaux orphelins et des volumes abandonnés. Ça peut consommer beaucoup d’espace disque.
Avant de lancer les commandes de nettoyage, voyons d’abord ce qui occupe de l’espace :
# Voir l'utilisation disque de Docker
docker system df
Cette commande t’affiche la taille des images, des conteneurs, des volumes et du cache de build.
Voici ensuite les commandes de nettoyage, de la plus ciblée à la plus agressive :
# Supprimer les conteneurs arrêtés
docker container prune
# Supprimer les images non utilisées (sans tag ni conteneur associé)
docker image prune
# Supprimer les réseaux inutilisés
docker network prune
# ⚠️ Le nettoyage complet — supprime TOUT ce qui n'est pas utilisé
docker system prune -a --volumes
⚠️ Attention — docker system prune -a --volumes est la commande nucléaire. Elle supprime tout ce qui n’est pas utilisé par un conteneur en cours d’exécution : images, conteneurs, réseaux ET volumes. Les données dans les volumes seront perdues définitivement. Utilise avec précaution, surtout en production.
💡 Tip DevOps — Mets en place un cron job de nettoyage sur tes serveurs Docker : 0 3 * * 0 docker system prune -af --filter "until=168h" nettoie chaque dimanche à 3h les ressources inutilisées depuis plus de 7 jours. C’est moins agressif et évite que le disque ne se remplisse silencieusement.
Récapitulatif des commandes essentielles
Voici un aide-mémoire des commandes que tu as apprises dans ce chapitre :
| Commande | Description |
|---|---|
docker run <image> | Créer et démarrer un conteneur |
docker run -d <image> | Lancer en arrière-plan |
docker run -it <image> bash | Lancer en mode interactif |
docker run -p 8080:80 <image> | Mapper un port |
docker run --rm <image> | Supprimer automatiquement à l’arrêt |
docker ps | Lister les conteneurs actifs |
docker ps -a | Lister tous les conteneurs |
docker stop <conteneur> | Arrêter un conteneur |
docker rm <conteneur> | Supprimer un conteneur |
docker images | Lister les images locales |
docker pull <image> | Télécharger une image |
docker rmi <image> | Supprimer une image |
docker logs <conteneur> | Voir les logs |
docker exec -it <conteneur> bash | Exécuter une commande |
docker inspect <conteneur> | Voir les métadonnées |
docker system prune | Nettoyer les ressources inutilisées |
À toi de jouer — Exercices pratiques
Exercice 1 — Explore un conteneur de l’intérieur
Lance un conteneur Ubuntu en mode interactif et compare l’environnement du conteneur avec celui de ta machine hôte.
docker run -it ubuntu bash
À l’intérieur du conteneur, exécute :
cat /etc/os-release— quelle version d’Ubuntu ?ps aux— combien de processus tournent ?hostname— quel est le hostname ?ls /— le système de fichiers est-il complet ?whoami— quel utilisateur es-tu ?
Quitte avec exit et exécute les mêmes commandes sur ta machine hôte. Note les différences.
🧠 À retenir — Dans le conteneur, tu ne verras que 1-2 processus (bash et ps). Sur ta machine hôte, tu en verras des centaines. C’est la preuve de l’isolation par les namespaces.
Exercice 2 — Conteneurs éphémères et gestion
Lance 3 conteneurs Nginx en arrière-plan, manipule-les, et nettoie tout.
# Lance 3 conteneurs avec des noms différents
docker run -d --name web1 -p 8081:80 nginx
docker run -d --name web2 -p 8082:80 nginx
docker run -d --name web3 -p 8083:80 nginx
# Vérifie qu'ils tournent
docker ps
# Teste que chaque serveur répond
curl http://localhost:8081
curl http://localhost:8082
curl http://localhost:8083
# Arrête-les tous d'un coup
docker stop web1 web2 web3
# Vérifie qu'ils sont arrêtés (mais existent encore)
docker ps -a
# Supprime-les
docker rm web1 web2 web3
Exercice 3 — Comprendre les couches d’images
Télécharge deux variantes de la même image et compare-les pour comprendre le système de couches.
# Télécharge la version complète et la version slim
docker pull python:3.12
docker pull python:3.12-slim
# Compare les tailles
docker images | grep python
# Inspecte les couches de l'image complète
docker history python:3.12
# Inspecte les couches de l'image slim
docker history python:3.12-slim
Questions à te poser :
- Quelle est la différence de taille entre les deux ?
- Combien de couches chaque image a-t-elle ?
- Pourquoi la version slim est-elle plus légère ? Qu’est-ce qui manque ?
💡 Tip DevOps — En production, utilise toujours les images les plus légères possibles. Préfère python:3.12-slim à python:3.12, et encore mieux, python:3.12-alpine (si tes dépendances sont compatibles). Une image plus petite = un pull plus rapide, une surface d’attaque réduite, et moins d’espace disque.
Exercice 4 — Défi réseau
Crée un réseau personnalisé et vérifie que deux conteneurs peuvent communiquer par nom.
# Crée un réseau
docker network create test-net
# Lance deux conteneurs sur ce réseau
docker run -d --name serveur --network test-net nginx
docker run -d --name client --network test-net alpine sleep 3600
# Depuis le client, ping le serveur par son nom
docker exec client ping -c 3 serveur
# Vérifie que le client peut accéder au serveur web
docker exec client wget -qO- http://serveur
# Nettoie
docker rm -f serveur client
docker network rm test-net
Si le ping serveur fonctionne, tu as compris le DNS intégré de Docker. C’est la base de l’architecture microservices avec Docker Compose.
🚀 Pratique en direct
Envie de tester immédiatement ? Lance le Playground DevOpsLab — un terminal Linux complet dans ton navigateur avec tous les outils pré-installés.
➡️ La suite : Dans le prochain chapitre, on approfondit les commandes Docker : run avec toutes ses options, la gestion des volumes, les variables d’environnement, et on construit notre première image avec un Dockerfile. On continue ! 🚀
🖥️ Pratique sur ton propre serveur
Pour suivre Apprendre Docker en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.
Série : Apprendre Docker
1 / 10- 1 Pourquoi Docker ? Les conteneurs expliqués
- 2 Tes premières commandes Docker
- 3 Ton premier Dockerfile
- 4 Dockerfile avancé : multi-stage et optimisation
- 5 Docker Compose : orchestrer tes services
- 6 Compose avancé : volumes, réseaux et scaling
- 7 Docker en production : logs et healthchecks
- 8 Déploiement et CI/CD avec Docker
- 9 Sécurité Docker : rootless et isolation
- 10 Scanning et conformité Docker
Sur cette page
Articles liés
Tes premières commandes Docker
Prends en main Docker avec tes premières commandes : docker run, pull, ps, images, exec et le cycle de vie des conteneurs.
Ton premier Dockerfile
Apprends à écrire un Dockerfile avec les instructions essentielles : FROM, COPY, RUN, CMD et docker build.
Dockerfile avancé : multi-stage et optimisation
Maîtrise les multi-stage builds, l'optimisation des layers, le cache Docker et les bonnes pratiques pour des images légères.