Linux pour les devs - 19 - Les conteneurs sans Docker

chroot, namespaces, cgroups et unshare : comprendre ce qu'est vraiment un conteneur Linux.

  1. 01 Linux pour les devs - 00 - Pourquoi Linux meme si tu codes sur Mac ou Windows
  2. 02 Linux pour les devs - 01 - Le terminal, bash et zsh
  3. 03 Linux pour les devs - 02 - Fichiers et répertoires
  4. 04 Linux pour les devs - 03 - Permissions et droits d'acces
  5. 05 Linux pour les devs - 04 - Utilisateurs, groupes et sudo
  6. 06 Linux pour les devs - 05 - nano, vim, sed et awk
  7. 07 Linux pour les devs - 06 - Pipes et redirections
  8. 08 Linux pour les devs - 07 - grep et find en profondeur
  9. 09 Linux pour les devs - 08 - Les processus : comprendre ce qui tourne sur ta machine
  10. 10 Linux pour les devs - 09 - systemd : gerer tes services comme un pro
  11. 11 Linux pour les devs - 10 - Le réseau : comprendre ce qui passe par le fil
  12. 12 Linux pour les devs - 11 - Le firewall : contrôler qui entre et qui sort
  13. 13 Linux pour les devs - 12 - SSH : l'acces distant sécurisé
  14. 14 Linux pour les devs - 13 - Les variables d'environnement : configurer sans hardcoder
  15. 15 Linux pour les devs - 14 - Scripts bash : automatiser pour ne plus se répéter
  16. 16 Linux pour les devs - 15 - cron : les taches planifiees
  17. 17 Linux pour les devs - 16 - Les logs : lire, filtrer, comprendre
  18. 18 Linux pour les devs - 17 - Stockage et disques
  19. 19 Linux pour les devs - 18 - Les gestionnaires de paquets
  20. 20 Linux pour les devs - 19 - Les conteneurs sans Docker
  21. 21 Linux pour les devs - 20 - Securiser un serveur Linux
  22. 22 Linux pour les devs - 21 - Performance et diagnostic
  23. 23 Linux pour les devs - 22 - tmux : le multiplexeur de terminal
  24. 24 Linux pour les devs - 23 - Glossaire Linux

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é

  • chroot isole 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
  • unshare combine 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

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.