Docker et les conteneurs, pour beaucoup, ça reste une abstraction opaque. Je m'en sers tous les jours. Et comme l'abstraction se digère mal à jeun, on va en parler avec des pancakes. À la fin, tu sauras ce qu'est une image, un conteneur, un volume, un réseau, et pourquoi une seule commande remplace une demi-journée à configurer des environnements.
Sans Docker : cauchemar en cuisine
Imagine un restaurant. Un vrai. Avec des clients pénibles qui veulent TOUS un pancake différent :
- Jean-Michel veut des pancakes aux myrtilles.
- Sophie veut des pancakes au chocolat.
- Cyril veut un kebab.
Sauf qu'il y a un problème : tu n'as qu'une seule cuisine, une seule poêle, une seule table pour tout préparer. Résultat : les ingrédients se mélangent, les pancakes ont tous le même goût, et il y a des morceaux de kebab dans les myrtilles. Sympa.
En informatique, c'est pareil. Sans Docker, toutes tes applications tournent sur le même serveur, partagent les mêmes bibliothèques, les mêmes dépendances. Il suffit d'une mise à jour de trop, et une appli en casse une autre sans que tu comprennes pourquoi.
Docker : chacun sa mini-cuisine
Docker, c'est le pote débrouillard qui débarque et propose : « et si chaque pancake avait sa propre mini-cuisine ? »
Maintenant, chaque type de pancake a son espace de cuisson, ses ingrédients, sa poêle :
- Isolation totale : les pancakes ne se mélangent plus, chacun garde son goût.
- Zéro conflit : pas de gluten qui traîne dans le vegan, pas de chocolat dans la myrtille.
- Nettoyage facile : un pancake est raté ? On le jette, on recommence, la cuisine reste propre.
En clair, Docker emballe chaque application avec tout ce dont elle a besoin : son runtime, ses dépendances, son environnement, dans un conteneur hermétique.
Images et conteneurs : la recette et le pancake
Docker fonctionne avec des images, qui sont des recettes toutes prêtes :
- Une image Docker, c'est une recette de pancakes (« pancake au chocolat 1.0 »).
- Un conteneur, c'est un pancake en train de cuire sur la poêle.
Tu veux refaire un pancake identique ? Pas besoin de tout recommencer à la main. Tu relances un conteneur depuis l'image, et tu obtiens le même pancake, au gramme près. L'image, c'est la recette ; le conteneur, c'est le pancake qui cuit.
Exemple de Dockerfile
# On part d'une image avec PHP préinstallé
FROM php:8.4-cli
# On installe les extensions PHP pour parler à une base de données
RUN docker-php-ext-install pdo pdo_mysql
# On copie le code source dans le conteneur, dossier /app
COPY . /app
# On définit le dossier de travail par défaut
WORKDIR /app
# La commande lancée au démarrage du conteneur
CMD ["php", "index.php"]Ligne par ligne :
FROM php:8.4-cli: on part d'une image officielle PHP en ligne de commande.RUN docker-php-ext-install pdo pdo_mysql: on ajoute les extensions pour MySQL.COPY . /app: on copie tout le projet local vers/appdans le conteneur.WORKDIR /app:/appdevient le dossier de travail des commandes suivantes.CMD ["php", "index.php"]: la commande par défaut au démarrage (ici, lancerphp index.php).
Docker Compose : parce qu'un seul pancake, c'est triste
Un pancake, c'est bien. Un petit-déj complet, c'est mieux :
- Pancakes
- Café
- Sirop d'érable
- Serveur web (parce qu'on bosse un peu quand même)
Plutôt que de tout démarrer à la main, Docker Compose s'en charge d'un coup. Dans un fichier compose.yaml, tu décris tous tes conteneurs et la façon dont ils se parlent. Une commande, et ton petit-déj est prêt.
Sur le même sujet
Composer : le manifeste du développeur moderne
Petite note d'époque : la commande, aujourd'hui, c'est docker compose (deux mots, intégré à Docker), et non plus l'ancien docker-compose avec un tiret. Et le fichier s'appelle compose.yaml, la clé version: en tête n'a plus lieu d'être.
Exemple de compose.yaml
services:
web:
image: nginx:latest # Nginx comme serveur web
ports:
- "8080:80" # port 8080 de la machine vers le port 80 du conteneur
volumes:
- ./public:/usr/share/nginx/html # dossier public/ local servi par Nginx
php:
build: . # construit l'image depuis le Dockerfile ci-dessus
volumes:
- .:/app # monte le code source local dans le conteneur
db:
image: mysql:latest # MySQL comme base de données
environment:
MYSQL_ROOT_PASSWORD: root # mot de passe root
MYSQL_DATABASE: myapp # crée une base "myapp" au démarrageLigne par ligne :
services:: la liste des conteneurs que Compose va gérer.web:: le service Nginx.image: nginx:latest: l'image officielle Nginx.ports: - "8080:80": redirige le port 8080 de la machine vers le 80 du conteneur.volumes: - ./public:/usr/share/nginx/html: monte le dossier local pour servir son contenu.php:: le service PHP, construit depuis le Dockerfile (build: .).db:: le service MySQL, avec ses variables d'environnement.
Et un aveu sur ces exemples : nginx:latest et mysql:latest, tous les tutos s'en servent, celui-ci aussi. En vrai, latest change sous tes pieds à chaque pull : le petit-déj qui marchait hier casse demain sans que tu aies touché à rien. Sur un vrai projet, fige tes versions (nginx:1.27, mysql:8.4) : la recette exacte plutôt que « la recette du moment ».
Un mot honnête : ce blog ne tourne pas sur Nginx + PHP-FPM comme dans l'exemple. Il tourne sur FrankenPHP, un seul binaire au lieu de deux services. Mais le principe Compose est identique, et pour apprendre, ce trio web + php + db reste le plus parlant.
Pour aller plus loin
FrankenPHP en prod : un binaire, quatre sous-domaines, un seul worker
Pour aller plus loin
Comment FrankenPHP a relégué PHP-FPM et Nginx au stade de reliques
Les volumes : ne pas perdre sa garniture
C'est bien beau de faire des pancakes, mais si tu jettes toute la garniture chaque fois que tu changes de poêle, ça devient vite pénible.
Un volume Docker, c'est le tupperware posé à côté de ta cuisine temporaire :
- Tu détruis ton conteneur ? Tes données précieuses survivent.
- Tu veux réutiliser ces données dans un nouveau conteneur ? Aucun souci.
services:
db:
image: mysql:8.4
volumes:
- ./mes_donnees:/data/application # bind mount : un dossier local
- ma_base_mysql:/var/lib/mysql # volume nommé, géré par Docker
volumes:
ma_base_mysql: # déclaré à la racine du fichier, Docker gère le stockageDeux styles ici :
- Le premier monte un dossier local dans le conteneur (pratique en dev, tu vois tes fichiers).
- Le second crée un espace de stockage géré par Docker (idéal en prod).
Petit piège au passage : le volume nommé se déclare deux fois. Une fois dans le service qui s'en sert, une fois dans le bloc volumes: à la racine du fichier. Oublie le second et Compose te le fera savoir.
C'est la différence entre ramener ta propre garniture et utiliser celle du restaurant.
Les réseaux : la table commune des pancakes
Tes conteneurs ont besoin de se parler. Ton PHP doit joindre ta base de données, ton serveur web doit joindre ton PHP. Tout un écosystème de pancakes qui s'échangent des ingrédients.
Par défaut, Docker Compose crée un réseau où tous tes services se voient automatiquement. Chaque service est joignable par son nom :
# Dans ton compose.yaml
services:
php:
# ...
db:
# ...
# PHP joint la base avec "db" comme nom d'hôte.
# En PHP : $db = new PDO('mysql:host=db;dbname=myapp');Tu peux aussi créer des réseaux dédiés pour cloisonner certains conteneurs :
networks:
frontend: # réseau pour l'interface web
backend: # réseau pour les services internesLes variables d'environnement : l'assaisonnement des pancakes
Comme un chef qui ajuste le sel selon le service, tes applications s'adaptent à leur environnement :
environment:
- APP_ENV=production
- DATABASE_URL=mysql://user:pass@db:3306/appOu avec un fichier .env, pour garder tes mots de passe hors du fichier Compose :
env_file:
- .env.productionC'est ta recette d'assaisonnement rangée à part, que tu changes sans toucher à la cuisine.
Docker Hub : le livre de recettes collaboratif
Pourquoi réinventer une recette de pancake quand des milliers de gens ont déjà partagé la leur ? Docker Hub, c'est un peu le GitHub des images Docker :
docker pull mysql:8.0
docker pull php:8.4-fpm-alpine
docker pull nginx:alpineTu peux aussi y publier tes propres images :
docker build -t mon-username/mon-image:latest .
docker push mon-username/mon-image:latestLà, n'importe qui peut réutiliser ta recette de pancake au chocolat fondant.
Dockerfile multi-stage : la préparation pro
En cuisine pro, on prépare d'abord tous les ingrédients, puis on assemble. En Docker, c'est le build multi-étapes :
# Étape de build : la cuisine où on prépare tout
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Étape de production : juste ce qu'il faut pour servir
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/htmlRésultat : une image finale minuscule, avec seulement ce que tu sers. Pas d'outils de compilation, pas de déchets, juste un pancake prêt à passer à table.
Les commandes essentielles : le vocabulaire du chef pancake
Pour t'en sortir au quotidien, ce petit lot suffit :
# Construire une image
docker build -t mon-image:v1 .
# Lancer un conteneur depuis une image
docker run -p 8080:80 mon-image:v1
# Voir les conteneurs en cours
docker ps
# Arrêter un conteneur
docker stop mon-conteneur
# Supprimer un conteneur
docker rm mon-conteneur
# Voir les logs d'un conteneur
docker logs mon-conteneur
# Ouvrir un shell dans un conteneur
docker exec -it mon-conteneur bashAvec Compose, c'est encore plus court :
# Démarrer tous les services
docker compose up -d
# Voir les logs de tous les services
docker compose logs
# Tout arrêter
docker compose downLa sécurité : parce que les pancakes empoisonnés, c'est pas cool
Ne fais pas tourner tes conteneurs en tant que root. C'est comme laisser les clés de ta cuisine à un inconnu :
# Créer un utilisateur dédié, aux droits limités
RUN adduser --disabled-password --gecos "" pancakeuser
USER pancakeuserEt surveille les permissions sur les volumes :
volumes:
- ./config:/app/config:ro # ro = lecture seuleCes deux réflexes sont un début, pas une forteresse. Fermer la porte setuid, retirer les capabilities dont un service n'a pas besoin, c'est un cran plus loin, et j'y reviendrai dans un billet dédié.
Pourquoi Docker, au fond
Parce que sans Docker, c'est souvent ça :
- « Ça marche sur ma machine », mais pas sur celle du collègue.
- Des heures perdues à configurer des environnements.
- Des conflits de dépendances qui donnent envie de tout casser.
- Des déploiements manuels qui te font suer à 2 h du matin.
Avec Docker, c'est plutôt ça :
- Chaque application est isolée, plus de dépendances qui se marchent dessus.
- Un déploiement reproductible, la même image en dev et en prod.
- Fini le « chez moi ça marchait ».
- Une montée en charge plus simple le jour où ton site décolle.
Passer à la grande échelle : l'orchestration
Quand ton restaurant de pancakes devient une chaîne, tu veux un système qui pilote des milliers de mini-cuisines tout seul. C'est le rôle de :
- Kubernetes : il gère des clusters entiers de conteneurs, répartit la charge, relance ce qui tombe, ajuste la taille selon le trafic.
- Docker Swarm : la version simplifiée, intégrée à Docker.
Est-ce qu'il te le faut ? Pour une chaîne mondiale, oui. Pour un blog sur un VPS, la réponse est plus nuancée, et honnêtement souvent non. C'est un débat à part entière, un autre pancake, sur lequel j'ai des choses à dire (bientôt).
On récapitule ? Non, on ouvre
Docker, c'est donner à chaque application sa propre mini-cuisine : ses ingrédients, ses ustensiles, sa recette. Moins de conflits, moins de « chez moi ça marchait », moins de galères de déploiement.
Ce billet s'arrête à la poêle : les images, les conteneurs, les volumes, les réseaux, Compose. Il ne descend pas dans le kernel ni ne monte jusqu'au cluster, ce sont d'autres étages.
Une fois tes mini-cuisines qui tournent, deux questions arrivent vite. La première : est-ce qu'il me faut vraiment Kubernetes pour orchestrer tout ça ? La seconde : est-ce que mes conteneurs sont bien fermés à clé, ou est-ce qu'un sudo traîne dans la cuisine ? Deux pancakes pour un prochain petit-déj.
Et toi, c'est quoi le premier truc que tu vas conteneuriser ?
Une coquille, une erreur dans ce billet ? Signale-la-moi.
Ce billet est publié sous licence Creative Commons BY-NC-SA 4.0 (attribution, pas d'usage commercial, partage dans les mêmes conditions).