Cours Docker Complet 🐳
Les 12 chapitres essentiels — conteneurs, images, Compose, volumes, réseau et déploiement
🏠 Hub Programmation
🔀 Git & GitHub
🟢 Node.js
🐍 Python
🔌 APIs REST
Introduction et installation
# → https://www.docker.com/products/docker-desktop/
# macOS, Windows, Linux — interface graphique + CLI
# Vérifier l'installation
docker –version # Docker version 27.x
docker compose version # Docker Compose v2.x
# Tester avec le conteneur hello-world
docker run hello-world
# → Télécharge l'image, crée un conteneur, exécute, affiche un message
Docker empaquette une application avec toutes ses dépendances dans un conteneur — un environnement isolé, léger et portable. « Ça marche sur ma machine » → « Ça marche partout ». Créé par Solomon Hykes en 2013, Docker est devenu le standard de déploiement. Utilisé par Google, Netflix, Spotify, Uber, PayPal et 90% des entreprises tech.
| Aspect | Conteneur Docker | Machine Virtuelle |
|---|---|---|
| Démarrage | Secondes | Minutes |
| Taille | Mo (image Alpine = 5 Mo) | Go (OS complet) |
| Isolation | Partage le kernel de l'hôte | OS complet séparé |
| Performance | Quasi-native (pas d'hyperviseur) | Overhead de virtualisation |
| Densité | Dizaines de conteneurs par serveur | Quelques VMs |
| Portabilité | Image identique partout | Format VM variable |
Un conteneur n'est PAS une mini VM. C'est un processus Linux isolé grâce aux namespaces (isolation) et cgroups (limites ressources). Il partage le kernel de l'hôte — c'est pour ça qu'il est si léger et rapide.
Premiers conteneurs
docker run nginx # En premier plan (bloquant)
docker run -d nginx # Détaché (arrière-plan)
docker run -d –name mon-nginx nginx # Avec un nom
# Mapper un port → accès depuis le navigateur
docker run -d -p 8080:80 nginx
# → http://localhost:8080 affiche la page Nginx
# Format : -p PORT_HOTE:PORT_CONTENEUR
# Lancer un shell interactif dans un conteneur
docker run -it ubuntu bash
# -i = interactif (garde stdin ouvert)
# -t = pseudo-terminal (TTY)
# Exécuter dans un conteneur en cours
docker exec -it mon-nginx bash
docker exec mon-nginx cat /etc/nginx/nginx.conf
docker ps # Conteneurs en cours
docker ps -a # Tous (y compris arrêtés)
# Arrêter / Démarrer / Redémarrer
docker stop mon-nginx # Arrêt gracieux (SIGTERM)
docker kill mon-nginx # Arrêt immédiat (SIGKILL)
docker start mon-nginx # Redémarrer un conteneur arrêté
docker restart mon-nginx
# Logs
docker logs mon-nginx # Tous les logs
docker logs -f mon-nginx # Suivre en temps réel (tail -f)
docker logs –tail 50 mon-nginx # 50 dernières lignes
# Inspecter
docker inspect mon-nginx # JSON complet (IP, volumes, config)
docker stats # CPU, RAM, réseau en temps réel
# Supprimer
docker rm mon-nginx # Supprimer un conteneur arrêté
docker rm -f mon-nginx # Forcer (même en cours)
# Nettoyage global
docker system prune -a # Tout supprimer (conteneurs, images, cache)
Les conteneurs sont éphémères par défaut. Quand vous supprimez un conteneur, toutes les données écrites dedans sont perdues. Pour persister des données, utilisez des volumes (chapitre 5).
Images
docker pull node:20-alpine # Node.js 20 sur Alpine Linux (léger)
docker pull postgres:16 # PostgreSQL 16
docker pull python:3.12-slim # Python 3.12 (version slim)
# Lister les images locales
docker images
docker image ls
# Supprimer une image
docker rmi node:20-alpine
docker image prune # Supprimer les images inutilisées
| Tag | Description | Taille |
|---|---|---|
| node:20 | Image complète (Debian) | ~1 Go |
| node:20-slim | Debian allégée | ~200 Mo |
| node:20-alpine | Alpine Linux (recommandé) | ~50 Mo |
| python:3.12 | Image complète | ~1 Go |
| python:3.12-slim | Slim (recommandé Python) | ~150 Mo |
| python:3.12-alpine | Alpine (⚠️ problèmes avec certains packages) | ~50 Mo |
Utilisez toujours un tag précis (node:20-alpine) au lieu de node:latest. Le tag latest change à chaque mise à jour — votre build pourrait casser un jour sans raison. Alpine est recommandé pour sa légèreté sauf pour Python (préférer slim à cause de la compilation de packages C).
Dockerfile
# 1. Image de base
FROM node:20-alpine
# 2. Dossier de travail dans le conteneur
WORKDIR /app
# 3. Copier les fichiers de dépendances EN PREMIER (cache Docker)
COPY package*.json ./
# 4. Installer les dépendances
RUN npm ci –only=production
# 5. Copier le reste du code
COPY . .
# 6. Port exposé (documentation)
EXPOSE 3000
# 7. Utilisateur non-root (sécurité)
USER node
# 8. Commande de démarrage
CMD [« node », « server.js »]
docker build -t mon-app:1.0 .
# Lancer
docker run -d -p 3000:3000 –name api mon-app:1.0
# .dockerignore — ne pas copier dans l'image
node_modules
.git
.env
npm-debug.log
Dockerfile
docker-compose.yml
L'ordre des instructions est crucial pour le cache. Docker met en cache chaque couche (layer). Si un fichier change, Docker reconstruit à partir de cette couche. Copiez package.json AVANT le code source : les dépendances ne sont réinstallées que si package.json change. Le code source change souvent → dernière couche.
WORKDIR /app
# Empêcher Python de bufferiser stdout (logs en temps réel)
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# Dépendances système si nécessaires
RUN apt-get update && apt-get install -y –no-install-recommends \
gcc libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# Dépendances Python (cache)
COPY requirements.txt .
RUN pip install –no-cache-dir -r requirements.txt
# Code
COPY . .
EXPOSE 8000
CMD [« gunicorn », « app:create_app() », « –bind », « 0.0.0.0:8000 »]
| Instruction | Rôle | Exemple |
|---|---|---|
| FROM | Image de base (obligatoire, 1ère ligne) | FROM node:20-alpine |
| WORKDIR | Dossier de travail | WORKDIR /app |
| COPY | Copier des fichiers hôte → image | COPY . . |
| RUN | Exécuter pendant le build | RUN npm ci |
| CMD | Commande par défaut au démarrage | CMD [« node », « app.js »] |
| ENTRYPOINT | Commande fixe (non remplaçable) | ENTRYPOINT [« python »] |
| ENV | Variable d'environnement | ENV NODE_ENV=production |
| EXPOSE | Port documenté (informatif) | EXPOSE 3000 |
| USER | Utilisateur non-root (sécurité) | USER node |
| ARG | Variable de build (pas disponible au runtime) | ARG NODE_VERSION=20 |
Volumes et persistance
docker run -d \
-v pgdata:/var/lib/postgresql/data \
postgres:16
# Docker stocke les données dans /var/lib/docker/volumes/pgdata/
# 2. Bind Mount — dossier hôte → conteneur (développement)
docker run -d \
-v $(pwd)/src:/app/src \
-p 3000:3000 \
mon-app
# Modifications locales → visibles dans le conteneur (hot reload)
# 3. tmpfs — en mémoire (données temporaires)
docker run -d \
–tmpfs /tmp \
mon-app
| Type | Syntaxe | Usage |
|---|---|---|
| Named Volume | -v pgdata:/data | BDD production, données persistantes |
| Bind Mount | -v ./src:/app/src | Développement, hot reload |
| tmpfs | –tmpfs /tmp | Données temporaires, secrets |
docker volume ls # Lister
docker volume inspect pgdata # Détails
docker volume rm pgdata # Supprimer
docker volume prune # Supprimer les orphelins
Réseau
docker network create mon-reseau
# Lancer des conteneurs sur le même réseau
docker run -d –name db \
–network mon-reseau \
-e POSTGRES_PASSWORD=secret \
postgres:16
docker run -d –name api \
–network mon-reseau \
-p 3000:3000 \
-e DATABASE_URL=postgresql://postgres:secret@db:5432/postgres \
mon-app
# « db » est résolu automatiquement en IP par Docker DNS
# → Les conteneurs se parlent par leur NOM
# Lister les réseaux
docker network ls
docker network inspect mon-reseau
Sur un réseau Docker custom, les conteneurs se découvrent par leur nom. C'est la résolution DNS intégrée de Docker. Le réseau par défaut (bridge) ne supporte PAS la résolution DNS — utilisez toujours un réseau custom. Docker Compose crée automatiquement un réseau pour vos services.
Dockerfile avancé
# Étape 1 : Build (grosse image avec outils de compilation)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Étape 2 : Production (image légère, seulement le résultat)
FROM node:20-alpine
WORKDIR /app
COPY –from=builder /app/dist ./dist
COPY –from=builder /app/node_modules ./node_modules
COPY package*.json ./
USER node
EXPOSE 3000
CMD [« node », « dist/server.js »]
# Résultat : image finale ne contient PAS le code source,
# les devDependencies, ni les outils de build
Le multi-stage divise le build en étapes. L'image finale ne contient que le strict nécessaire — pas de compilateurs, pas de devDependencies, pas de code source. Résultat : images 2-10x plus petites, surface d'attaque réduite, déploiement plus rapide.
HEALTHCHECK –interval=30s –timeout=5s –retries=3 \
CMD wget –quiet –tries=1 –spider http://localhost:3000/health || exit 1
# Ou avec curl
HEALTHCHECK –interval=30s –timeout=5s –retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Vérifier le statut
docker ps # → affiche « healthy » ou « unhealthy »
Docker Compose
services:
# Application Node.js
api:
build: .
ports:
– « 3000:3000 »
environment:
– DATABASE_URL=postgresql://postgres:secret@db:5432/mydb
– REDIS_URL=redis://cache:6379
– NODE_ENV=production
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
restart: unless-stopped
# Base de données PostgreSQL
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: mydb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
volumes:
– pgdata:/var/lib/postgresql/data
ports:
– « 5432:5432 »
healthcheck:
test: [« CMD-SHELL », « pg_isready -U postgres »]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Cache Redis
cache:
image: redis:7-alpine
ports:
– « 6379:6379 »
restart: unless-stopped
volumes:
pgdata:
docker compose up -d # Démarrer tout en arrière-plan
docker compose up -d –build # Reconstruire les images avant
docker compose down # Arrêter et supprimer les conteneurs
docker compose down -v # + supprimer les volumes
docker compose logs -f api # Logs d'un service
docker compose ps # Statut des services
docker compose exec api sh # Shell dans un service
docker compose restart api # Redémarrer un service
Docker Compose définit toute votre stack dans un seul fichier YAML. Un seul docker compose up lance l'app, la BDD, le cache, le reverse proxy — tout configuré et connecté. Compose crée automatiquement un réseau entre vos services : db est résolu en IP depuis le service api.
Compose avancé
services:
api:
build: .
ports:
– « 3000:3000 »
depends_on:
– db
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
volumes:
– pgdata:/var/lib/postgresql/data
volumes:
pgdata:
services:
api:
build:
target: builder # Stage de dev (avec devDeps)
volumes:
– ./src:/app/src # Hot reload
environment:
– NODE_ENV=development
command: npm run dev # Commande de dev (nodemon)
db:
ports:
– « 5432:5432 » # Exposer pour les outils locaux
services:
api:
restart: always
environment:
– NODE_ENV=production
deploy:
resources:
limits:
cpus: « 1.0 »
memory: 512M
# Lancer en prod
docker compose -f compose.yml -f compose.prod.yml up -d
POSTGRES_PASSWORD=mon_secret_123
NODE_ENV=production
API_PORT=3000
# compose.yml
services:
api:
ports:
– « $:3000 »
env_file:
– .env # Injecter toutes les variables
Ne commitez JAMAIS le fichier .env avec des secrets. Ajoutez .env dans .gitignore. Fournissez un .env.example avec des valeurs factices pour documenter les variables nécessaires. En production, utilisez les secrets de votre plateforme (Docker Secrets, AWS SSM, Vault).
Registres et CI/CD
docker login
docker build -t monuser/mon-app:1.0 .
docker push monuser/mon-app:1.0
# GitHub Container Registry (ghcr.io)
docker login ghcr.io -u USERNAME
docker build -t ghcr.io/monuser/mon-app:1.0 .
docker push ghcr.io/monuser/mon-app:1.0
# Taguer une image
docker tag mon-app:1.0 monuser/mon-app:latest
docker tag mon-app:1.0 monuser/mon-app:1.0
name: Build & Push Docker
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
– uses: actions/checkout@v4
– name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: $
password: $
– name: Build and push
uses: docker/build-push-action@v6
with:
push: true
tags: monuser/mon-app:latest,monuser/mon-app:$
Production et orchestration
| Plateforme | Complexité | Idéal pour |
|---|---|---|
| Docker Compose + VPS | Simple | Side projects, petites apps |
| Railway / Render / Fly.io | Simple | PaaS avec support Docker natif |
| AWS ECS | Moyen | Production AWS, auto-scaling |
| Google Cloud Run | Simple | Serverless conteneurisé, pay-per-use |
| Kubernetes (K8s) | Complexe | Grande échelle, multi-services, enterprise |
Kubernetes n'est nécessaire que pour les grandes équipes avec beaucoup de services. Pour la plupart des projets, Docker Compose sur un VPS (avec un reverse proxy Traefik ou Caddy) suffit. Ne sur-ingéniérez pas. Cloud Run et Fly.io sont d'excellentes alternatives managed.
USER node # Ne pas exécuter en root
# 2. Image minimale
FROM node:20-alpine # Moins de packages = moins de vulnérabilités
# 3. Scanner les vulnérabilités
docker scout cves mon-app:1.0
# 4. Ne pas stocker de secrets dans l'image
# ❌ ENV API_KEY=secret123
# ✅ Passer au runtime : docker run -e API_KEY=$API_KEY
# 5. Read-only filesystem
docker run –read-only –tmpfs /tmp mon-app
Bonnes pratiques
| Action | Commande |
|---|---|
| Lancer un conteneur | docker run -d -p 8080:80 –name web nginx |
| Shell dans un conteneur | docker exec -it web sh |
| Voir les logs | docker logs -f web |
| Construire une image | docker build -t app:1.0 . |
| Lancer la stack Compose | docker compose up -d –build |
| Arrêter la stack | docker compose down |
| Tout nettoyer | docker system prune -a |
| Pousser une image | docker push user/app:1.0 |
✅ À FAIRE
• Tags précis (node:20-alpine, pas latest)
• Multi-stage builds (images légères)
• .dockerignore (exclure node_modules, .git)
• USER node (non-root)
• Copier package.json AVANT le code (cache)
• docker compose pour le dev local
• Healthchecks dans le Dockerfile
• Named volumes pour les BDD
• Scanner les vulnérabilités (docker scout)
• .env.example versionné (pas .env)
❌ À ÉVITER
• FROM node:latest (imprévisible)
• Exécuter en root dans le conteneur
• Secrets dans le Dockerfile (ENV API_KEY=…)
• Ignorer le .dockerignore
• docker compose down -v en prod (perte de données)
• Images inutilement grosses (utiliser alpine/slim)
• Tout dans un seul conteneur (1 service = 1 conteneur)
• npm install au lieu de npm ci (build non reproductible)
• Pas de logs (docker logs doit fonctionner)
• Kubernetes pour un side project
🏠 Hub Programmation
🔀 Cours Git & GitHub
🟢 Cours Node.js
🐍 Cours Python
🔌 Cours APIs REST
☁️ AWS Cloud Practitioner
Cours Docker Complet — Conteneurs, images, Compose, volumes, réseau et déploiement
Référence : docs.docker.com | Docker Hub | Compose

