Namespaces
| Fiche express | |
|---|---|
| Domaine | Isolation noyau — base des conteneurs |
| Commande | unshare, nsenter, ip netns
|
| Contexte | Primitive du noyau Linux (PID, réseau, mount, UTS, IPC, user…) sur laquelle Docker, LXC et Kubernetes construisent l'isolation des conteneurs |
| Voir aussi | Cgroups (limitation de ressources, complémentaire à l'isolation) |
Les namespaces sont un mécanisme du noyau Linux qui isole une ressource globale du système (identifiants de processus, interfaces réseau, points de montage, nom d'hôte, files IPC, utilisateurs…) de sorte qu'un groupe de processus en voit une instance propre, indépendante du reste de la machine. C'est la brique de base sur laquelle reposent les conteneurs : un conteneur Docker/LXC n'est rien de plus qu'un ou plusieurs processus lancés dans leur propre jeu de namespaces, combinés à des cgroups pour la limitation de ressources (CPU/RAM) et à un système de fichiers superposé (overlayfs) pour le rootfs.
Principaux types de namespaces : pid (arborescence de processus propre, PID 1 isolé), net (interfaces, routes, règles iptables propres), mnt (points de montage propres), uts (hostname/domainname propres), ipc (files de messages/sémaphores/mémoire partagée propres) et user (mapping UID/GID propre, permet un root dans le conteneur qui n'est pas root sur l'hôte).
Créer un namespace réseau à la main (netns + veth + bridge)
Exemple complet de ce que fait Docker en interne à chaque création de conteneur : deux namespaces réseau isolés, reliés à un bridge par des paires veth, avec NAT pour l'accès sortant.
#!/usr/bin/bash
## Variables namespace
NS1="x1"
NS2="x2"
## Variables des paires veth (cables virtuels)
VETH1="xeth1"
VETH2="xeth2"
## Variables des interfaces cote conteneur
VPEER1="xpeer1"
VPEER2="xpeer2"
## Variables IP des conteneurs
VPEER_ADDR1="10.11.0.10"
VPEER_ADDR2="10.11.0.20"
## Variables du bridge
BR_ADDR="10.11.0.1"
BR_DEV="xavki0"
## Creation des namespaces
ip netns add $NS1
ip netns add $NS2
## Creation des paires veth (cables) et de leurs interfaces
ip link add ${VETH1} type veth peer name ${VPEER1}
ip link add ${VETH2} type veth peer name ${VPEER2}
## Deplacement d'une extremite de chaque veth dans le namespace correspondant
ip link set ${VPEER1} netns ${NS1}
ip link set ${VPEER2} netns ${NS2}
## Activation des veth cote hote
ip link set ${VETH1} up
ip link set ${VETH2} up
ip --netns ${NS1} a
ip --netns ${NS2} a
## Activation des interfaces a l'interieur des namespaces (loopback + veth)
ip netns exec ${NS1} ip link set lo up
ip netns exec ${NS2} ip link set lo up
ip netns exec ${NS1} ip link set ${VPEER1} up
ip netns exec ${NS2} ip link set ${VPEER2} up
## Attribution d'une IP a chaque interface cote conteneur
ip netns exec ${NS1} ip addr add ${VPEER_ADDR1}/16 dev ${VPEER1}
ip netns exec ${NS2} ip addr add ${VPEER_ADDR2}/16 dev ${VPEER2}
## Creation et activation du bridge
ip link add ${BR_DEV} type bridge
ip link set ${BR_DEV} up
## Rattachement des veth cote hote au bridge
ip link set ${VETH1} master ${BR_DEV}
ip link set ${VETH2} master ${BR_DEV}
## Attribution de l'IP du bridge
ip addr add ${BR_ADDR}/16 dev ${BR_DEV}
## Route par defaut de chaque namespace via le bridge
ip netns exec ${NS1} ip route add default via ${BR_ADDR}
ip netns exec ${NS2} ip route add default via ${BR_ADDR}
## Acces externe : forwarding IP + NAT (equivalent du bridge docker0 + MASQUERADE de Docker)
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s ${BR_ADDR}/16 ! -o ${BR_DEV} -j MASQUERADE
unshare et nsenter
unshare crée directement un ou plusieurs nouveaux namespaces et y lance une commande (sans passer par ip netns) — pratique pour un test rapide d'isolation sans toucher à Docker :
unshare --uts --pid --net --mount --fork bash # nouveau hostname + PID 1 + reseau + points de montage isoles
nsenter fait l'inverse : il entre dans le(s) namespace(s) d'un processus déjà existant. Utile pour déboguer un conteneur ou un netns manuel depuis l'hôte, y compris quand le conteneur n'a pas de shell utilisable en interne :
nsenter --net=/run/netns/netns0 bash # shell dans le namespace reseau netns0
nsenter --net=/run/netns/netns0 ping -c 2 172.18.0.20 # tester la connectivite depuis ce namespace
nsenter --net=/run/netns/netns0 ip route add default via 172.18.0.1
# equivalent pour un conteneur Docker : recuperer son PID puis entrer dans ses namespaces
PID=$(docker inspect --format '{{.State.Pid}}' mon_conteneur)
nsenter -t "$PID" -n -m -u -i -p sh