Réseaux¶
Par défaut, chaque conteneur Docker est connecté à un réseau virtuel interne géré par le moteur. Ce réseau permet aux conteneurs de communiquer entre eux et avec l’extérieur, sans modifier la configuration réseau de la machine hôte.
Docker crée pour chaque réseau une interface virtuelle sur l’hôte (de type bridge) et attribue automatiquement une adresse IP à chaque conteneur connecté. Le trafic entre conteneurs transite par ce bridge ; le trafic vers l’extérieur passe par les règles NAT du système.
Trois drivers couvrent les cas d’usage courants :
bridge(par défaut) : réseau privé isolé sur l’hôte, adapté à la grande majorité des déploiementshost: le conteneur partage directement la pile réseau de l’hôte, sans isolationnone: aucune interface réseau, isolation totale
La différence entre le réseau bridge créé par défaut et un réseau bridge personnalisé est importante en pratique : le réseau par défaut ne propose pas de résolution DNS par nom de conteneur, ce qui oblige à manipuler des adresses IP. Un réseau personnalisé active automatiquement un serveur DNS interne : les conteneurs se joignent par leur nom, ce qui est plus robuste et lisible.
Séparer les conteneurs sur des réseaux distincts permet également de cloisonner les flux : une base de données placée sur un réseau backend ne sera pas joignable depuis un conteneur qui n’est connecté qu’au réseau frontend. C’est le principe de moindre privilège appliqué au réseau.
01. Pile réseau Docker et drivers¶
Résultat attendu du ping par IP (étape 2) :
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.108 msRésultat attendu du ping par nom (étape 2 — bridge par défaut) :
ping: bad address 'cont-b'Sur le réseau bridge par défaut, la résolution DNS par nom de conteneur n’est pas disponible. Elle nécessite un réseau personnalisé (voir exercice 02).
| Driver | DNS par nom | Isolation | Usage |
|---|---|---|---|
bridge (défaut) | Non | Partielle | Tests rapides |
| réseau personnalisé | Oui | Oui | Production |
host | N/A | Aucune | Performance maximale |
none | N/A | Totale | Sécurité maximale |
02. Réseaux personnalisés¶
Résultat attendu du ping par nom (étape 3) :
PING serveur-web (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.098 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.071 msLa résolution DNS fonctionne par nom de conteneur sur un réseau personnalisé — contrairement au réseau bridge par défaut (EX-10).
Résultat attendu de docker network inspect reseau-app (extrait) :
"Containers": {
"abc...": { "Name": "serveur-web", "IPv4Address": "172.18.0.2/16" },
"def...": { "Name": "client", "IPv4Address": "172.18.0.3/16" }
}Étape 1 — Créer le réseau
docker network create wp-net # reseau bridge personnalise
docker network lsÉtape 2 — Lancer MariaDB
docker run -d \ # mode detach
--name wp-db \ # nom du conteneur (utilise comme nom DNS)
--network wp-net \ # reseau personnalise
-e MYSQL_ROOT_PASSWORD=rootpass \ # mot de passe root
-e MYSQL_DATABASE=wordpress \ # base a creer au demarrage
-e MYSQL_USER=wp \ # utilisateur applicatif
-e MYSQL_PASSWORD=wppass \ # mot de passe utilisateur
mariadb:10.11 # image
docker logs wp-db # attendre la ligne "ready for connections"Étape 3 — Lancer WordPress
docker run -d \ # mode detach
--name wp-app \ # nom du conteneur
--network wp-net \ # meme reseau que wp-db
-p 8080:80 \ # port-hote:port-conteneur
-e WORDPRESS_DB_HOST=wp-db \ # nom DNS du conteneur MariaDB
-e WORDPRESS_DB_USER=wp \ # utilisateur de la base
-e WORDPRESS_DB_PASSWORD=wppass \ # mot de passe
-e WORDPRESS_DB_NAME=wordpress \ # nom de la base
wordpress:latest # imageÉtape 4 — Vérifier
# Ouvrir http://localhost:8080 dans le navigateur
# Tester la resolution DNS depuis wp-app
docker exec wp-app ping -c 2 wp-dbRésultat attendu du ping :
PING wp-db (192.168.x.2): 56 data bytes
64 bytes from 192.168.x.2: seq=0 ttl=64 time=0.091 msÉtape 5 — Nettoyer
docker stop wp-app wp-db
docker rm wp-app wp-db
docker network rm wp-net
docker image prune -f
docker volume prune -f03. Isolation réseau entre piles (Namespace net)¶
Étape 3 — ping intra-réseau (succès attendu) :
PING web-2 (192.168.x.3): 56 data bytes
64 bytes from 192.168.x.3: seq=0 ttl=64 time=0.091 msÉtape 4 — ping inter-réseaux (isolation attendue) :
ping: bad address 'db-1'db-1 est inaccessible depuis web-1. Ils partagent le même hôte physique mais leurs Namespaces net sont distincts — aucune route ne les relie.
Étape 5 — après docker network connect :
Le conteneur proxy dispose de deux interfaces réseau (une par réseau) et peut joindre les deux piles. web-1 reste isolé de db-1.
Étape 6 — Résultat attendu :
pile-frontend : web-1 web-2 proxy
pile-backend : db-1 proxyweb-1 n’apparaît que dans pile-frontend : il ne peut pas joindre db-1. proxy, connecté aux deux réseaux, apparaît dans les deux listes.
| Situation | Résultat |
|---|---|
| Conteneurs sur le même réseau | Communication par nom DNS |
| Conteneurs sur réseaux différents | Isolation totale |
| Conteneur connecté aux deux réseaux | Passerelle contrôlée |
Étape 1 — Créer les réseaux
docker network create wp-frontend # reseau web
docker network create wp-backend # reseau base de donnees
docker network lsÉtape 2 — Lancer MariaDB sur le backend uniquement
docker run -d \ # mode detach
--name wp-db \ # nom du conteneur
--network wp-backend \ # reseau backend uniquement
-e MYSQL_ROOT_PASSWORD=rootpass \ # mot de passe root
-e MYSQL_DATABASE=wordpress \ # base a creer
-e MYSQL_USER=wp \ # utilisateur applicatif
-e MYSQL_PASSWORD=wppass \ # mot de passe
mariadb:10.11
docker logs wp-db # attendre "ready for connections"Étape 3 — Lancer WordPress et le connecter aux deux réseaux
docker run -d \ # mode detach
--name wp-app \ # nom du conteneur
--network wp-frontend \ # reseau frontend au lancement
-p 8080:80 \ # port-hote:port-conteneur
-e WORDPRESS_DB_HOST=wp-db \ # nom DNS de MariaDB
-e WORDPRESS_DB_USER=wp \ # utilisateur
-e WORDPRESS_DB_PASSWORD=wppass \ # mot de passe
-e WORDPRESS_DB_NAME=wordpress \ # nom de la base
wordpress:latest
# Connecter wp-app au reseau backend pour joindre MariaDB
docker network connect wp-backend wp-appÉtape 4 — Vérifier
# WordPress joignable depuis le navigateur : http://localhost:8080
# wp-db joignable depuis wp-app (deux reseaux)
docker exec wp-app ping -c 2 wp-dbRésultat attendu du ping depuis wp-app :
PING wp-db (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.091 msÉtape 5 — Nettoyer
docker stop wp-app wp-db
docker rm wp-app wp-db
docker network rm wp-frontend wp-backend
docker image prune -f
docker volume prune -fArchitecture des réseaux :
[wp-frontend] [wp-backend]
wp-app <-------> wp-app <-------> wp-db
(port 8080) (pont entre
les deux reseaux)WordPress joue le rôle de passerelle contrôlée : seul lui peut joindre MariaDB. Le trafic web (port 8080) reste sur le réseau frontend.
Volumes¶
04. Bind mount¶
Résultat attendu après modification :
<h1>Modified from host</h1>La modification est immédiate — pas de redémarrage du conteneur nécessaire. C’est le comportement attendu d’un bind mount : le système de fichiers hôte est monté directement dans le conteneur via le Namespace mnt.
alert - Unknown Directive
alert - Unknown DirectivePortez votre attention sur le chemin du dossier à monter
05. Volumes de données¶
Résultat attendu de docker volume inspect data-app :
[
{
"CreatedAt": "2026-06-15T10:00:00Z",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/data-app/_data",
"Name": "data-app",
"Scope": "local"
}
]Résultat attendu de l’étape 6 (restauration) :
Initial entry - Mon Jun 15 10:00:00 UTC 2026Les données sont intégralement restaurées sur un volume distinct.
| Commande | Action |
|---|---|
docker volume create NOM | Créer un volume |
docker volume ls | Lister les volumes |
docker volume inspect NOM | Détails et chemin hôte |
docker volume rm NOM | Supprimer (si non utilisé) |
docker volume prune | Supprimer tous les volumes inutilisés |
Étape 1 — Créer les volumes et le réseau
docker volume create wp-data # donnees MariaDB
docker volume create wp-logs # logs Apache
docker network create wp-net # reseau prive
docker volume ls
docker network lsÉtape 2 — Lancer MariaDB avec volume de données
docker run -d \ # mode detach
--name wp-db \ # nom du conteneur
--network wp-net \ # reseau prive
-v wp-data:/var/lib/mysql \ # volume de donnees : chemin interne MariaDB
-e MYSQL_ROOT_PASSWORD=rootpass \ # mot de passe root
-e MYSQL_DATABASE=wordpress \ # base a creer
-e MYSQL_USER=wp \ # utilisateur applicatif
-e MYSQL_PASSWORD=wppass \ # mot de passe utilisateur
mariadb:10.11
docker logs wp-db # attendre "ready for connections"Étape 3 — Lancer WordPress avec volume de logs
docker run -d \ # mode detach
--name wp-app \ # nom du conteneur
--network wp-net \ # meme reseau que wp-db
-p 8080:80 \ # port-hote:port-conteneur
-v wp-logs:/var/log/apache2 \ # volume de logs : chemin interne Apache
-e WORDPRESS_DB_HOST=wp-db \ # nom DNS du conteneur MariaDB
-e WORDPRESS_DB_USER=wp \ # utilisateur
-e WORDPRESS_DB_PASSWORD=wppass \ # mot de passe
-e WORDPRESS_DB_NAME=wordpress \ # nom de la base
wordpress:latestÉtape 4 — Générer du trafic et lire les logs
curl -s http://localhost:8080 > /dev/null
curl -s http://localhost:8080 > /dev/null
curl -s http://localhost:8080/wp-login.php > /dev/null
docker run --rm -v wp-logs:/logs alpine ls -lh /logs
docker run --rm -v wp-logs:/logs alpine tail -5 /logs/access.log
docker run --rm -v wp-data:/data alpine ls -lh /dataÉtape 5 — Sauvegarde et restauration
docker run --rm \
-v wp-data:/data \
-v /tmp:/backup \
alpine tar czf /backup/wp-data-backup.tar.gz -C /data .
ls -lh /tmp/wp-data-backup.tar.gz
docker stop wp-app wp-db && docker rm wp-app wp-db
docker volume create wp-data-restored
docker run --rm \
-v wp-data-restored:/data \
-v /tmp:/backup \
alpine tar xzf /backup/wp-data-backup.tar.gz -C /data
docker run --rm -v wp-data-restored:/data alpine ls -lh /dataÉtape 6 — Nettoyer
docker volume rm wp-data wp-logs wp-data-restored
docker network rm wp-net
rm /tmp/wp-data-backup.tar.gz
docker image prune -fa
docker volume prune -fa| Volume | Conteneur | Chemin interne | Contenu |
|---|---|---|---|
wp-data | wp-db | /var/lib/mysql | Fichiers de la base MariaDB |
wp-logs | wp-app | /var/log/apache2 | Logs Apache |
06. Conteneurs liés à un volume partagé¶
Résultat attendu de docker logs consommateur :
Mon Jun 15 10:00:05 UTC 2026
Mon Jun 15 10:00:10 UTC 2026
Mon Jun 15 10:00:15 UTC 2026Les deux conteneurs accèdent au même volume de façon concurrente. Le consommateur est en lecture seule (:ro) : il ne peut pas modifier log.txt, même accidentellement.
Étape 1 — Créer les ressources
docker volume create nginx-logs
docker network create analyse-netÉtape 2 — Lancer Nginx
docker run -d \ # mode detach
--name web \ # nom du conteneur
--network analyse-net \ # reseau dedie
-p 8090:80 \ # port-hote:port-conteneur
-v nginx-logs:/var/log/nginx \ # volume de logs : chemin interne Nginx
nginx:alpine # imageÉtape 4 — Lancer GoAccess
docker run -d \ # mode detach
--name analyse \ # nom du conteneur
--network analyse-net \ # meme reseau
-p 7890:7890 \ # port du tableau de bord
-v nginx-logs:/var/log/nginx:ro \ # logs en lecture seule
allinurl/goaccess \ # image GoAccess
/var/log/nginx/access.log \ # fichier de log a analyser
--log-format=COMBINED \ # format des logs Nginx
--real-time-html \ # mise a jour en temps reel
-o /dev/null # sortie HTML vers /dev/null (dashboard websocket uniquement)Rôle des paramètres GoAccess :
| Paramètre | Rôle |
|---|---|
--log-format=COMBINED | Format de log Apache/Nginx standard |
--real-time-html | Active la mise à jour via WebSocket |
-p 7890:7890 | Port du tableau de bord et du WebSocket |
:ro | Le conteneur ne peut pas modifier les logs |
Tableau de bord GoAccess — panneaux principaux :
Unique visitors : nombre de visites par heure
Requested files : pages les plus demandées
HTTP status codes : répartition 200 / 301 / 404 / 500
Hosts : adresses IP des clients
Browsers / OS : détection du navigateur et du système
Étape 7 — Nettoyer
docker stop web analyse
docker rm web analyse
docker network rm analyse-net
docker volume rm nginx-logs
docker image prune -fa
docker volume prune -faExercice final — WordPress en load balancing derrière Nginx¶
Étape 1 — Créer les ressources
docker network create wp-frontend
docker network create wp-backend
docker volume create wp-data
docker volume create wp-logsÉtape 2 — Lancer MariaDB
docker run -d \ # mode detach
--name wp-db \ # nom du conteneur
--network wp-backend \ # reseau backend uniquement
-v wp-data:/var/lib/mysql \ # volume de donnees
-e MYSQL_ROOT_PASSWORD=rootpass \ # mot de passe root
-e MYSQL_DATABASE=wordpress \ # base a creer
-e MYSQL_USER=wp \ # utilisateur applicatif
-e MYSQL_PASSWORD=wppass \ # mot de passe
mariadb:10.11
docker logs wp-db # attendre "ready for connections"Étape 3 — Lancer WordPress (sans port exposé)
docker run -d \ # mode detach
--name wp-app-1 \ # nom du conteneur
--network wp-frontend \ # reseau frontend
-v wp-logs:/var/log/apache2 \ # volume de logs
-e WORDPRESS_DB_HOST=wp-db \ # nom DNS de MariaDB
-e WORDPRESS_DB_USER=wp \ # utilisateur
-e WORDPRESS_DB_PASSWORD=wppass \ # mot de passe
-e WORDPRESS_DB_NAME=wordpress \ # nom de la base
wordpress:latest
# pas de -p : Nginx sera le seul point d entree
# Connecter WordPress au backend pour joindre MariaDB
docker network connect wp-backend wp-app-1Étape 4 — Configuration Nginx et lancement
Créer le fichier de configuration Nginx :
mkdir -p /tmp/nginx-lbContenu de /tmp/nginx-lb/nginx.conf :
events {
worker_connections 1024;
}
http {
# Declaration du groupe de serveurs upstream (load balancing)
upstream wordpress_pool {
server wp-app-1:80; # instance WordPress 1
# server wp-app-2:80; # decommenter pour le load balancing
}
server {
listen 80;
location / {
# Transmettre les requetes a l upstream WordPress
proxy_pass http://wordpress_pool;
# En-tetes transmis a WordPress pour qu il connaisse l IP du client
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}Lancer Nginx :
docker run -d \ # mode detach
--name nginx-lb \ # nom du conteneur
--network wp-frontend \ # reseau frontend
-p 8080:80 \ # seul port expose sur l hote
-v /tmp/nginx-lb/nginx.conf:/etc/nginx/nginx.conf:ro \ # configuration en bind mount
nginx:alpine # image legereÉtape 5 — Vérifier
# Navigateur : http://localhost:8080
curl -s http://localhost:8080 | head -5
# Logs de Nginx
docker logs nginx-lb
# Verifier que MariaDB est inaccessible depuis le frontend
docker run --rm --network wp-frontend alpine ping -c 2 wp-dbLogs Nginx attendus :
172.18.0.1 - - [17/Jun/2026:10:00:01 +0000] "GET / HTTP/1.1" 302 0 "-" "curl/7.88.1" "-"Étape 6 (avancé) — Load balancing sur deux WordPress
# Lancer un second WordPress
docker run -d \ # mode detach
--name wp-app-2 \ # deuxieme instance
--network wp-frontend \ # reseau frontend
-v wp-logs:/var/log/apache2 \ # meme volume de logs
-e WORDPRESS_DB_HOST=wp-db \ # meme base de donnees
-e WORDPRESS_DB_USER=wp \
-e WORDPRESS_DB_PASSWORD=wppass \
-e WORDPRESS_DB_NAME=wordpress \
wordpress:latest
docker network connect wp-backend wp-app-2
# Mettre a jour nginx.conf : decommenter la ligne wp-app-2
# Puis recharger Nginx sans interruption
docker exec nginx-lb nginx -s reload
# Generer du trafic et observer la distribution
for i in $(seq 1 6); do curl -s http://localhost:8080 > /dev/null; done
docker logs wp-app-1
docker logs wp-app-2Rôle des directives Nginx :
| Directive | Rôle |
|---|---|
upstream wordpress_pool | Définit le groupe de serveurs en amont |
server wp-app-1:80 | Ajoute une instance au pool (nom DNS Docker) |
proxy_pass http://wordpress_pool | Envoie les requêtes vers le pool |
proxy_set_header Host | Transmet le nom de domaine à WordPress |
proxy_set_header X-Real-IP | Transmet l’IP réelle du client |
proxy_set_header X-Forwarded-For | Historique des proxies traversés |
Par défaut, Nginx utilise round-robin : les requêtes sont distribuées à tour de rôle entre les instances du pool. D’autres algorithmes sont disponibles : least_conn (instance la moins chargée), ip_hash (affinité de session par IP client).
Étape 7 — Nettoyer
docker stop nginx-lb wp-app-1 wp-app-2 wp-db 2>/dev/null
docker rm nginx-lb wp-app-1 wp-app-2 wp-db 2>/dev/null
docker network rm wp-frontend wp-backend
docker volume rm wp-data wp-logs
rm -rf /tmp/nginx-lb
docker image prune -fa
docker volume prune -faArchitecture finale :
[Client]
|
v
[nginx-lb] port 8080 reseau : wp-frontend
| \
| \
v v
[wp-app-1] [wp-app-2] reseaux : wp-frontend + wp-backend
| / volumes : wp-logs
v /
[wp-db] reseau : wp-backend
volume : wp-dataAnnexe — Namespaces et isolation avancée¶
Ces exercices approfondissent les mécanismes internes de Docker. Ils sont indépendants du fil directeur WordPress et s’adressent aux stagiaires souhaitant aller plus loin.
07. Volume tmpfs — données sensibles en mémoire (Namespace mnt)¶
Étape 2 — lecture du secret :
temp-passwordÉtape 3 — grep tmpfs dans /proc :
tmpfs /run/secrets tmpfs rw,nosuid,noexec,size=10240k 0 0Le montage est bien de type tmpfs — aucun bloc disque alloué, aucune trace dans /var/lib/docker/volumes/.
Étape 4 — après redémarrage :
cat: can't open '/run/secrets/db_password': No such file or directoryÉtape 5 — après redémarrage avec volume nommé :
persisted-password| Type de montage | Persisté sur disque | Survit au redémarrage | Usage typique |
|---|---|---|---|
tmpfs | Non (RAM) | Non | Secrets, tokens, cache temporaire |
| Volume nommé | Oui | Oui | Bases de données, uploads |
| Bind mount | Oui (hôte) | Oui | Développement, config |
08. Partage de Namespace entre conteneurs — pattern sidecar¶
Partie A — ip addr sur les deux conteneurs (résultat identique) :
1: lo: <LOOPBACK,UP>
18: eth0@if19: inet 172.17.0.2/16Les deux conteneurs partagent la même interface réseau — ils sont dans le même Namespace net. Toute connexion à localhost depuis le sidecar atteint le processus du conteneur principal.
Partie A — wget depuis le sidecar :
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>Partie B — ps aux depuis sidecar-pid :
PID USER COMMAND
1 root nginx: master process
29 nginx nginx: worker process
30 root sleep 3600Le sidecar voit les processus de app-pid — ils partagent le Namespace PID. Son propre processus sleep est également visible.
| Namespace partagé | Option Docker | Cas d’usage |
|---|---|---|
| réseau | --network container:NOM | Proxy, collecteur de métriques réseau |
| PID | --pid container:NOM | Débogage, profiling, injection de tracing |
| les deux | les deux options combinées | Pattern sidecar complet (Kubernetes) |
09. Conteneurs rootless — héritage LXC et User Namespace¶
Solution to Exercise (Note) #
Étape 1 — id dans le conteneur :
uid=0(root) gid=0(root) groups=0(root)Étape 1 — ps depuis l’hôte :
PID UID USER COMM
3842 0 root sleepEn mode standard, le root du conteneur est le root de l’hôte. Si un processus s’échappe du conteneur, il a les droits root sur la machine.
Étape 2 — avec --user 1000 :
uid=1000 gid=1000
touch: /test: Permission deniedÉtape 4 — uid_map sans remappage :
0 0 4294967295Mapping identité : UID 0 dans le conteneur = UID 0 sur l’hôte.
Étape 5 — uid_map avec userns-remap :
0 100000 65536UID 0 dans le conteneur est mappé à l’UID 100000 sur l’hôte. Un processus échappé n’a aucun privilège sur la machine.
| Mode | UID dans le conteneur | UID sur l’hôte | Risque |
|---|---|---|---|
| Standard | 0 (root) | 0 (root) | Élevé |
--user 1000 | 1000 | 1000 | Faible |
userns-remap | 0 (root apparent) | 100000+ | Faible |
| Docker rootless | 0 (root apparent) | UID courant | Minimal |
Filiation LXC → Docker : les LXC unprivileged containers (2013) ont démontré la viabilité du User Namespace pour l’isolation sans privilèges. Docker a intégré userns-remap en 2016 (v1.10), puis le mode rootless complet en 2019 (v19.03). Aujourd’hui, Docker rootless est l’approche recommandée pour les environnements multi-utilisateurs et les pipelines CI/CD.