Bien démarrer avec Docker : guide pour débutants
Docker est un outil qui permet d’exécuter des applications dans des conteneurs : des environnements isolés, reproductibles et légers. L’objectif de ce guide est de vous donner une compréhension solide (pas seulement des recettes) et de vous faire pratiquer avec de vraies commandes.
1) Pourquoi Docker ? Le problème que Docker résout
Avant Docker, déployer une application signifiait souvent :
- installer des dépendances spécifiques sur une machine (versions de bibliothèques, runtimes, paquets système),
- reproduire la même configuration entre développement, test et production,
- gérer des conflits entre applications (ex. deux versions de Python, Node, Java, etc.),
- “ça marche sur ma machine” devenait une phrase fréquente.
Docker apporte :
- Reproductibilité : l’application et ses dépendances sont empaquetées.
- Isolation : un conteneur n’écrase pas l’environnement d’un autre.
- Portabilité : un conteneur tourne de la même manière sur une machine compatible Docker.
- Rapidité : démarrage plus rapide qu’une machine virtuelle, car on partage le noyau du système hôte.
2) Concepts essentiels : image, conteneur, registre
Image Docker
Une image est un modèle immuable (un “snapshot” de système de fichiers + métadonnées) qui contient :
- un système minimal (souvent basé sur Debian, Alpine, Ubuntu…),
- votre application,
- ses dépendances,
- une commande par défaut à exécuter.
On peut voir une image comme une recette prête à l’emploi.
Commandes utiles :
docker images
docker image ls
Conteneur Docker
Un conteneur est une instance en cours d’exécution (ou arrêtée) d’une image. Il ajoute une couche en écriture au-dessus de l’image (vos fichiers temporaires, logs, etc.).
Lister les conteneurs :
docker ps
docker ps -a
Registre (Docker Hub, registre privé)
Un registre stocke des images. Le plus connu est Docker Hub. On peut :
- pull : télécharger une image
- push : publier une image
Exemple :
docker pull nginx:latest
docker pull python:3.12-slim
3) Installation et vérification
Après installation de Docker Desktop (Windows/macOS) ou Docker Engine (Linux), vérifiez :
docker version
docker info
Test rapide avec l’image “hello-world” :
docker run hello-world
Cette commande télécharge l’image si nécessaire puis exécute un conteneur qui affiche un message.
4) Votre première exécution : comprendre docker run
La commande la plus importante au début est :
docker run [options] image[:tag] [commande]
Exemples :
Lancer un conteneur interactif
docker run -it ubuntu:24.04 bash
-igarde l’entrée standard ouverte-talloue un pseudo-terminalbashest la commande exécutée dans le conteneur
Dans le conteneur, essayez :
cat /etc/os-release
ls -la
exit
Lancer en arrière-plan (mode détaché)
docker run -d nginx:latest
Voir les conteneurs :
docker ps
Nommer un conteneur
docker run -d --name web nginx:latest
docker ps
Exposer des ports : -p
Un conteneur peut écouter sur un port interne (ex. 80). Pour y accéder depuis votre machine :
docker run -d --name web -p 8080:80 nginx:latest
8080: port sur votre machine80: port dans le conteneur
Puis ouvrez http://localhost:8080.
Variables d’environnement : -e
Exemple avec PostgreSQL :
docker run -d --name db \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
postgres:16
5) Cycle de vie des conteneurs : démarrer, arrêter, supprimer
Arrêter :
docker stop web
Démarrer un conteneur déjà créé :
docker start web
Redémarrer :
docker restart web
Supprimer un conteneur :
docker rm web
Supprimer un conteneur en cours d’exécution (forcé) :
docker rm -f web
6) Observer et diagnostiquer : logs, exec, inspect
Logs
Pour voir ce que l’application écrit :
docker logs web
docker logs -f web
-f suit les logs en temps réel.
Exécuter une commande dans un conteneur : docker exec
Très utile pour déboguer :
docker exec -it web bash
Sur Nginx, l’image peut ne pas contenir bash. Essayez alors :
docker exec -it web sh
Inspecter la configuration : docker inspect
docker inspect web
Pour extraire une info précise (ex. l’adresse IP interne) :
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web
7) Volumes : persister les données
Par défaut, les données écrites dans un conteneur disparaissent quand on supprime le conteneur. Pour une base de données, c’est inacceptable. Les volumes permettent de persister.
Volume nommé
Créer un volume :
docker volume create pgdata
docker volume ls
Lancer PostgreSQL avec volume :
docker run -d --name db \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
-p 5432:5432 \
postgres:16
Supprimez le conteneur puis recréez-le : les données restent dans pgdata.
Supprimer un volume (attention, destructif) :
docker volume rm pgdata
Montage de dossier (bind mount)
Utile en développement pour partager votre code avec le conteneur.
Exemple : servir un fichier HTML local avec Nginx.
Créez un dossier et un fichier :
mkdir -p site
echo "<h1>Bonjour Docker</h1>" > site/index.html
Lancez Nginx en montant ce dossier :
docker run -d --name web \
-p 8080:80 \
-v "$(pwd)/site":/usr/share/nginx/html:ro \
nginx:latest
:rosignifie lecture seule (bonne pratique).- Toute modification locale se reflète immédiatement dans le conteneur.
8) Réseaux Docker : communiquer entre conteneurs
Docker crée un réseau par défaut, mais en pratique on utilise souvent un réseau bridge dédié pour un groupe de services (application + base de données).
Créer un réseau :
docker network create appnet
docker network ls
Lancer une base de données sur ce réseau :
docker run -d --name db \
--network appnet \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=appdb \
postgres:16
Lancer un conteneur client sur le même réseau pour tester la connexion :
docker run -it --rm \
--network appnet \
postgres:16 \
psql -h db -U postgres -d appdb
Remarquez -h db : le nom du conteneur devient un nom DNS sur le réseau Docker. C’est un point clé : on évite de coder des IP.
9) Construire vos propres images : Dockerfile expliqué en profondeur
Une image se construit généralement avec un Dockerfile. C’est un fichier texte décrivant les étapes.
Exemple 1 : application Python simple (Flask)
Créez un dossier :
mkdir -p demo-flask
cd demo-flask
Créez app.py :
cat > app.py <<'EOF'
from flask import Flask
app = Flask(__name__)
@app.get("/")
def home():
return "Bonjour depuis un conteneur Docker !\n"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
EOF
Créez requirements.txt :
cat > requirements.txt <<'EOF'
flask==3.0.3
EOF
Créez Dockerfile :
cat > Dockerfile <<'EOF'
FROM python:3.12-slim
# Bonnes pratiques : éviter d’écrire des .pyc et activer des logs immédiats
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
# Installer les dépendances d’abord (meilleur cache)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copier le code ensuite
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
EOF
Comprendre chaque instruction
FROM python:3.12-slim: base minimale avec Python.ENV ...: variables d’environnement dans l’image.WORKDIR /app: définit le répertoire de travail.COPY requirements.txt .: copie dans l’image.RUN pip install ...: exécute une commande pendant la construction (résultat figé dans l’image).COPY app.py .: copie le code.EXPOSE 5000: documentation du port (n’ouvre pas le port tout seul).CMD [...]: commande par défaut au démarrage du conteneur.
Construire l’image :
docker build -t demo-flask:1.0 .
Lancer :
docker run -d --name flask -p 5000:5000 demo-flask:1.0
Tester :
curl http://localhost:5000/
Voir les logs :
docker logs -f flask
Arrêter et supprimer :
docker rm -f flask
10) Caching et couches : pourquoi l’ordre du Dockerfile compte
Docker construit une image en couches. Chaque instruction (FROM, RUN, COPY, etc.) crée une couche. Si une couche ne change pas, Docker peut la réutiliser (cache), accélérant les builds.
Dans l’exemple Flask, on copie requirements.txt et on installe les dépendances avant de copier le code. Ainsi :
- si vous modifiez
app.pymais pasrequirements.txt, Docker réutilise la couche d’installation des dépendances, - le build est plus rapide.
Si vous faisiez COPY . . avant pip install, la moindre modification de code invaliderait le cache et forcerait une réinstallation complète.
11) .dockerignore : éviter d’embarquer l’inutile
Quand vous faites docker build, Docker envoie un “contexte” (les fichiers du dossier) au moteur. Il faut exclure ce qui n’est pas nécessaire : .git, caches, environnements virtuels, etc.
Créez un .dockerignore :
cat > .dockerignore <<'EOF'
.git
__pycache__
*.pyc
.venv
venv
EOF
12) Tags, versions et bonnes pratiques d’images
Tags
Une image est identifiée par nom:tag. Exemples :
nginx:latestpostgres:16python:3.12-slim
Bonnes pratiques :
- éviter
latesten production : préférez une version explicite, - documenter vos tags (ex.
1.0,1.1,2026-02-14), - garder une stratégie simple et cohérente.
Tagger une image :
docker tag demo-flask:1.0 demo-flask:stable
docker images | grep demo-flask
13) Docker Compose : orchestrer plusieurs conteneurs
Quand une application a plusieurs services (API + base de données + cache), lancer tout à la main devient pénible. Docker Compose permet de décrire et lancer un ensemble.
Créez un dossier :
mkdir -p demo-compose
cd demo-compose
Créez app.py :
cat > app.py <<'EOF'
import os
import psycopg2
from flask import Flask
app = Flask(__name__)
def get_conn():
return psycopg2.connect(
host=os.environ["DB_HOST"],
dbname=os.environ["DB_NAME"],
user=os.environ["DB_USER"],
password=os.environ["DB_PASSWORD"],
)
@app.get("/")
def home():
conn = get_conn()
cur = conn.cursor()
cur.execute("SELECT NOW();")
now = cur.fetchone()[0]
cur.close()
conn.close()
return f"Connexion OK, heure DB: {now}\n"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
EOF
Créez requirements.txt :
cat > requirements.txt <<'EOF'
flask==3.0.3
psycopg2-binary==2.9.9
EOF
Créez Dockerfile :
cat > Dockerfile <<'EOF'
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
EOF
Créez compose.yaml :
cat > compose.yaml <<'EOF'
services:
api:
build: .
ports:
- "5000:5000"
environment:
DB_HOST: db
DB_NAME: appdb
DB_USER: postgres
DB_PASSWORD: secret
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_DB: appdb
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
EOF
Lancer :
docker compose up -d --build
docker compose ps
Tester :
curl http://localhost:5000/
Voir les logs :
docker compose logs -f
Arrêter et supprimer (en gardant le volume) :
docker compose down
Supprimer aussi les volumes (destructif) :
docker compose down -v
Comprendre depends_on
depends_on gère l’ordre de démarrage, mais ne garantit pas que la base est prête à accepter des connexions. En pratique, on ajoute souvent :
- une logique de retry côté application,
- ou un script d’attente,
- ou un “healthcheck” selon les besoins.
14) Nettoyage : éviter d’encombrer votre machine
Docker peut accumuler images, conteneurs arrêtés, volumes orphelins.
Voir l’espace :
docker system df
Supprimer conteneurs arrêtés, réseaux inutilisés, images non référencées :
docker system prune
Inclure aussi les images non utilisées :
docker system prune -a
Supprimer aussi les volumes non utilisés (attention) :
docker system prune -a --volumes
15) Sécurité et bonnes pratiques de base
Exécuter en non-root (quand c’est pertinent)
Beaucoup d’images tournent en root par défaut. Pour réduire les risques, on peut créer un utilisateur.
Exemple (principe général) :
RUN useradd -m appuser
USER appuser
Cela dépend des besoins (accès aux ports, écriture dans certains dossiers, etc.).
Minimiser l’image
- privilégier des images
slimou Alpine (selon compatibilité), - éviter d’installer des outils inutiles,
- utiliser
--no-cache-diravec pip, nettoyer les caches apt si vous utilisez Debian/Ubuntu.
Épingler les versions
python:3.12-slimplutôt quepython:latest,postgres:16plutôt quepostgres.
Ne pas mettre de secrets dans l’image
Évitez de mettre des mots de passe dans le Dockerfile. Préférez :
- variables d’environnement au runtime,
- fichiers montés,
- solutions de gestion de secrets selon votre environnement.
16) Dépannage : erreurs fréquentes et méthodes
“Port is already allocated”
Vous mappez un port déjà utilisé.
Diagnostic :
docker ps
Solution : changer le port hôte, par exemple :
docker run -p 8081:80 nginx:latest
“Cannot connect to the Docker daemon”
Le service Docker n’est pas démarré ou droits insuffisants. Vérifiez Docker Desktop ou le service. Sur Linux, selon configuration, il peut falloir appartenir au groupe docker.
Un conteneur s’arrête immédiatement
Souvent, la commande principale se termine. Regardez :
docker logs <nom>
docker ps -a
Comprendre ce qui tourne réellement
Inspectez :
docker inspect <nom>
Vérifiez :
- la commande (
Cmd,Entrypoint), - les ports,
- les volumes,
- le réseau.
17) Résumé des commandes indispensables
Images
docker pull nginx:latest
docker images
docker rmi nginx:latest
docker build -t monimage:1.0 .
Conteneurs
docker run -d --name monconteneur -p 8080:80 nginx:latest
docker ps
docker ps -a
docker stop monconteneur
docker start monconteneur
docker rm -f monconteneur
docker logs -f monconteneur
docker exec -it monconteneur sh
Volumes et réseaux
docker volume create data
docker volume ls
docker network create monreseau
docker network ls
Compose
docker compose up -d --build
docker compose logs -f
docker compose down
docker compose down -v
18) Prochaines étapes recommandées
Pour progresser après ce guide :
- Construire une image multi-étapes (multi-stage build) pour réduire la taille finale, surtout pour les projets compilés.
- Ajouter des healthchecks et une stratégie de redémarrage.
- Étudier les différences entre bind mounts et volumes selon les environnements.
- Apprendre à publier une image sur un registre (public ou privé).
- Explorer les limites de ressources (CPU/RAM) et l’observabilité (métriques, logs structurés).
Conclusion
Docker simplifie la vie en rendant les environnements prévisibles et reproductibles. En comprenant bien la différence entre images et conteneurs, la gestion des volumes, des réseaux, et la construction via Dockerfile, vous avez les bases nécessaires pour conteneuriser une application réelle et la faire tourner localement ou sur un serveur.
Si vous me dites votre système (Linux, macOS, Windows) et le type d’application (Python, Node, PHP, Java…), je peux proposer un exemple adapté avec une structure de projet réaliste et des commandes prêtes à exécuter.