Skip to content

Cgroups namespaces conteneurisation Linux : fonctionnement

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.5 autorise environ 1,5 unité de processeur répartie dans le temps.
  • Mémoire : --memory=512m fixe une limite de 512 mébioctets, après laquelle le noyau peut déclencher un dépassement ou tuer un processus.
  • Processus : --pids-limit=100 ré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 NOM affiche les limites, les montages, le réseau et les capacités déclarées.
  • docker stats montre en temps réel le CPU, la mémoire, le réseau et les E/S observés par Docker.
  • docker top NOM liste les processus vus depuis le moteur, tandis que docker exec NOM ps montre 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.