19 - Les conteneurs sans Docker
Ce que tu vas apprendre
- Comprendre ce qu'est chroot et ses limites
- Découvrir les namespaces Linux (PID, network, mount, user)
- Comprendre les cgroups pour limiter les ressources
- Construire un "conteneur" a la main avec unshare
- Pourquoi tout ca compte quand tu utilises Docker
Prerequisites
Connaitre les processus et les bases du système de fichiers.
Quand j'ai commence a utiliser Docker, c'etait magique. Un docker run et hop, un environnement isole. Mais je n'avais aucune idee de ce qui se passait en dessous. Le jour ou j'ai du debugger un problème de réseau entre conteneurs, j'etais perdu. Comprendre les mecanismes sous-jacents m'a rendu bien meilleur pour diagnostiquer les problèmes. Et spoiler : un conteneur, ce n'est pas une VM. C'est juste un processus Linux avec quelques restrictions.
chroot : l'ancetre de l'isolation
chroot change la racine du système de fichiers pour un processus. C'est le mecanisme d'isolation le plus ancien de Linux :
bash# Creer un mini systeme de fichiers
mkdir -p /tmp/mon-root/{bin,lib,lib64,proc}
# Copier bash et ses dependances
cp /bin/bash /tmp/mon-root/bin/
cp /bin/ls /tmp/mon-root/bin/
# Copier les bibliotheques necessaires (ldd montre les dependances)
ldd /bin/bash
# Copier chaque .so listee dans lib/ ou lib64/
# Entrer dans le chroot
sudo chroot /tmp/mon-root /bin/bash
# Tu es maintenant "enferme" dans /tmp/mon-root
# / est maintenant /tmp/mon-root du point de vue du processus
ls /
# bin lib lib64 proc
Le problème de chroot : c'est facile d'en sortir. Un processus root dans un chroot peut remonter l'arborescence avec des appels système. Ce n'est pas un mecanisme de sécurité, c'est juste un changement de perspective du système de fichiers.
Les namespaces : l'isolation réelle
Les namespaces sont le vrai mecanisme d'isolation du kernel Linux. Chaque namespace isole une ressource système spécifique :
| Namespace | Isole | Effet |
|---|---|---|
| PID | Processus | Le conteneur voit seulement ses propres processus |
| Network | Réseau | Le conteneur a ses propres interfaces, IPs, ports |
| Mount | Points de montage | Le conteneur a son propre système de fichiers |
| UTS | Hostname | Le conteneur a son propre nom de machine |
| User | UIDs/GIDs | Root dans le conteneur n'est pas root sur l'hote |
| IPC | Communication inter-processus | Mémoire partagee isolee |
| Cgroup | Vue des cgroups | Le conteneur ne voit que ses propres limites |
PID namespace
bash# Lancer un shell dans un nouveau PID namespace
sudo unshare --pid --fork --mount-proc bash
# Dans ce shell, les processus sont isoles
ps aux
# Tu ne vois que bash et ps, pas les processus de l'hote
# PID 1 dans ce namespace est ton bash
echo $
# 1
C'est exactement ce que fait Docker. Ton processus dans le conteneur voit PID 1, mais sur l'hote, il a un PID normal comme n'importe quel autre processus.
Network namespace
bash# Creer un namespace reseau
sudo ip netns add mon-ns
# Lancer une commande dedans
sudo ip netns exec mon-ns ip addr
# Seulement l'interface loopback, pas de connexion au reseau de l'hote
# Creer une paire d'interfaces virtuelles (veth)
sudo ip link add veth0 type veth peer name veth1
# Mettre une extremite dans le namespace
sudo ip link set veth1 netns mon-ns
# Configurer les IPs
sudo ip addr add 10.0.0.1/24 dev veth0
sudo ip link set veth0 up
sudo ip netns exec mon-ns ip addr add 10.0.0.2/24 dev veth1
sudo ip netns exec mon-ns ip link set veth1 up
sudo ip netns exec mon-ns ip link set lo up
# Tester la connexion
sudo ip netns exec mon-ns ping 10.0.0.1
# Nettoyer
sudo ip netns delete mon-ns
C'est comme ca que Docker créé le réseau de ses conteneurs. La commande docker network manipule des namespaces réseau et des paires veth sous le capot.
Mount namespace
bash# Nouveau mount namespace
sudo unshare --mount bash
# Les montages faits ici sont invisibles depuis l'hote
mount -t tmpfs none /tmp
# Ce /tmp est propre a ce namespace
cgroups : limiter les ressources
Les namespaces isolent la visibilité. Les cgroups (control groups) limitent la consommation de ressources : CPU, mémoire, I/O disque.
bash# Voir les cgroups actuels
ls /sys/fs/cgroup/
# Creer un cgroup v2
sudo mkdir /sys/fs/cgroup/mon-groupe
# Limiter la memoire a 100 Mo
echo "104857600" | sudo tee /sys/fs/cgroup/mon-groupe/memory.max
# Limiter le CPU a 50%
echo "50000 100000" | sudo tee /sys/fs/cgroup/mon-groupe/cpu.max
# Ajouter un processus au cgroup
echo $ | sudo tee /sys/fs/cgroup/mon-groupe/cgroup.procs
# Verifier les limites
cat /sys/fs/cgroup/mon-groupe/memory.max
cat /sys/fs/cgroup/mon-groupe/cpu.max
Quand tu fais docker run --memory=512m --cpus=0.5, Docker écrit exactement ces valeurs dans les fichiers de cgroup. Rien de magique.
Construire un "conteneur" avec unshare
On peut combiner tout ca pour créer un conteneur rudimentaire :
bash# Creer un rootfs minimal (avec debootstrap sur Debian/Ubuntu)
sudo apt install debootstrap
sudo debootstrap bookworm /tmp/mon-conteneur http://deb.debian.org/debian
# Lancer un shell isole dans ce rootfs
sudo unshare \
--pid \
--fork \
--mount-proc \
--mount \
--uts \
--ipc \
--net \
chroot /tmp/mon-conteneur /bin/bash
# Dans ce shell :
hostname mon-conteneur
ps aux
# PID 1 est ton bash
# Pas d'acces reseau
# Pas de visibilite sur les processus de l'hote
# Systeme de fichiers isole
Ca ressemble beaucoup a un conteneur Docker, non ? C'est parce que c'en est un. Docker ajoute par dessus une gestion des images (les layers), un daemon, un format de configuration, et un ecosysteme. Mais le coeur, c'est exactement ca : namespaces + cgroups + un système de fichiers isole.
Sur paltemps.fr, j'utilise Docker en production. Mais avoir compris ces mecanismes m'a sauve plus d'une fois quand il fallait debugger des problèmes de réseau ou de limites de ressources sur les conteneurs.
Pourquoi ca compte pour toi
Quand tu sais ce qu'il y a sous le capot :
- Tu comprends pourquoi un conteneur démarré en millisecondes (c'est juste un processus, pas une VM)
- Tu peux debugger les problèmes réseau Docker en inspectant les namespaces
- Tu comprends les limites de ressources et pourquoi ton conteneur se fait tuer (OOM killer)
- Tu sais que root dans un conteneur peut etre dangereux si le namespace user n'est pas configure
- Tu peux lire les metriques dans
/sys/fs/cgroup/directement
bash# Voir le namespace d'un processus Docker
docker inspect --format '{{.State.Pid}}' mon-conteneur
# Disons que ca retourne 12345
ls -la /proc/12345/ns/
# Tu vois les namespaces du conteneur
# Entrer dans le namespace reseau d'un conteneur Docker
sudo nsenter -t 12345 -n ip addr
Résumé
chrootisole le système de fichiers mais n'est pas sécurisé seul- Les namespaces (PID, network, mount, user, UTS, IPC) isolent la visibilité des ressources
- Les cgroups limitent la consommation de CPU, mémoire et I/O
unsharecombine les namespaces pour créer un conteneur basique- Docker = namespaces + cgroups + images en couches + daemon + outillage
- Comprendre ces mecanismes aide a debugger les vrais problèmes en production
Article précédent : Les gestionnaires de paquets Article suivant : Securiser un serveur Linux