Tu entends parler de Docker partout. On te dit: lance juste le conteneur, maîtrise de Docker, et tu voie des docker run dans tous les tutoriels... mais tu ne comprends toujours pas ce que c'est vraiment ?
Si "ça marche sur ma machine" est ta phrase préférée et que tu as déjà passé des heures à installer des dépendances... cet article est fait pour toi.
Docker, ce n'est pas sorcier. Une fois que tu comprends le concept de base, tout devient limpide. Et je vais t'expliquer ça simplement, sans jargon inutile.
Dans ce guide, on va apprendre Docker en pratique, avec des exemples concrets et des situations réelles que tu vas rencontrer.
Ce que tu vas apprendre :
-
C'est quoi Docker et pourquoi c'est révolutionnaire
-
La différence entre conteneurs et machines virtuelles
-
Les commandes essentielles expliquées simplement
-
Créer tes propres images avec Dockerfile
-
Docker Compose pour les projets multi-conteneurs
-
Les bonnes pratiques pour ne pas galérer
-
Déployer tes applications comme un pro
Allez, on démarre de zéro ! 🚀
Partie 1 : Comprendre Docker (la théorie simple)
C'est quoi Docker, concrètement ?
Imagine que tu prépares un gâteau. Tu as la recette, les ingrédients, le bon four... et ça marche parfaitement chez toi. Maintenant, tu veux que ton ami fasse exactement le même gâteau chez lui. Problème : il n'a pas le même four, pas les mêmes ustensiles, et ses ingrédients sont légèrement différents.
Docker, c'est comme si tu pouvais emballer ta cuisine entière (four, ustensiles, ingrédients, recette) dans une boîte portable. Ton ami n'a qu'à ouvrir la boîte, et il a exactement le même environnement que toi.
En termes techniques :
Docker est une plateforme de conteneurisation. Il permet d'empaqueter une application avec toutes ses dépendances (librairies, configurations, outils) dans un "conteneur" isolé et portable.
Avec Docker, tu peux :
-
Exécuter n'importe quelle application sans installer ses dépendances
-
Garantir que ton code fonctionne partout (dev, test, prod)
-
Isoler tes applications les unes des autres
-
Déployer en quelques secondes
-
Partager ton environnement avec ton équipe
Le problème que Docker résout
Avant Docker :
Développeur : "Ça marche sur ma machine !"
Ops : "Mais pas sur le serveur..."
Développeur : "Tu as quelle version de Python ?"
Ops : "3.8"
Développeur : "Ah, il faut 3.11... et tu as installé libpq-dev ?"
Ops : "C'est quoi ça ?"
[3 heures plus tard...]
Avec Docker :
Développeur : "Voici le conteneur"
Ops : "docker run... C'est en prod."
[30 secondes plus tard...]
Conteneur vs Machine virtuelle
Machine virtuelle (VM) :
┌─────────────────────────────────────┐
│ Ton Application │
├─────────────────────────────────────┤
│ Système d'exploitation │ ← OS complet (Windows, Linux...)
├─────────────────────────────────────┤
│ Hyperviseur │ ← VMware, VirtualBox, etc.
├─────────────────────────────────────┤
│ Système hôte (ton PC) │
└─────────────────────────────────────┘
-
Lourd (plusieurs Go par VM)
-
Lent à démarrer (minutes)
-
Isole complètement avec un OS entier
Conteneur Docker :
┌─────────────────────────────────────┐
│ Ton Application │
├─────────────────────────────────────┤
│ Librairies et dépendances │ ← Seulement ce qui est nécessaire
├─────────────────────────────────────┤
│ Docker Engine │
├─────────────────────────────────────┤
│ Système hôte (ton PC) │
└─────────────────────────────────────┘
-
Léger (quelques Mo à quelques centaines de Mo)
-
Démarre en secondes
-
Partage le kernel de l'hôte
Analogie :
-
VM = Maison entière avec fondations, murs, toit, jardin
-
Conteneur = Appartement dans un immeuble (partage les fondations)
Les concepts clés à comprendre
Image : Un template en lecture seule contenant tout ce qu'il faut pour exécuter une application. C'est comme une recette de cuisine ou un moule.
Conteneur : Une instance en cours d'exécution d'une image. C'est le gâteau fait à partir de la recette. Tu peux avoir plusieurs conteneurs à partir de la même image.
Dockerfile : Un fichier texte contenant les instructions pour construire une image. C'est la recette écrite étape par étape.
Docker Hub : Un registre public où trouver et partager des images. C'est comme GitHub mais pour les images Docker.
Volume : Un espace de stockage persistant. Les données dans un conteneur disparaissent quand il est supprimé, sauf si tu utilises un volume.
Réseau : Permet aux conteneurs de communiquer entre eux et avec l'extérieur.
Garde ces définitions en tête, on va les utiliser tout de suite en pratique !
Partie 2 : Installation
Installer Docker
Linux (Ubuntu/Debian) :
# Mettre à jour les paquets
sudo apt update
# Installer les prérequis
sudo apt install apt-transport-https ca-certificates curl software-properties-common
# Ajouter la clé GPG officielle de Docker
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Ajouter le repository Docker
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Installer Docker
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
# Vérifier l'installation
docker --version
Linux (Alma/Rocky/RHEL) :
# Installer les prérequis
sudo dnf install -y dnf-plugins-core
# Ajouter le repository Docker
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# Installer Docker
sudo dnf install docker-ce docker-ce-cli containerd.io
# Démarrer Docker
sudo systemctl start docker
sudo systemctl enable docker
# Vérifier
docker --version
macOS :
-
Télécharge Docker Desktop depuis https://docker.com
-
Installe le .dmg
-
Lance Docker Desktop
-
Vérifie dans le terminal :
docker --version
Windows :
-
Active WSL 2 (Windows Subsystem for Linux)
-
Télécharge Docker Desktop depuis https://docker.com
-
Installe et redémarre
-
Vérifie :
docker --version
Configuration post-installation (Linux)
Exécuter Docker sans sudo :
# Ajouter ton utilisateur au groupe docker
sudo usermod -aG docker $USER
# Appliquer les changements (déconnecte-toi et reconnecte-toi, ou)
newgrp docker
# Tester sans sudo
docker run hello-world
Vérifier que tout fonctionne
# Le test classique
docker run hello-world
Tu devrais voir :
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
Félicitations ! 🎉 Docker est installé et fonctionnel !
Partie 3 : Les commandes de base
Ton premier conteneur
# Lancer un conteneur Ubuntu
docker run ubuntu echo "Bonjour depuis Docker !"
# Que s'est-il passé ?
# 1. Docker a téléchargé l'image "ubuntu" (si pas en local)
# 2. Docker a créé un conteneur à partir de cette image
# 3. Docker a exécuté la commande "echo ..."
# 4. Le conteneur s'est arrêté
Mode interactif
# Lancer Ubuntu en mode interactif
docker run -it ubuntu bash
# -i : interactif (garde STDIN ouvert)
# -t : alloue un pseudo-TTY (terminal)
# Tu es maintenant DANS le conteneur !
root@abc123:/# ls
root@abc123:/# cat /etc/os-release
root@abc123:/# exit
Lister les conteneurs
# Conteneurs en cours d'exécution
docker ps
# Tous les conteneurs (y compris arrêtés)
docker ps -a
# Format personnalisé
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Status}}"
Gérer les conteneurs
# Lancer un conteneur en arrière-plan (détaché)
docker run -d --name mon-nginx nginx
# -d : détaché (en arrière-plan)
# --name : donner un nom au conteneur
# Voir les logs
docker logs mon-nginx
docker logs -f mon-nginx # -f : suivre en temps réel
# Arrêter un conteneur
docker stop mon-nginx
# Démarrer un conteneur arrêté
docker start mon-nginx
# Redémarrer
docker restart mon-nginx
# Supprimer un conteneur (doit être arrêté)
docker rm mon-nginx
# Supprimer un conteneur en cours d'exécution (force)
docker rm -f mon-nginx
Exécuter une commande dans un conteneur
# Lancer un conteneur
docker run -d --name mon-ubuntu ubuntu sleep infinity
# Exécuter une commande dedans
docker exec mon-ubuntu ls /
# Entrer dans le conteneur
docker exec -it mon-ubuntu bash
# Tu es dedans !
root@xyz789:/# apt update
root@xyz789:/# exit
Exposer des ports
# Lancer Nginx et exposer le port 80
docker run -d -p 8080:80 --name web nginx
# -p 8080:80 : mappe le port 8080 de ta machine au port 80 du conteneur
# Maintenant, ouvre http://localhost:8080 dans ton navigateur !
Format des ports :
-p [IP_HOTE:]PORT_HOTE:PORT_CONTENEUR
Exemples :
-p 8080:80 # localhost:8080 → conteneur:80
-p 127.0.0.1:8080:80 # Seulement accessible en local
-p 80:80 # Même port
Gérer les images
# Lister les images locales
docker images
# Télécharger une image (sans lancer de conteneur)
docker pull python:3.11
# Supprimer une image
docker rmi python:3.11
# Supprimer les images non utilisées
docker image prune
# Rechercher sur Docker Hub
docker search postgres
Nettoyer le système
# Supprimer tous les conteneurs arrêtés
docker container prune
# Supprimer toutes les images non utilisées
docker image prune
# Supprimer tout ce qui n'est pas utilisé (attention !)
docker system prune
# Voir l'espace utilisé
docker system df
Partie 4 : Les images Docker
Comprendre les tags
Une image a un nom et un tag (version) :
nom:tag
Exemples :
python:3.11
python:3.11-slim
python:latest
ubuntu:22.04
nginx:alpine
Tags courants :
-
latest: La dernière version (par défaut si pas de tag) -
alpine: Version ultra-légère basée sur Alpine Linux -
slim: Version allégée -
Version spécifique :
3.11,22.04, etc.
# Ces deux commandes sont identiques
docker pull nginx
docker pull nginx:latest
# Image légère
docker pull python:3.11-alpine # ~50 Mo vs ~1 Go pour la version complète
Conseil : En production, utilise toujours un tag précis, jamais latest !
Le Docker Hub
Docker Hub (hub.docker.com) est le registre public par défaut. Tu y trouveras :
-
Images officielles :
python,nginx,postgres,redis... -
Images vérifiées : De grandes entreprises (Microsoft, Oracle...)
-
Images communautaires : N'importe qui peut publier
# Rechercher des images
docker search flask
# Voir les détails sur hub.docker.com
# https://hub.docker.com/_/python
Créer ta propre image avec Dockerfile
Le Dockerfile est la recette pour construire ton image.
Structure de base :
# Image de base
FROM python:3.11-slim
# Métadonnées
LABEL maintainer="ton@email.com"
# Définir le répertoire de travail
WORKDIR /app
# Copier les fichiers
COPY requirements.txt .
# Exécuter des commandes
RUN pip install --no-cache-dir -r requirements.txt
# Copier le reste de l'application
COPY . .
# Exposer un port
EXPOSE 5000
# Commande par défaut
CMD ["python", "app.py"]
Exemple concret : Application Flask
Créons une structure de projet :
mon-app/
├── app.py
├── requirements.txt
└── Dockerfile
app.py :
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Bonjour depuis Docker ! 🐳"
@app.route("/health")
def health():
return {"status": "ok"}
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
requirements.txt :
flask==3.0.0
Dockerfile :
# Image de base Python
FROM python:3.11-slim
# Répertoire de travail
WORKDIR /app
# Copier et installer les dépendances
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copier le code
COPY app.py .
# Port exposé
EXPOSE 5000
# Commande de lancement
CMD ["python", "app.py"]
Construire l'image :
# Se placer dans le dossier du projet
cd mon-app
# Construire l'image
docker build -t mon-app-flask .
# -t : tag (nom de l'image)
# . : contexte de build (dossier courant)
# Vérifier
docker images | grep mon-app
Lancer le conteneur :
docker run -d -p 5000:5000 --name flask-app mon-app-flask
# Tester
curl http://localhost:5000
# Bonjour depuis Docker ! 🐳
Les instructions Dockerfile essentielles
FROM : Image de base
FROM python:3.11-slim
FROM node:20-alpine
FROM ubuntu:22.04
WORKDIR : Répertoire de travail
WORKDIR /app
# Toutes les commandes suivantes s'exécutent dans /app
COPY : Copier des fichiers
COPY fichier.txt /app/
COPY . /app/
COPY --chown=user:group fichier.txt /app/
ADD : Comme COPY, mais avec des super-pouvoirs
ADD archive.tar.gz /app/ # Extrait automatiquement
ADD https://example.com/file /app/ # Télécharge depuis une URL
RUN : Exécuter une commande (pendant le build)
RUN apt-get update && apt-get install -y curl
RUN pip install flask
CMD : Commande par défaut (au lancement)
CMD ["python", "app.py"]
CMD ["npm", "start"]
ENTRYPOINT : Point d'entrée (plus strict que CMD)
ENTRYPOINT ["python"]
CMD ["app.py"]
# Résultat : python app.py
# docker run mon-image script.py → python script.py
ENV : Variables d'environnement
ENV FLASK_ENV=production
ENV DATABASE_URL=postgres://localhost/db
ARG : Arguments de build
ARG VERSION=1.0
RUN echo "Version: $VERSION"
# docker build --build-arg VERSION=2.0 .
EXPOSE : Documenter les ports
EXPOSE 5000
EXPOSE 80 443
USER : Changer d'utilisateur
RUN useradd -m appuser
USER appuser
VOLUME : Déclarer un volume
VOLUME /data
Multi-stage builds (builds multi-étapes)
Pour des images plus légères, utilise les builds multi-étapes :
# Étape 1 : Build
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Étape 2 : Production
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Avantages :
-
L'image finale ne contient pas les outils de build
-
Image beaucoup plus légère
-
Meilleure sécurité
Partie 5 : Les volumes (persistance des données)
Le problème
Par défaut, les données dans un conteneur sont éphémères. Quand le conteneur est supprimé, tout est perdu.
# Créer un fichier dans un conteneur
docker run -it --name test ubuntu bash
root@abc:/# echo "Hello" > /data.txt
root@abc:/# exit
# Supprimer et recréer
docker rm test
docker run -it --name test ubuntu bash
root@xyz:/# cat /data.txt
# cat: /data.txt: No such file or directory
Les volumes Docker
# Créer un volume
docker volume create mes-donnees
# Lister les volumes
docker volume ls
# Utiliser un volume
docker run -d -v mes-donnees:/app/data --name app ubuntu sleep infinity
# -v nom-volume:chemin-dans-conteneur
# Les données persistent même si le conteneur est supprimé !
Bind mounts (montage de dossiers)
Monte un dossier de ta machine dans le conteneur :
# Monter le dossier courant
docker run -v $(pwd):/app -w /app python:3.11 python script.py
# -v chemin-hôte:chemin-conteneur
# -w : définir le répertoire de travail
# Exemple pratique : développement avec rechargement automatique
docker run -d -p 5000:5000 -v $(pwd):/app mon-app-flask
Utilisation courante : Développement
# Ton code est monté dans le conteneur
# Chaque modification est immédiatement visible
docker run -d \
-p 5000:5000 \
-v $(pwd):/app \
-e FLASK_DEBUG=1 \
mon-app-flask
Volumes vs Bind mounts
| Aspect | Volume | Bind mount |
|---|---|---|
| Gestion | Par Docker | Par toi |
| Emplacement | /var/lib/docker/volumes | N'importe où |
| Backup | Via Docker | Directement |
| Performance | Optimisé | Dépend du système |
| Cas d'usage | Production, données | Développement, config |
Exemples pratiques
Base de données PostgreSQL :
# Les données survivent au redémarrage
docker run -d \
--name postgres \
-e POSTGRES_PASSWORD=secret \
-v postgres-data:/var/lib/postgresql/data \
-p 5432:5432 \
postgres:15
Partager des données entre conteneurs :
# Créer un volume
docker volume create shared-data
# Conteneur 1 : écrit
docker run -v shared-data:/data alpine sh -c "echo 'Hello' > /data/message.txt"
# Conteneur 2 : lit
docker run -v shared-data:/data alpine cat /data/message.txt
# Hello
Partie 6 : Les réseaux Docker
Les types de réseaux
# Lister les réseaux
docker network ls
# Par défaut, tu as :
# - bridge : réseau par défaut pour les conteneurs
# - host : partage le réseau de l'hôte
# - none : pas de réseau
Communication entre conteneurs
Méthode 1 : Réseau bridge par défaut (par IP)
# Lancer deux conteneurs
docker run -d --name web nginx
docker run -d --name app python:3.11 sleep infinity
# Trouver l'IP
docker inspect web | grep IPAddress
# Depuis app, accéder à web par IP
docker exec app curl http://172.17.0.2
Méthode 2 : Réseau personnalisé (par nom) - RECOMMANDÉ
# Créer un réseau
docker network create mon-reseau
# Lancer les conteneurs sur ce réseau
docker run -d --name web --network mon-reseau nginx
docker run -d --name app --network mon-reseau python:3.11 sleep infinity
# Depuis app, accéder à web PAR SON NOM
docker exec app curl http://web
# Ça marche ! Docker fait la résolution DNS automatiquement
Exemple : Application avec base de données
# Créer le réseau
docker network create app-network
# Lancer PostgreSQL
docker run -d \
--name db \
--network app-network \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=myapp \
postgres:15
# Lancer l'application (qui se connecte à "db")
docker run -d \
--name web \
--network app-network \
-p 5000:5000 \
-e DATABASE_URL=postgresql://postgres:secret@db:5432/myapp \
mon-app
L'application peut accéder à la base de données via db:5432 !
Partie 7 : Docker Compose (le game changer)
C'est quoi Docker Compose ?
Docker Compose permet de définir et gérer des applications multi-conteneurs avec un fichier YAML.
Avant (sans Compose) :
docker network create app-net
docker volume create db-data
docker run -d --name db --network app-net -v db-data:/var/lib/postgresql/data -e POSTGRES_PASSWORD=secret postgres:15
docker run -d --name redis --network app-net redis:alpine
docker run -d --name web --network app-net -p 5000:5000 -e DATABASE_URL=... mon-app
Après (avec Compose) :
docker compose up -d
Installation de Docker Compose
Docker Compose v2 est inclus avec Docker Desktop. Sur Linux :
# Vérifier si installé
docker compose version
# Si pas installé
sudo apt install docker-compose-plugin
Le fichier docker-compose.yml
Structure de base :
version: "3.8"
services:
web:
image: nginx
ports:
- "80:80"
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
Exemple complet : Application Flask + PostgreSQL + Redis
Structure du projet :
mon-projet/
├── app/
│ ├── app.py
│ └── requirements.txt
├── Dockerfile
└── docker-compose.yml
app/app.py :
from flask import Flask
import redis
import psycopg2
import os
app = Flask(__name__)
# Connexion Redis
cache = redis.Redis(host='redis', port=6379)
@app.route("/")
def hello():
# Incrémenter le compteur de visites
visits = cache.incr('visits')
return f"Bonjour ! Cette page a été vue {visits} fois."
@app.route("/health")
def health():
return {"status": "ok", "services": {"redis": "up", "db": "up"}}
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)
app/requirements.txt :
flask==3.0.0
redis==5.0.0
psycopg2-binary==2.9.9
Dockerfile :
FROM python:3.11-slim
WORKDIR /app
COPY app/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ .
EXPOSE 5000
CMD ["python", "app.py"]
docker-compose.yml :
version: "3.8"
services:
web:
build: .
ports:
- "5000:5000"
environment:
- FLASK_DEBUG=1
- DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
volumes:
- ./app:/app
depends_on:
- db
- redis
restart: unless-stopped
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
- postgres-data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:alpine
restart: unless-stopped
volumes:
postgres-data:
Les commandes Docker Compose
# Lancer tous les services
docker compose up
# En arrière-plan
docker compose up -d
# Voir les logs
docker compose logs
docker compose logs -f web # Suivre les logs du service "web"
# Voir le statut
docker compose ps
# Arrêter
docker compose stop
# Arrêter et supprimer
docker compose down
# Supprimer aussi les volumes (ATTENTION : perte de données)
docker compose down -v
# Reconstruire les images
docker compose build
docker compose up --build
# Exécuter une commande dans un service
docker compose exec web bash
docker compose exec db psql -U postgres
# Scaler un service (plusieurs instances)
docker compose up -d --scale web=3
Les options du docker-compose.yml
build : Construire depuis un Dockerfile
services:
web:
build: . # Dockerfile dans le dossier courant
# Ou avec options
build:
context: ./app
dockerfile: Dockerfile.prod
args:
VERSION: 1.0
image : Utiliser une image existante
services:
db:
image: postgres:15-alpine
ports : Exposer des ports
services:
web:
ports:
- "5000:5000" # host:container
- "127.0.0.1:80:80" # Seulement en local
volumes : Monter des volumes
services:
web:
volumes:
- ./code:/app # Bind mount
- data-volume:/app/data # Volume nommé
volumes:
data-volume:
environment : Variables d'environnement
services:
web:
environment:
- DEBUG=1
- DATABASE_URL=postgresql://...
# Ou depuis un fichier
env_file:
- .env
depends_on : Ordre de démarrage
services:
web:
depends_on:
- db
- redis
db:
image: postgres:15
redis:
image: redis:alpine
restart : Politique de redémarrage
services:
web:
restart: unless-stopped # Redémarre sauf si arrêté manuellement
# Autres options : no, always, on-failure
healthcheck : Vérifier la santé
services:
web:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 30s
timeout: 10s
retries: 3
networks : Réseaux personnalisés
services:
web:
networks:
- frontend
- backend
db:
networks:
- backend
networks:
frontend:
backend:
Fichier .env avec Docker Compose
docker-compose.yml :
services:
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
.env :
DB_PASSWORD=super_secret_password
# Docker Compose charge automatiquement .env
docker compose up -d
Partie 8 : Bonnes pratiques
1. Images légères
# MAUVAIS : Image complète (1+ Go)
FROM python:3.11
# BON : Image slim (~150 Mo)
FROM python:3.11-slim
# ENCORE MIEUX : Alpine (~50 Mo)
FROM python:3.11-alpine
2. Ordonne tes instructions (cache)
Docker met en cache chaque instruction. Mets les instructions qui changent rarement en premier.
# MAUVAIS : Réinstalle les dépendances à chaque changement de code
FROM python:3.11-slim
COPY . /app
RUN pip install -r /app/requirements.txt
# BON : Les dépendances sont en cache
FROM python:3.11-slim
COPY requirements.txt /app/
RUN pip install -r /app/requirements.txt
COPY . /app
3. Un processus par conteneur
# MAUVAIS : Tout dans un conteneur
services:
monolithe:
# web + db + cache + queue...
# BON : Un service par conteneur
services:
web:
image: mon-app
db:
image: postgres
redis:
image: redis
worker:
image: mon-worker
4. Ne pas tourner en root
# Créer un utilisateur non-root
FROM python:3.11-slim
RUN useradd -m -r appuser && \
mkdir /app && \
chown appuser:appuser /app
WORKDIR /app
USER appuser
COPY --chown=appuser:appuser . .
5. Utilise .dockerignore
Comme .gitignore, mais pour Docker :
.dockerignore :
.git
.gitignore
__pycache__
*.pyc
.env
.env.local
venv/
node_modules/
.pytest_cache/
*.md
docker-compose*.yml
Dockerfile*
6. Tags explicites
# MAUVAIS : Quelle version ?
FROM python:latest
# BON : Version précise
FROM python:3.11.6-slim-bookworm
7. Variables d'environnement pour la configuration
# Permettre la configuration sans reconstruire
ENV FLASK_ENV=production
ENV PORT=5000
CMD ["python", "app.py"]
# Overrider au lancement
docker run -e FLASK_ENV=development -e PORT=8080 mon-app
8. Healthchecks
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
9. Multi-stage pour la production
# Build
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/index.js"]
10. Logs sur stdout/stderr
# Les logs doivent aller sur stdout, pas dans des fichiers
import logging
import sys
logging.basicConfig(
stream=sys.stdout,
level=logging.INFO
)
Docker capture automatiquement stdout/stderr avec docker logs.
Partie 9 : Debugging et dépannage
Voir ce qui se passe
# Logs d'un conteneur
docker logs mon-conteneur
docker logs -f mon-conteneur # Suivre en temps réel
docker logs --tail 100 mon-conteneur # 100 dernières lignes
# Statistiques en temps réel
docker stats
# Inspecter un conteneur
docker inspect mon-conteneur
# Voir les processus dans un conteneur
docker top mon-conteneur
Entrer dans un conteneur
# Conteneur en cours d'exécution
docker exec -it mon-conteneur bash
docker exec -it mon-conteneur sh # Si pas de bash
# Démarrer un conteneur arrêté en mode debug
docker run -it --entrypoint bash mon-image
Problèmes courants
"port is already allocated"
# Trouver ce qui utilise le port
sudo lsof -i :5000
# ou
docker ps # Un autre conteneur ?
# Solution : utiliser un autre port
docker run -p 5001:5000 mon-app
"no space left on device"
# Voir l'espace utilisé
docker system df
# Nettoyer
docker system prune -a
# Supprimer les volumes orphelins
docker volume prune
"container exited immediately"
# Voir les logs
docker logs mon-conteneur
# Le conteneur a besoin d'un processus qui tourne
# MAUVAIS :
CMD ["echo", "hello"] # Se termine immédiatement
# BON :
CMD ["python", "app.py"] # Processus qui reste actif
"cannot connect to database"
# Vérifier que les conteneurs sont sur le même réseau
docker network inspect mon-reseau
# Vérifier que le service est prêt
docker compose logs db
# Utiliser depends_on avec condition
services:
web:
depends_on:
db:
condition: service_healthy
Debug avec Docker Compose
# Voir tous les logs
docker compose logs
# Logs d'un service spécifique
docker compose logs -f web
# Reconstruire et relancer
docker compose up --build
# Voir la configuration finale (variables résolues)
docker compose config
Partie 10 : Déploiement en production
Construire pour la production
# Dockerfile.prod
FROM python:3.11-slim
# Métadonnées
LABEL maintainer="toi@email.com"
LABEL version="1.0"
# Variables d'environnement de production
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
# Utilisateur non-root
RUN useradd -m -r appuser
WORKDIR /app
# Dépendances
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Code
COPY --chown=appuser:appuser . .
USER appuser
EXPOSE 5000
# Healthcheck
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
# Gunicorn au lieu du serveur de développement Flask
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]
Docker Compose pour la production
docker-compose.prod.yml :
version: "3.8"
services:
web:
build:
context: .
dockerfile: Dockerfile.prod
ports:
- "5000:5000"
environment:
- FLASK_ENV=production
- DATABASE_URL=${DATABASE_URL}
restart: always
deploy:
replicas: 2
resources:
limits:
cpus: "0.5"
memory: 512M
db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
restart: always
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web
restart: always
volumes:
postgres-data:
Pousse vers un registre
# Se connecter à Docker Hub
docker login
# Taguer l'image
docker tag mon-app:latest ton-username/mon-app:1.0
# Pousser
docker push ton-username/mon-app:1.0
# Sur le serveur de production
docker pull ton-username/mon-app:1.0
docker run -d -p 5000:5000 ton-username/mon-app:1.0
Registres privés
# GitHub Container Registry
docker login ghcr.io
docker tag mon-app ghcr.io/ton-username/mon-app:1.0
docker push ghcr.io/ton-username/mon-app:1.0
# GitLab Container Registry
docker login registry.gitlab.com
docker tag mon-app registry.gitlab.com/ton-groupe/mon-projet:1.0
docker push registry.gitlab.com/ton-groupe/mon-projet:1.0
Cheat Sheet : Commandes essentielles
Conteneurs
docker run IMAGE # Lancer un conteneur
docker run -d IMAGE # En arrière-plan
docker run -it IMAGE bash # Mode interactif
docker run -p 8080:80 IMAGE # Exposer un port
docker run -v /host:/container IMAGE # Monter un volume
docker run --name NOM IMAGE # Nommer le conteneur
docker run -e VAR=valeur IMAGE # Variable d'environnement
docker run --rm IMAGE # Supprimer après arrêt
docker ps # Conteneurs actifs
docker ps -a # Tous les conteneurs
docker stop CONTENEUR # Arrêter
docker start CONTENEUR # Démarrer
docker restart CONTENEUR # Redémarrer
docker rm CONTENEUR # Supprimer
docker rm -f CONTENEUR # Forcer la suppression
docker logs CONTENEUR # Voir les logs
docker logs -f CONTENEUR # Suivre les logs
docker exec -it CONTENEUR bash # Entrer dans le conteneur
docker inspect CONTENEUR # Détails complets
Images
docker images # Lister les images
docker pull IMAGE:TAG # Télécharger
docker build -t NOM . # Construire depuis Dockerfile
docker tag IMAGE NOM:TAG # Taguer
docker push NOM:TAG # Pousser vers registre
docker rmi IMAGE # Supprimer
docker image prune # Nettoyer les images inutilisées
Volumes
docker volume ls # Lister
docker volume create NOM # Créer
docker volume rm NOM # Supprimer
docker volume prune # Nettoyer
Réseaux
docker network ls # Lister
docker network create NOM # Créer
docker network connect NET CONT # Connecter un conteneur
docker network inspect NOM # Détails
Docker Compose
docker compose up # Lancer
docker compose up -d # En arrière-plan
docker compose up --build # Reconstruire et lancer
docker compose down # Arrêter et supprimer
docker compose down -v # + supprimer les volumes
docker compose ps # Statut
docker compose logs # Logs
docker compose logs -f SERVICE # Suivre les logs d'un service
docker compose exec SERVICE bash # Entrer dans un service
docker compose build # Construire les images
docker compose pull # Mettre à jour les images
Nettoyage
docker system df # Espace utilisé
docker system prune # Nettoyer tout
docker system prune -a # Tout, y compris images non utilisées
docker container prune # Conteneurs arrêtés
docker image prune # Images orphelines
docker volume prune # Volumes orphelins
Conclusion
Félicitations ! Tu connais maintenant Docker et ses fondamentaux.
Ce qu'on a couvert :
-
Les concepts de base (images, conteneurs, volumes, réseaux)
-
Les commandes essentielles
-
Créer tes propres images avec Dockerfile
-
Docker Compose pour les applications multi-conteneurs
-
Les bonnes pratiques de production
-
Le debugging et dépannage
Les commandements du Docker-fu :
-
Un processus par conteneur
-
Images légères (alpine, slim)
-
Ne pas tourner en root
-
Tags explicites, jamais
latesten prod -
Utilise .dockerignore
-
Ordonne tes instructions pour le cache
-
Variables d'environnement pour la config
-
Volumes pour les données persistantes
-
Réseaux personnalisés pour la communication
- Docker Compose pour tout projet sérieux
Rappelle-toi :
-
Docker simplifie énormément le déploiement
-
"Ça marche sur ma machine" n'est plus une excuse
-
Commence simple, complexifie progressivement
-
La documentation officielle est excellente
Prochaines étapes :
-
Dockerise un de tes projets existants
-
Crée un docker-compose.yml pour ta stack
-
Explore Docker Swarm ou Kubernetes pour l'orchestration
-
Mets en place un pipeline CI/CD avec Docker
-
Pratique, pratique, pratique !
Docker, c'est comme le vélo : au début on tombe, puis on ne peut plus s'en passer. Dans quelques semaines, tu ne pourras plus imaginer développer sans conteneurs.
Tu as des questions sur Docker ? Un conteneur qui refuse de démarrer ? Partage en commentaire ! Et si ce guide t'a aidé, partage-le avec d'autres devs qui veulent enfin comprendre les conteneurs.

Laisser un commentaire