L’ESSENTIEL
La conteneurisation Linux repose sur deux mécanismes distincts : les namespaces isolent ce qu’un processus peut voir, tandis que les cgroups limitent et mesurent les ressources qu’il peut consommer.
- Un namespace peut isoler les processus, le réseau, les montages, les identifiants utilisateurs et les communications interprocessus.
- Un cgroup peut contrôler le CPU, la mémoire, les entrées-sorties et le nombre de processus.
- Docker partage le noyau Linux avec l’hôte : un conteneur n’est donc pas une machine virtuelle.
- L’isolation n’est pas une frontière absolue : les privilèges, les capabilities, le noyau et les volumes montés restent déterminants.
La variable qui change le niveau de protection est le profil de lancement du conteneur, notamment ses privilèges, ses capacités Linux et ses accès au système hôte.
Pourquoi Docker combine namespaces et cgroups ?
Docker combine ces mécanismes parce qu’ils répondent à deux problèmes différents. Les namespaces donnent à chaque conteneur une vue restreinte du système, alors que les cgroups empêchent un service de monopoliser les ressources de la machine.
Une image Docker contient le code, les bibliothèques et les fichiers nécessaires à l’application. Lorsqu’elle est lancée, le moteur crée un ou plusieurs processus qui utilisent le noyau de l’hôte, contrairement à une machine virtuelle qui exécute un noyau invité séparé.
Le fonctionnement des conteneurs Docker dépend aussi d’un système de fichiers en couches, généralement basé sur OverlayFS sous Linux. Cette couche rend les images réutilisables, mais elle ne remplace ni les permissions Linux ni les contrôles d’accès du noyau.
Namespaces Linux : isoler la vue du système
Un namespace Linux crée une vue séparée d’une ressource du noyau pour un groupe de processus. Deux processus peuvent ainsi utiliser le même identifiant de processus à l’intérieur de leurs environnements respectifs, tout en restant distincts pour le système hôte.
La documentation du noyau Linux décrit les namespaces comme des espaces de noms imbriquables, associés à des ressources précises. Un processus peut appartenir simultanément à un namespace de processus, de réseau, de montage et d’autres types.
Les huit types de namespaces
| Type | Ressource isolée | Effet principal |
|---|---|---|
| PID | Identifiants de processus | Le processus initial peut apparaître avec le PID 1 dans le conteneur. |
| Mount | Points de montage | Le processus voit une arborescence de fichiers différente. |
| Network | Interfaces, routes et ports | Le conteneur possède sa propre pile réseau virtuelle. |
| User | UID, GID et capabilities | Un utilisateur root dans le conteneur peut être mappé vers un UID non privilégié sur l’hôte. |
| UTS, IPC, cgroup, time | Nom d’hôte, IPC, vue des cgroups et horloges | Ces espaces séparent des paramètres système ciblés. |
Le namespace time est disponible dans les noyaux Linux modernes, mais son usage dépend du moteur et de la configuration. Les anciens environnements documentent souvent seulement les 7 autres types, car le namespace time a été ajouté plus tard.
Ce qu’un namespace isole réellement
| Namespace | Ce que le processus voit | Ce qui reste partagé |
|---|---|---|
| PID | Une arborescence de processus locale | Le noyau et les processus visibles depuis l’hôte |
| Mount | Des points de montage propres | Le stockage physique et les volumes explicitement montés |
| Network | Interfaces, routes et sockets propres | La carte réseau physique et le noyau réseau de l’hôte |
| User | Des identifiants et privilèges mappés | Les contrôles du noyau et les permissions réelles sur l’hôte |
Un namespace limite donc la visibilité, mais il ne chiffre pas les données et ne corrige pas une vulnérabilité du noyau. Pour comprendre la relation entre processus, noyau et appels système, consultez cette présentation de l’architecture du système d’exploitation et du noyau.
Inspecter les namespaces d’un processus
Vous pouvez afficher les namespaces du shell courant avec ls -l /proc/$$/ns. Chaque lien symbolique indique un identifiant de namespace, par exemple pid:[4026531836], que le noyau utilise pour distinguer cet espace des autres.
La commande lsns, fournie par le paquet util-linux, donne une vue plus lisible : lsns -p PID liste les namespaces associés à un processus donné. La commande nsenter -t PID -m -u -i -n -p permet d’entrer dans plusieurs espaces d’un processus, mais elle doit être réservée à l’administration autorisée.
Cgroups : contrôler les ressources
Les control groups, ou cgroups, regroupent des processus afin de comptabiliser, prioriser ou limiter leur consommation. Ils évitent qu’une application consomme toute la mémoire ou tout le temps processeur disponible.
La documentation du noyau Linux distingue les contrôleurs de ressources, comme memory, cpu, pids et io. Docker utilise généralement l’interface cgroup exposée par systemd ou directement par le noyau, selon la distribution et le moteur de conteneurs.
CPU, mémoire, processus et I/O contrôlés par les cgroups
- CPU : une limite comme
--cpus=1.5autorise environ 1,5 unité de processeur répartie dans le temps. - Mémoire :
--memory=512mfixe une limite de 512 mébioctets, après laquelle le noyau peut déclencher un dépassement ou tuer un processus. - Processus :
--pids-limit=100réduit le risque d’une fork bomb en limitant le nombre de processus du conteneur. - Entrées-sorties : les contrôleurs d’E/S peuvent pondérer ou plafonner certaines opérations sur les périphériques compatibles.
Une limite mémoire n’est pas une garantie de performance : les caches, les fichiers temporaires et les besoins de l’application doivent être mesurés. Testez les valeurs avec la charge réelle et surveillez les événements OOM, c’est-à-dire Out Of Memory.
Cgroups v1 et v2 : quelles différences ?
| Aspect | Cgroups v1 | Cgroups v2 |
|---|---|---|
| Organisation | Hiérarchies séparées par contrôleur | Hiérarchie unifiée |
| Gestion | Interfaces et comportements variables | Interface plus cohérente |
| Adoption en 2026 | Présent pour compatibilité | Choix courant des distributions récentes |
| Vérification | /sys/fs/cgroup selon le montage |
Fichiers comme memory.max et cpu.max |
Vérifiez le mode actif avec stat -fc %T /sys/fs/cgroup ou examinez les contrôleurs disponibles dans /sys/fs/cgroup/cgroup.controllers. Les noms et chemins peuvent changer selon la distribution, systemd et le runtime utilisé.
Namespaces et cgroups : deux mécanismes complémentaires
Les namespaces répondent à la question « que le processus peut-il voir ? », tandis que les cgroups répondent à « quelles ressources peut-il consommer ? ». Utiliser l’un sans l’autre produit un environnement incomplet.
Isolation contre limitation des ressources
| Mécanisme | Objectif | Exemple Docker | Risque traité |
|---|---|---|---|
| Namespace PID | Masquer les processus | --pid |
Réduire la visibilité entre services |
| Namespace réseau | Séparer interfaces et routes | --network |
Limiter l’exposition réseau |
| Namespace mount | Séparer l’arborescence | --read-only |
Réduire les écritures |
| Cgroup mémoire | Limiter la RAM | --memory |
Réduire l’impact d’une fuite mémoire |
| Cgroup CPU | Limiter le temps processeur | --cpus |
Éviter la saturation CPU |
Comment Docker les associe pour créer un conteneur
Lors du lancement, Docker demande au runtime, souvent containerd et runc, de créer les namespaces nécessaires, de préparer le système de fichiers puis d’inscrire le processus dans un cgroup. Le runtime configure ensuite les interfaces réseau, les montages et les permissions avant d’exécuter le processus principal.
Cette chaîne explique pourquoi une erreur de permission peut venir du montage, du user namespace, d’AppArmor ou du cgroup, et pas seulement du fichier de configuration de l’application. Les règles de pare-feu restent indépendantes et méritent une configuration séparée, comme l’explique ce guide sur le fonctionnement d’iptables et d’UFW.
Que se passe-t-il lors du lancement d’un conteneur Docker ?
PID, mount, réseau et user namespaces
Le processus principal reçoit généralement le PID 1 dans son namespace PID. Il voit une arborescence de fichiers construite depuis l’image, une interface réseau virtuelle et un nom d’hôte associé au conteneur.
Le user namespace n’est pas activé de la même manière dans toutes les installations. Sans remappage, un processus root dans le conteneur peut disposer de capacités significatives dans son namespace, même si les appels restent filtrés par les capabilities, le profil de sécurité et les permissions de l’hôte.
Attribution du conteneur à un cgroup
Docker place les processus du conteneur dans un cgroup identifié par son nom ou son identifiant. Les fichiers memory.current, memory.max, cpu.stat et pids.current permettent de mesurer l’usage dans un environnement cgroup v2.
Observer l’isolation et les limites d’un conteneur
Commandes Docker et fichiers /proc à connaître
docker inspect NOMaffiche les limites, les montages, le réseau et les capacités déclarées.docker statsmontre en temps réel le CPU, la mémoire, le réseau et les E/S observés par Docker.docker top NOMliste les processus vus depuis le moteur, tandis quedocker exec NOM psmontre la vue interne du conteneur.readlink /proc/PID/ns/*compare les identifiants de namespaces entre l’hôte et le conteneur.
Vérifier les limites CPU et mémoire
Commencez par docker inspect -f '{{.HostConfig.Memory}} {{.HostConfig.NanoCpus}}' NOM. Une valeur mémoire nulle indique généralement l’absence de limite explicite, pas une réserve garantie pour le conteneur.
Sur l’hôte, recherchez le cgroup du processus avec cat /proc/PID/cgroup, puis lisez les fichiers correspondants dans /sys/fs/cgroup. Effectuez ces vérifications avec les droits d’administration nécessaires et ne modifiez pas directement ces fichiers sur une machine de production sans comprendre l’impact.
Mettre en place un exemple pratique avec Docker
Isoler les processus et le réseau
Cette commande lance un serveur Nginx avec une arborescence en lecture seule, sans privilèges additionnels et sur un réseau désactivé : docker run --rm --read-only --network none --cap-drop=ALL nginx:alpine. Elle convient seulement à un service qui n’a pas besoin d’écouter sur un port ou d’écrire dans son système de fichiers.
Pour un service réseau, utilisez plutôt un réseau dédié et publiez uniquement le port nécessaire. Ne montez pas /var/run/docker.sock dans un conteneur sans raison précise : ce socket peut donner un contrôle très large au moteur Docker.
Limiter la mémoire et le CPU
Un exemple de limite explicite est docker run --rm --memory=512m --cpus=1.0 --pids-limit=100 nginx:alpine. La limite de CPU réduit le temps processeur disponible, mais elle ne fixe pas une fréquence ni une latence constante.
Observez ensuite le service avec docker stats et testez son comportement lors d’une saturation contrôlée. Pour une charge critique, faites valider les seuils par l’administrateur de la plateforme avant de les appliquer en production.
Les limites de l’isolation des conteneurs
Ce que les namespaces et les cgroups ne protègent pas
Ils ne corrigent pas une faille du noyau, ne chiffrent pas les secrets et ne rendent pas sûr un volume monté en écriture. Un processus compromis peut aussi exploiter une mauvaise configuration réseau, une capability excessive ou une application vulnérable.
Un conteneur partage le noyau de l’hôte, ce qui distingue fondamentalement la conteneurisation d’une machine virtuelle. Pour des charges non fiables nécessitant une frontière plus forte, utilisez une machine virtuelle ou une solution de sandboxing conçue pour ce niveau de menace.
Privilèges, capabilities et user namespaces
Évitez --privileged, qui retire une grande partie des restrictions habituelles, et supprimez les capabilities inutiles avec --cap-drop=ALL, puis ajoutez seulement celles qui sont nécessaires. Exécutez aussi l’application avec un UID non root lorsque son image le permet.
Le user namespace améliore le remappage des identifiants, mais il peut modifier le comportement des volumes, des fichiers et des outils d’administration. Testez cette option sur une copie des données et documentez les UID utilisés avant tout déploiement.
Bonnes pratiques de sécurité et de dimensionnement
- Fixez des limites CPU, mémoire et PID adaptées à la charge mesurée, puis surveillez les dépassements.
- Supprimez les capabilities inutiles, évitez le mode privilégié et utilisez un profil AppArmor ou SELinux lorsque la distribution le fournit.
- Réduisez les volumes montés, préférez la lecture seule et ne partagez pas les sockets d’administration avec des applications.
- Maintenez le noyau, le runtime et les images à jour, puis vérifiez les changements de comportement lors d’une mise à niveau de distribution.
À retenir : namespaces, cgroups et conteneurisation
- Les namespaces isolent la vue des processus sur le système.
- Les cgroups mesurent et limitent les ressources consommées.
- Docker assemble ces mécanismes avec un système de fichiers, des capabilities et des profils de sécurité.
- La sécurité réelle dépend encore du noyau, des privilèges, des montages, du réseau et de la qualité de l’application.
