Intro
Docker est un pilier de la livraison logicielle moderne, mais son architecture interne reste souvent une boîte noire. Ce guide explique Docker de bout en bout : le modèle client‑daemon, les images et leurs couches, les runtimes, le réseau, le stockage, la sécurité, le diagnostic et la reprise après incident. Au fil du parcours, vous exécuterez des commandes proches de votre quotidien d’ingénierie pour dépanner sereinement et concevoir des systèmes plus sûrs et fiables.
Vue d’ensemble : composants et rôles
Docker suit un modèle client‑serveur avec une pile d’exécution sous le capot :
- Client Docker : la CLI (docker) locale ou distante. Elle analyse vos commandes et communique avec le daemon Docker via l’API Docker.
- Daemon Docker (dockerd) : le serveur qui construit les images, gère conteneurs, réseaux et volumes, et expose l’API REST via un socket Unix ou TCP.
- containerd et runc : containerd orchestre le cycle de vie des conteneurs et les images pour dockerd. runc est le runtime OCI bas niveau qui crée réellement les conteneurs à l’aide des primitives Linux.
- Fonctionnalités du noyau Linux : namespaces (PID, NET, MNT, UTS, IPC), cgroups, capabilities, seccomp, et AppArmor/SELinux assurent l’isolation et les limites de ressources.
- Images et registres : les images sont des systèmes de fichiers en lecture seule, superposés par couches. Les registres (Docker Hub, registres privés) les stockent et les distribuent.
- Conteneurs, réseaux et volumes : les conteneurs sont des instances exécutables d’images avec une fine couche inscriptible. Les réseaux relient les conteneurs; les volumes (ou bind mounts) conservent et partagent les données.
Voyez le client Docker comme la télécommande, le daemon comme le plan de contrôle, containerd/runc comme le moteur d’exécution, et le noyau comme l’agent d’isolation et de gestion des ressources.
Fonctionnement des images et des couches
Les images sont construites à partir d’un Dockerfile, où chaque instruction crée généralement une couche. Les couches sont adressées par contenu et mises en cache, ce qui accélère les reconstructions et permet à plusieurs images de partager des couches de base sur disque.
Exemple : une petite image qui exécute curl.
# Dockerfile
FROM alpine:3.20
RUN apk add --no-cache curl
CMD ["curl", "-sS", "https://example.com"]
Construire et exécuter :
docker build -t mycurl .
docker run --rm mycurl
Astuce : ordonnez les instructions du Dockerfile pour que les étapes qui changent rarement (p. ex. apt/apk add) précèdent les copies de code qui évoluent fréquemment. Vous maximiserez les hits de cache et réduirez le temps de rebuild.
Pour observer le partage des couches et l’espace disque :
docker image inspect mycurl --format '{{json .RootFS.Layers}}' | jq '.'
docker system df
Flux de données : build, pull/push et exécution
- Build : le client compacte (tar) votre contexte de build et l’envoie au daemon. Le daemon exécute le Dockerfile et produit une image par couches.
- Pull/Push : le daemon s’authentifie auprès d’un registre, négocie les manifests, télécharge ou envoie les couches par digest et les met en cache localement.
- Exécuter : le daemon demande à containerd de créer un conteneur à partir du système de fichiers racine et de la configuration de l’image. runc configure namespaces, cgroups et montages, puis exécute le processus. Le conteneur tourne comme un processus Linux classique, isolé de l’hôte.
Tracez l’activité en temps réel :
# Afficher les événements du daemon pendant build et run
docker events --since 10m &
docker build -t demo .
docker run --rm demo
kill %1
Flux de contrôle : comment le client parle au daemon
Par défaut, le client envoie des requêtes HTTP vers l’API Docker via un socket Unix. Vous pouvez cibler des daemons distants via SSH ou via TCP sécurisé par TLS. Cette séparation permet la gestion distante, l’application de RBAC via des proxys et des frontières de sécurité nettes.
Exemples :
# Local (socket Unix)
docker ps
# Distant via SSH (pas besoin d’ouvrir un port TCP du daemon)
export DOCKER_HOST=ssh://ubuntu@prod01
docker ps
# Vérification explicite de l’API
docker version
Si vous devez exposer TCP, utilisez TLS avec certificats client et des règles de pare‑feu. Préférez le transport SSH pour sa simplicité et sa sécurité.
Réseau en pratique
Docker propose plusieurs pilotes réseau :
- bridge : le réseau local par défaut avec NAT. Les bridges définis par l’utilisateur ajoutent un DNS embarqué et une meilleure isolation.
- host : partage la pile réseau de l’hôte (pas d’isolation). Utile pour certains cas sensibles aux performances.
- none : pas de réseau.
- macvlan/ipvlan : rattache directement les conteneurs au réseau physique avec des adresses MAC/IP propres.
Créer un réseau applicatif isolé et tester la découverte de service :
docker network create appnet
docker run -d --name web --network appnet -p 8080:80 nginx:alpine
# Utiliser une petite image curl pour joindre le conteneur web par son nom
docker run --rm --network appnet curlimages/curl:8.8.0 curl -sS http://web
Inspecter et diagnostiquer :
docker network inspect appnet
Si vous observez des conflits d’IP (surtout sur portables ou avec VPN), configurez des pools d’adresses personnalisés :
{
"default-address-pools": [
{ "base": "10.90.0.0/16", "size": 24 }
]
}
Stockage : overlay2, volumes et bind mounts
La plupart des hôtes Linux utilisent overlay2, un système de fichiers en union qui empile les couches d’image plus une petite couche inscriptible de conteneur. Pour des données persistantes, utilisez des volumes ou des bind mounts plutôt que de compter sur la couche inscriptible du conteneur.
- Volumes : gérés par Docker, stockés sous /var/lib/docker/volumes. Idéals pour bases de données et état.
- Bind mounts : mappent un chemin de l’hôte dans le conteneur. Idéals en développement ou quand vous devez contrôler le chemin d’hôte.
Exemple avec un volume nommé :
docker volume create pgdata
docker run -d --name db \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16
Vérifier l’usage disque et purger prudemment les éléments obsolètes :
docker system df
docker image prune -f
docker container prune -f
docker volume prune # vérifiez bien avant en production
Configuration sûre : durcir le daemon et les conteneurs
Adoptez une base conservatrice en production :
- N’exposez pas le daemon sans TLS. Préférez le transport SSH.
- Activez le remapping des espaces utilisateurs (user namespace remapping) pour réduire l’exposition des UID hôte.
- Utilisez live-restore pour que les conteneurs continuent de tourner lors d’un redémarrage du daemon.
- Limitez les privilèges des conteneurs : supprimez des capabilities, utilisez seccomp/AppArmor, des systèmes de fichiers en lecture seule et exécutez avec un utilisateur non‑root quand c’est possible.
- Configurez la rotation des logs pour éviter la croissance illimitée du pilote json-file.
daemon.json minimal :
{
"userns-remap": "default",
"live-restore": true,
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "5" }
}
Appliquer et vérifier :
sudo systemctl restart docker
docker info --format '{{.SecurityOptions}}'
Exécuter un serveur web de façon sûre sur le port 80 sans capacités root complètes :
docker run -d --name web-safe \
--read-only \
--tmpfs /tmp \
--cap-drop ALL --cap-add NET_BIND_SERVICE \
--user 1000:1000 \
-p 8080:80 \
nginx:alpine
Pour figer exactement le contenu et éviter la dérive des tags, utilisez les digests d’image :
docker pull nginx:alpine
docker inspect --format='{{index .RepoDigests 0}}' nginx:alpine
# Exemple : nginx@sha256:...
docker run nginx@sha256:...
Le mode rootless est une autre option robuste quand il est supporté : il exécute dockerd et les conteneurs sans droits root sur l’hôte, réduisant encore les risques.
Vérification et diagnostics
Commencez par un inventaire d’environnement :
docker version
docker info | less
Contrôle de bout en bout :
docker run --rm hello-world
Quand quelque chose se comporte mal, utilisez cette trousse rapide :
- Journaux et détails :
docker logs -f <id>,docker inspect <id>,docker top <id> - Activité système :
docker events --since 10m,docker stats - Réseaux :
docker network ls,docker network inspect <net> - Pression disque :
docker system df,df -h,du -sh /var/lib/docker - Services de l’hôte :
journalctl -u docker -u containerd --since 1h,systemctl status docker
Pannes courantes et reprise
- Le daemon ne démarre pas : vérifiez
journalctl -u docker. Validez la syntaxe de daemon.json et la compatibilité du pilote de stockage. Essayez d’annuler les changements récents. - Les conteneurs échouent au démarrage après un reboot hôte : activez
live-restore. Inspectez les erreurs avecdocker inspect --format '{{.State.Error}}' <id>. - Faible espace disque sur overlay2 : les pulls et démarrages peuvent échouer. Évaluez via
docker system dfetdf -hsur l’hôte. Purgez les artefacts inutilisés et, si besoin, agrandissez le système de fichiers. - Conflits réseau ou soucis DNS : inspectez avec
docker network inspect. Envisagez desdefault-address-poolspersonnalisés. Assurez-vous que les routes VPN/hôte ne chevauchent pas les CIDR des conteneurs. - Enflement des fichiers de log : configurez la rotation json-file (au niveau daemon) ou des options de logs au run par conteneur.
Stratégies de retour arrière :
- Figer et revenir via un digest ou un tag antérieur :
docker run --rm nginx@sha256:<previous>
- Pour des services (p. ex. en Docker Swarm), utilisez le rollback intégré :
docker service update --rollback my_service
- Conservez des sauvegardes de volumes pour les services stateful; testez régulièrement la restauration.
Parcours pratique : de zéro à vérifié
- Noter les versions et pilotes :
docker version
docker info | egrep 'Storage Driver|Cgroup|Security Options'
- Construire et exécuter une image d’exemple (ordonnancement favorable au cache) :
FROM alpine:3.20
RUN apk add --no-cache curl
COPY . /app
WORKDIR /app
CMD ["sh", "-c", "curl -sS https://example.com"]
docker build -t sample .
docker run --rm sample
- Créer un réseau applicatif et valider la connectivité :
docker network create appnet
docker run -d --name web --network appnet nginx:alpine
docker run --rm --network appnet curlimages/curl:8.8.0 curl -sS http://web
- Rendre les données persistantes avec un volume nommé :
docker volume create appdata
docker run -d --name kv \
-v appdata:/data \
redis:7-alpine
- Appliquer des réglages sûrs du daemon et confirmer :
sudo tee /etc/docker/daemon.json >/dev/null <<'JSON'
{
"userns-remap": "default",
"live-restore": true,
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "5" }
}
JSON
sudo systemctl restart docker
docker info --format '{{.SecurityOptions}}'
Liste de contrôle opérations
- Surveiller la santé du daemon : docker info, logs système, et alertes sur échecs.
- Garder les images à jour : pulls, rebuilds avec correctifs, et tests en staging.
- Maîtriser l’espace disque : docker system df; purger images, conteneurs et volumes inutilisés avec des garde‑fous planifiés.
- Vérifier les sauvegardes : restaurer régulièrement des volumes en environnement non‑prod.
- Durcir les valeurs par défaut : userns-remap, live-restore, rotation des logs et privilèges minimisés pour les conteneurs.
- Documenter les changements : suivez les éditions de daemon.json, les mises à jour d’images et les CIDR réseaux.
Vérification rapide de l’espace disque :
docker system df
Conclusion
Comprendre l’architecture de Docker paie à chaque debug de déploiement instable, chasse aux glitches réseau ou durcissement d’un hôte de production. Gardez le modèle mental clair : le client contrôle le daemon; le daemon orchestre containerd et runc; le noyau fait respecter l’isolation; les images et couches sont adressées par contenu et mises en cache; réseaux et volumes complètent l’environnement d’exécution. Avec les exemples et listes ci‑dessus, vous pouvez construire, exécuter, sécuriser, diagnostiquer et restaurer des charges de travail Docker en toute confiance.