Comprendre les Namespaces Linux

Les espaces de noms sont l'une des fonctionnalités du noyau Linux qui rendent les conteneurs possibles. Dans cet article, nous allons découvrir ce que sont les espaces de noms Linux et leur fonctionnement grâce à des explications simples, des illustrations et des exemples.

Comprendre les Namespaces Linux
Photo par Marc Basarab / Unsplash

Que sont les espaces de noms Linux

Espaces de noms Linux Peut être utilisé pour fournir aux processus une vue limitée de certains composants système. C'est un élément important pour isoler les processus les uns des autres. Les outils de conteneurisation comme Docker et Containerd exploitent les espaces de noms Linux pour isoler les conteneurs.

Présentation des espaces de noms Linux

Types d'espaces de noms Linux

Types d'espaces de noms Linux

Selon le composant système dont nous souhaitons restreindre l'affichage des processus, nous devons utiliser des types d'espaces de noms spécifiques. Les types d'espaces de noms disponibles sur le système sont les suivants :

$ lsns -o TYPE
TYPE
time      # Virtualizes the view on some of the system clocks values
ipc       # Virtualizes the view on System V IPC and POSIX message queues
cgroup    # Virtualizes the view on Cgroups
user      # Virtualizes the view on user and group IDs
net       # Virtualizes the view on Network devices, stacks, ports, etc
uts       # Virtualizes the view on hostname and NIS domain name
mnt       # Virtualizes the view on mount points
pid       # Virtualizes the view on process IDs

Pour une description détaillée de ce que fait chaque type d'espace de noms, jetez un œil à ceci :

Liste des espaces de noms

La commande « lsns » permet de lister les espaces de noms utilisés par les processus. Son exécution listera par défaut les espaces de noms visibles par l'utilisateur actuel (lisibles depuis le procfs pseudo-système de fichiers)

# Who is the current user?
$ whoami
ubuntu

# Listing namespaces as user ubuntu
$ lsns
        NS TYPE   NPROCS   PID USER   COMMAND
4026531834 time        3 74838 ubuntu /lib/systemd/systemd --user
4026531835 cgroup      3 74838 ubuntu /lib/systemd/systemd --user
4026531836 pid         3 74838 ubuntu /lib/systemd/systemd --user
4026531837 user        3 74838 ubuntu /lib/systemd/systemd --user
4026531838 uts         3 74838 ubuntu /lib/systemd/systemd --user
4026531839 ipc         3 74838 ubuntu /lib/systemd/systemd --user
4026531840 net         3 74838 ubuntu /lib/systemd/systemd --user
4026531841 mnt         3 74838 ubuntu /lib/systemd/systemd --user

# Switched to root user
$ whoami
root

# Listing namespaces as user root
$ lsns
        NS TYPE   NPROCS     PID USER             COMMAND
4026531835 cgroup    129       1 root             /sbin/init
4026531836 pid       126       1 root             /sbin/init
4026531837 user      129       1 root             /sbin/init
4026531838 uts       123       1 root             /sbin/init
4026531839 ipc       126       1 root             /sbin/init
4026531840 mnt       118       1 root             /sbin/init
4026531860 mnt         1      21 root             kdevtmpfs
4026531992 net       126       1 root             /sbin/init
4026532144 mnt         1 1770518 root             /lib/systemd/systemd-udevd
4026532146 mnt         3 1766779 root             nginx: master process nginx -g daemon off;
4026532147 uts         3 1766779 root             nginx: master process nginx -g daemon off;
4026532149 ipc         3 1766779 root             nginx: master process nginx -g daemon off;
4026532150 pid         3 1766779 root             nginx: master process nginx -g daemon off;
4026532152 net         3 1766779 root             nginx: master process nginx -g daemon off;
4026532204 mnt         1     697 root             /usr/sbin/irqbalance --foreground
4026532206 mnt         1     745 root             /usr/sbin/ModemManager
4026532227 mnt         1 1770505 systemd-timesync /lib/systemd/systemd-timesyncd
4026532228 mnt         1 1770508 systemd-network  /lib/systemd/systemd-networkd
4026532229 uts         1 1770518 root             /lib/systemd/systemd-udevd
4026532231 mnt         1 1770495 systemd-resolve  /lib/systemd/systemd-resolved
4026532232 mnt         1 1770551 root             /lib/systemd/systemd-logind
4026532233 uts         1 1770505 systemd-timesync /lib/systemd/systemd-timesyncd
4026532234 uts         1 1770551 root             /lib/systemd/systemd-logind

Pour chaque espace de noms dans la sortie de la commande 'lsns', nous voyons de gauche à droite :

  • NS - l'ID de l'espace de noms
  • TYPE - le type d'espace de noms
  • NPROCS - le nombre de processus dans l'espace de noms
  • PID - l'ID de processus le plus bas dans l'espace de noms
  • USER - le nom d'utilisateur du PID
  • COMMANDE - la commande du PID

Pour en savoir plus sur les champs disponibles qui peuvent être affichés, utilisez « lsns --help ».

Espaces de noms du système initial

Le système Linux utilise initialement des espaces de noms de chaque type. Pour le prouver, listons les espaces de noms du premier processus du système (PID 1) :

# Listing namespaces of process with PID 1
(root)$ lsns -p 1
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      178   1 root /sbin/init
4026531835 cgroup    178   1 root /sbin/init
4026531836 pid       123   1 root /sbin/init
4026531837 user      178   1 root /sbin/init
4026531838 uts       120   1 root /sbin/init
4026531839 ipc       123   1 root /sbin/init
4026531840 mnt       115   1 root /sbin/init
4026531992 net       123   1 root /sbin/init

Lors du processus de démarrage de Linux, une fois le noyau chargé en RAM par le chargeur de démarrage, le premier processus créé pour initialiser le système a le PID 1. Comme le montre le résultat de la commande précédente, le premier processus du système appartient aux espaces de noms de tous les types disponibles.

Liste des espaces de noms d'un processus spécifique

Pour lister les espaces de noms utilisés par un processus spécifique, nous pouvons utiliser la commande suivante :

# Syntax
# lsns -p PID

# Listing namespaces of process with PID 1
$ lsns -p 1
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time      129   1 root /sbin/init
4026531835 cgroup    129   1 root /sbin/init
4026531836 pid       126   1 root /sbin/init
4026531837 user      129   1 root /sbin/init
4026531838 uts       123   1 root /sbin/init
4026531839 ipc       126   1 root /sbin/init
4026531840 mnt       118   1 root /sbin/init
4026531992 net       126   1 root /sbin/init

# Listing namespaces of process with PID 1766779
$ lsns -p 1766779
        NS TYPE   NPROCS     PID USER COMMAND
4026531834 time      129       1 root /sbin/init
4026531835 cgroup    129       1 root /sbin/init
4026531837 user      129       1 root /sbin/init
4026532146 mnt         3 1766779 root nginx: master process nginx -g daemon off;
4026532147 uts         3 1766779 root nginx: master process nginx -g daemon off;
4026532149 ipc         3 1766779 root nginx: master process nginx -g daemon off;
4026532150 pid         3 1766779 root nginx: master process nginx -g daemon off;
4026532152 net         3 1766779 root nginx: master process nginx -g daemon off;

La dernière commande montre que le processus avec le PID 1766779 appartient aux espaces de noms système par défaut pour « time », « cgroup » et « user » (car les ID de processus associés à ces espaces de noms sont 1) mais fait partie d'espaces de noms supplémentaires pour les autres types d'espaces de noms (mnt, uts, ipc, pid et net).

Lister tous les processus d'un espace de noms

Pour lister tous les processus appartenant à un espace de noms spécifique, nous pouvons utiliser la commande suivante :

# Syntaxt
# lsns NAMESPACE_ID

# Listing processes belonging to namespace with ID 4026531836
$ lsns 4026531836
    PID    PPID USER   COMMAND
1739571       1 ubuntu /lib/systemd/systemd --user
1770724 1770723 ubuntu -bash
1772249 1770724 ubuntu └─lsns 4026531836

Pour chaque processus dans la sortie de la commande de listage, nous voyons de gauche à droite :

  • PID - L'ID du processus
  • PPID - L'ID du processus parent
  • UTILISATEUR - Le nom de l'utilisateur propriétaire du processus
  • COMMANDE - La commande du processus

Liste des processus dans les mêmes espaces de noms que PID

Pour lister les processus appartenant au même espace de noms qu'un autre processus, nous pouvons utiliser les commandes suivantes :

# Syntax 1 (consider namespaces of all types)
# pgrep --ns PID -a 

# Example 1:
# Listing processes belonging to same namespaces 
# as process with ID 1739571 (consider namespaces of all types)

$ pgrep --ns 1739571 -a
1739571 /lib/systemd/systemd --user
1770724 -bash

# Syntax 2 (consider namespaces of types NSTYPE only)
# pgrep --ns PID -a --nslist NSTYPE 
# NSTYPE = time, cgroup, user, ...

# Example 2:
# Listing processes belonging to same namespaces 
# as process with ID 1739571 (consider namespaces of type user only)

$ pgrep --ns 1739571 -a --nslist user
1739571 /lib/systemd/systemd --user
1770724 -bash

# Example 3:
# Listing processes belonging to same namespaces 
# as process with ID 1739571 (consider namespaces
# of types user, ipc and pid only)

$ pgrep --ns 1739571 -a --nslist user,ipc,pid
1739571 /lib/systemd/systemd --user
1770724 -bash

Exécution de programmes dans de nouveaux espaces de noms Linux

Pour exécuter un programme dans de nouveaux espaces de noms, nous pouvons utiliser la commande « unshare ». Les types d'espaces de noms à créer pour l'exécution du programme sont spécifiés par un ou plusieurs indicateurs de la commande « unshare ». Voici quelques exemples.

Exécuter un programme dans un espace de noms nouvellement créé

Pour exécuter mon programme dans un espace de noms réseau nouvellement créé, utilisez :

unshare --net myprogram

Pour exécuter mon programme dans deux espaces de noms réseau et cgroup nouvellement créés, utilisez :

unshare --net --cgroup myprogram

En savoir plus sur la commande unshare

Pour obtenir la liste des indicateurs disponibles pour chaque type d'espace de noms, utilisez :

unshare --help

Lorsqu'un programme n'est pas spécifié pour la commande « unshare », le programme par défaut qui sera exécuté est celui spécifié par la variable d'environnement $SHELL (par défaut /bin/sh).

Exemple : exécuter bash dans un espace de noms réseau nouvellement créé

Pour exécuter le programme bash dans un nouvel espace de noms de type « net » par exemple, nous pouvons utiliser la commande suivante :

(root)$ unshare --net bash

Par défaut, à l'intérieur de ce nouvel espace de noms réseau, nous ne voyons que l'interface réseau de bouclage :

(root)$ ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

À l'intérieur de ce nouvel espace de noms réseau, les vues des tables de routage et d'arp sont également différentes de celles que nous voyons à l'extérieur de l'espace de noms... elles sont vides :

(root)$ route -n
(root)$ arp -a

Si nous exécutons la même commande en dehors de l'espace de noms, nous obtenons ceci :

(root)$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.18.208.1    0.0.0.0         UG    100    0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
172.18.0.0      0.0.0.0         255.255.0.0     U     0      0        0 br-7642594ed86e
172.18.208.0    0.0.0.0         255.255.240.0   U     0      0        0 eth0
172.18.208.1    0.0.0.0         255.255.255.255 UH    100    0        0 eth0

(root)$ arp -a
? (172.17.0.2) at 02:42:ac:11:00:02 [ether] on docker0
DESKTOP-0HQ48FL.mshome.net (172.18.208.1) at 00:15:5d:73:9d:ba [ether] on eth0
DESKTOP-0HQ48FL.mshome.net (172.18.208.1) at <incomplete> on br-7642594ed86e
test.mshome.net (172.18.208.73) at <incomplete> on br-7642594ed86e

Saisie des espaces de noms Linux existants

Pour exécuter un programme dans un ou plusieurs espaces de noms d'autres processus, nous pouvons utiliser la commande « nsenter » comme suit.

Exécuter des programmes dans d'autres espaces de noms de processus

Entrez tous les espaces de noms du processus avec le PID 8256 et exécutez la commande bash :

nsenter -t 8256 --all bash

Entrez les espaces de noms spécifiques d'autres processus

Entrez l'espace de noms de montage du processus avec le PID 8256 :

nsenter -t 8256 --mount

En savoir plus sur la commande nsenter

Pour obtenir la liste des indicateurs disponibles pour chaque type d'espace de noms, utilisez :

nsenter --help

Lorsqu'un programme n'est pas spécifié pour la commande 'nsenter', le programme par défaut qui sera exécuté est celui spécifié par la variable d'environnement $SHELL (par défaut /bin/sh).

Exploration des espaces de noms d'un conteneur Docker

# No container running yet
$ ps aux | grep nginx | grep -v grep

# Nginx container creation
$ docker run -d nginx
64c34cc1ded89d9f5604eecc861bd4df0c85494649090eb2b3dc171f2a92b644

# Here it is
$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS     NAMES
64c34cc1ded8   nginx     "/docker-entrypoint.…"   39 seconds ago   Up 36 seconds   80/tcp    awesome_banzai

# Here are the processes created by the container
$ ps aux | grep nginx | grep -v grep
root        8256  0.0  0.1  11456  7472 ?        Ss   May19   0:00 nginx: master process nginx -g daemon off;
systemd+    8316  0.0  0.0  11920  2892 ?        S    May19   0:00 nginx: worker process
systemd+    8317  0.0  0.0  11920  2892 ?        S    May19   0:00 nginx: worker process

Les processus utilisant la commande « nginx » proviennent du conteneur Docker. Listons les espaces de noms de l'un de ces processus :

$ lsns -p 8256
        NS TYPE   NPROCS   PID USER COMMAND
4026531834 time      122     1 root /sbin/init
4026531835 cgroup    122     1 root /sbin/init
4026531837 user      122     1 root /sbin/init
4026532155 mnt         3  8256 root nginx: master process nginx -g daemon off;
4026532156 uts         3  8256 root nginx: master process nginx -g daemon off;
4026532157 ipc         3  8256 root nginx: master process nginx -g daemon off;
4026532158 pid         3  8256 root nginx: master process nginx -g daemon off;
4026532160 net         3  8256 root nginx: master process nginx -g daemon off;

La vue de ce processus sur les composants système est limitée par des espaces de noms de types autres que time, cgroup et user. Comme indiqué dans la commande précédente, les espaces de noms auxquels appartient le processus avec le PID 8256 pour time, cgroup et user sont les espaces de noms système initiaux contenant le PID 1 (commande /sbin/init). Par conséquent, les composants système hôtes associés à ces types d'espaces de noms ne sont pas isolés des processus conteneurs Nginx.

Les processus de conteneur nginx appartiennent aux espaces de noms nouvellement créés de types mnt, uts, ipc, pid et net (le PID le plus bas des espaces de noms correspond au PID nginx), et par conséquent, seuls les composants du système hôte liés à ces types d'espaces de noms sont isolés des processus de conteneur nginx.

Pour l'espace de noms de type 'net' (réseau) par exemple, voici les interfaces réseau que le processus nginx (PID 8256) voit :

$ nsenter -t 8256 --net
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

et les interfaces réseau qui sont réellement disponibles sur le système hôte :

$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 52:54:00:2c:e2:ea brd ff:ff:ff:ff:ff:ff
    inet 172.19.22.97/20 brd 172.19.31.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 172.18.208.73/20 brd 172.18.223.255 scope global dynamic eth0
       valid_lft 84950sec preferred_lft 84950sec
    inet6 fe80::5054:ff:fe2c:e2ea/64 scope link
       valid_lft forever preferred_lft forever
3: br-7642594ed86e: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:d2:37:fb:2a brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global br-7642594ed86e
       valid_lft forever preferred_lft forever
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:24:2a:a3:ce brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:24ff:fe2a:a3ce/64 scope link
       valid_lft forever preferred_lft forever
6: veth5b9d547@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether ea:b1:a8:41:e8:b3 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::e8b1:a8ff:fe41:e8b3/64 scope link
       valid_lft forever preferred_lft forever

Cela démontre que le conteneur possède sa propre vue du réseau (périphériques, table de routage, etc.), isolée des composants réseau de l'hôte. Le même type d'isolation s'applique également aux systèmes de fichiers montés sur le conteneur.espace de noms mnt), les processus qu'il voit (espace de noms pid), nom d'hôte (espace de noms uts) et IPC (espace de noms ipc).


C'est tout. J'espère que vous comprenez mieux les espaces de noms Linux maintenant.

Vous souhaitez signaler une erreur ou poser une question ? N'hésitez pas à m'envoyer un e-mail à gmkziz@hackerstack.org. Je serai ravi de répondre.

Si vous aimez mes articles, pensez à vous inscrire à ma newsletter afin de recevoir les derniers articles dès qu'ils sont disponibles.

Prenez soin de vous, continuez à apprendre et à bientôt pour le prochain post 🚀