tuto4: done

This commit is contained in:
nemunaire 2017-11-09 01:30:41 +01:00
commit a476cd8b6d
14 changed files with 643 additions and 331 deletions

View file

@ -1,53 +1,77 @@
\newpage
Les *namespaces*
================
Les espaces de noms -- *namespaces* {#namespaces}
===================================
## Introduction
Les espaces de noms du noyau, les *namespaces*, permettent de dupliquer
certaines structures du noyau, dans le but de les isoler d'un groupe de
processus à un autre.
Les espaces de noms du noyau, les *namespaces*, permettent de
dupliquer certaines structures, habituellement considérées uniques
pour le noyau, dans le but de les isoler d'un groupe de processus à un
autre.
On en dénombre 7 depuis Linux 4.6 : `CGroup`, `IPC`, `network`, `mount`, `PID`,
`user` et `UTS`.
On en dénombre sept depuis Linux 4.6 : `cgroup`, `IPC`, `network`,
`mount`, `PID`, `user` et `UTS`.
La notion d'espace de noms est relativement nouvelle et a été intégrée
progressivement au sein du noyau Linux. Aussi, toutes les structures
ne sont pas encore *containerisables* :
[le document fondateur](https://www.kernel.org/doc/ols/2006/ols2006v1-pages-101-112.pdf)
parle ainsi d'isoler les périphériques, ou encore l'horloge. Pour ce
dernier,
[un patch a même déjà été proposé](https://lwn.net/Articles/179825/).
### `mount` *namespaces*
### L'espace de noms `mount` {#mount-ns}
Depuis Linux 2.4.19.
Isole la liste des points de montage.
Cet espace de noms isole la liste des points de montage.
Chaque processus d'un *namespace* différent peut monter, démonter et
réorganiser à sa guise les points de montage. Une partition ne sera donc pas
nécessairement démonté après un appel à `umount(2)`, elle le sera lorsqu'elle
Chaque processus appartenant à un *namespace* différent peut monter, démonter
et réorganiser à sa guise les points de montage, sans que cela n'ait d'impact
sur les processus hors de cet espace de noms. Une partition ne sera donc pas
nécessairement démontée après un appel à `umount(2)`, elle le sera lorsqu'elle
aura effectivement été démontée de chaque *namespace* dans lequel elle était
montée.
Attention il convient cependant de prendre garde aux types de liaison existant
entre vos points de montage (voir la partie sur
[les particularités des points de montage](#mount)), car les montages et
démontages pourraient alors être répercutés dans l'espace de noms parent.
### `UTS` *namespaces*
Une manière rapide pour s'assurer que nos modifications ne sortiront pas de
notre *namespace* est d'appliquer le type esclave à l'ensemble de nos points de
montage, récursivement, dès que l'on est entré dans notre nouvel espace de
noms.
```shell
mount --make-rslave /
```
### L'espace de noms `UTS` {#uts-ns}
Depuis Linux 2.6.19.
Isole le nom de machine et son domaine NIS.
Cet espace de noms isole le nom de machine et son domaine NIS.
### `IPC` *namespaces*
### L'espace de noms `IPC` {#ipc-ns}
Depuis Linux 2.6.19.
Isole les objets IPC et les files de messages POSIX.
Cet espace de noms isole les objets IPC et les files de messages POSIX.
Une fois le *namespace* attaché à un processus, il ne peut alors plus parler
qu'avec les autres processus de son *namespace*.
qu'avec les autres processus de son espace de noms (lorsque ceux-ci passent par
l'API IPC du noyau).
### `PID` *namespaces*
### L'espace de noms `PID`
Depuis Linux 2.6.24.
Isole la liste des processus et virtualise leurs numéros.
Cet espace de noms isole la liste des processus et virtualise leurs numéros.
Une fois dans un espace, le processus ne voit que le sous-arbre de processus
également attachés à son espace. Il s'agit d'un sous-ensemble de l'arbre global
@ -55,22 +79,22 @@ de PID : les processus de tous les PID *namespaces* apparaissent donc dans
l'arbre initial.
Pour chaque nouvel espace de noms de processus, une nouvelle numérotation est
initié ; ainsi, le premier processus de cet espace porte le numéro 1 et aura
les mêmes propriétés que le processus `init` usuel ; entre autres, si un
initiée. Ainsi, le premier processus de cet espace porte le numéro 1 et aura
les mêmes propriétés que le processus `init` usuel ; entre autre, si un
processus est rendu orphelin dans ce *namespace*, il devient un fils de ce
processus, et non un fils de l'`init` de l'arbre global.
### `network` *namespaces*
### L'espace de nom `network`
Depuis Linux 2.6.29.
Fourni une isolation pour toutes les ressources associées aux réseaux : les
interfaces, les piles protocolaires IPv4 et IPv6, les tables de routage,
pare-feu, ports numérotés, etc.
Cet espace de noms fournit une isolation pour toutes les ressources associées
aux réseaux : les interfaces, les piles protocolaires IPv4 et IPv6, les tables
de routage, règles pare-feu, ports numérotés, etc.
Une interface réseau (`eth0`, `wlan0`, ...) ne peut se trouver que dans un seul
*namespace* à la fois, mais il est possible de les déplacer.
espace de noms à la fois. Il est par contre possible de les déplacer.
Lorsque le *namespace* est libéré (généralement lorsque le dernier processus
attaché à cet espace de noms se termine), les interfaces qui le composent sont
@ -78,30 +102,32 @@ ramenées dans l'espace initial (et non pas dans l'espace parent, en cas
d'imbrication).
### `user` *namespaces*
### L'espace de noms `user`
Depuis Linux 3.8.
Isole la liste des utilisateurs, des groupes, leurs identifiants, les
capabilities, la racine et le trousseau de clefs du noyau.
Cet espace de noms isole la liste des utilisateurs, des groupes, leurs
identifiants, les *capabilities*, la racine et le trousseau de clefs du noyau.
La principale caractéristique est que les identifiants d'utilisateur et de
groupe pour un processus peuvent être différent entre l'intérieur et
l'extérieur du conteneur. Il est alors possible, alors que l'on est un simple
utilisateur à l'extérieur du *namespace*, d'avoir l'UID 0 dans le conteneur.
groupe pour un processus peuvent être différents entre l'intérieur et
l'extérieur de l'espace de noms. Il est donc possible, alors que l'on est un
simple utilisateur à l'extérieur du *namespace*, d'avoir l'UID 0 dans le
conteneur.
### `CGroup` *namespaces*
### L'espace de noms `cgroup` {#cgroup-ns}
Depuis Linux 4.6.
Isole la vue de la racine des *Control Group* en la plaçant sur un
sous-groupe de l'arborescence.
Cet espace de noms filtre l'arborescence des *Control Group* en changeant la
racine de l'arborescence des cgroups. Au sein d'un *namespace*, la racine vue
correspond en fait à un sous-groupe de l'arborescence globale.
Ainsi, un processus dans un `CGroup` *namespace* ne peut pas voir le contenu
des sous-groupes parents (pouvant laisser fuiter des informations sur le reste
du système). Cela peut également permettre de faciliter la migration de
processus (d'un système à un autre) : l'arborescences des cgroups n'a alors
processus (d'un système à un autre) : l'arborescence des cgroups n'a alors
plus d'importance car le processus ne voit que son groupe.
@ -112,38 +138,34 @@ plus d'importance car le processus ne voit que son groupe.
De la même manière que l'on peut utiliser l'appel système `chroot(2)` depuis un
shell via la commande `chroot(1)`, la commande `unshare(1)` permet de faire le
nécessaire pour appeler l'appel système `unshare(2)`, puis, tout comme
nécessaire pour lancer l'appel système `unshare(2)`, puis, tout comme
`chroot(1)`, exécuter le programme passé en paramètre.
En fonction des options qui lui sont passées, `unshare(1)` va créer le/les
nouveaux *namespaces* et placer le processus dedans.
Par exemple, nous pouvons modifier sans crainte le nom de notre machine, si
nous sommes passé dans un autre *namespace* `UTS` :
nous sommes passés dans un autre *namespace* `UTS` :
<div lang="en-US">
```shell
42sh# hostname --fqdn
koala.zoo.paris
42sh# sudo unshare -u /bin/bash
bash# hostname --fqdn
koala.zoo.paris
bash# hostname lynx.zoo.paris
bash# hostname --fqdn
lynx.zoo.paris
bash# exit
42sh# hostname --fqdn
koala.zoo.paris
42sh# hostname --fqdn
koala.zoo.paris
42sh# sudo unshare -u /bin/bash
bash# hostname --fqdn
koala.zoo.paris
bash# hostname lynx.zoo.paris
bash# hostname --fqdn
lynx.zoo.paris
bash# exit
42sh# hostname --fqdn
koala.zoo.paris
```
</div>
Nous avons pu ici modifier le nom de machine, sans que cela n'affecte notre
Nous avons pu ici modifier le nom de la machine, sans que cela n'affecte notre
machine hôte.
Essayons maintenant avec d'autres options de notre programme pour voir les
effets produits : par exemple, comparons un `ip address` à l'extérieur et à
l'intérieur d'un `unshare -n`.
### Les appels systèmes
@ -166,8 +188,9 @@ avec d'autres `flags` attendu par la fonction.
Les mêmes `flags` sont utilisés lors des appels à `unshare(2)` ou `setns(2)`.
Pour créer un nouveau processus qui sera à la fois dans un nouvel *namespace*
réseau et dans un nouveau *namespace* CGroup, on écrirait un code similaire à :
Pour créer un nouveau processus qui sera à la fois dans un nouvel espace de
noms réseau et dans un nouveau *namespace* `cgroup`, on écrirait un code
similaire à :
<div lang="en-US">
```c
@ -188,67 +211,10 @@ pid_t pid = clone(do_execvp,
Le premier argument est un pointeur sur fonction. Il s'agit de la fonction qui
sera appelée par le nouveau processus.
## Comparaison de *namespace*
Les *namespaces* d'un programme sont exposés sous forme de liens symboliques
dans le répertoire `/proc/<PID>/ns/`.
Deux programmes qui partagent un même *namespace* auront un lien vers la même
structure de données.
Écrivons un script ou un programme, `cmpns`, permettant de déterminer si deux
programmes s'exécutent dans les mêmes *namespaces*.
### Exemples
<div lang="en-US">
```sh
42sh$ ./cmpns $(pgrep influxdb) $(pgrep init)
- cgroup: differ
- ipc: differ
- mnt: differ
- net: differ
- pid: differ
- user: same
- uts: same
```
</div>
<div lang="en-US">
```sh
42sh$ ./cmpns $(pgrep init) self
- cgroup: same
- ipc: same
- mnt: same
- net: same
- pid: same
- user: same
- uts: same
```
</div>
Ici, `self` fait référence au processus actuellement exécuté.
Et pourquoi pas :
<div lang="en-US">
```sh
42sh$ unshare -m ./cmpns $$ self
- cgroup: same
- ipc: same
- mnt: differ
- net: same
- pid: same
- user: same
- uts: same
```
</div>
## Rejoindre un *namespace*
Rejoindre un *namespace* se fait en utilisant l'appel système `setns(2)`,
Rejoindre un espace de noms se fait en utilisant l'appel système `setns(2)`,
auquel on passe le *file descriptor* d'un des liens du dossier
`/proc/<PID>/ns/` :
@ -293,31 +259,15 @@ Dans un shell, on utilisera la commande `nsenter(1)` :
</div>
### `docker exec`
## Durée de vie d'un *namespace* {#ns-lifetime}
Si vous avez bien suivi jusque là, vous avez dû comprendre qu'un `docker exec`,
n'était donc rien de plus qu'un `nsenter(1)`.
Réécrivons, en quelques lignes, la commande `docker exec` !
Pour savoir si vous avez réussi, comparez les sorties des commandes :
- `ip address` ;
- `hostname` ;
- `mount` ;
- `pa -aux` ;
- ...
## Durée de vie d'un *namespace*
Le noyau tient à jour un compteur de référence pour chaque *namespace*. Dès
qu'une référence tombe à 0, le *namespace* est automatiquement libéré, les
Le noyau tient à jour un compteur de références pour chaque *namespace*. Dès
qu'une référence tombe à 0, l'espace de noms est automatiquement libéré, les
points de montage sont démontés, les interfaces réseaux sont réattribués à
l'espace de noms initial, ...
Ce compteur évolue selon plusieurs critères, et principalement selon le nombre
de processus qui l'utilisent. C'est-à-dire que, la plupart du temps, le
de processus qui l'utilise. C'est-à-dire que, la plupart du temps, le
*namespace* est libéré lorsque le dernier processus s'exécutant dedans se
termine.
@ -339,7 +289,8 @@ de fichier valide vers le *namespace* (pour passer à `setns(2)`).
### Faire persister un *namespace*
Il n'est pas possible de faire persister un namespace d'un reboot à l'autre.
Il n'est pas possible de faire persister un espace de noms d'un reboot à
l'autre.
Même en étant attaché à un fichier du disque, il s'agit d'un pointeur vers une
structure du noyau, qui ne persistera pas au redémarrage.
@ -349,12 +300,13 @@ structure du noyau, qui ne persistera pas au redémarrage.
Je vous recommande la lecture des *man* suivants :
* `namespaces(7)` : introduisant et énumérant les namespaces ;
* `namespaces(7)` : introduisant et énumérant les *namespaces* ;
Pour tout connaître en détails, [la série d'articles de Michael Kerrisk sur
les *namespaces*](https://lwn.net/Articles/531114/) est excellente ! Auquel il
faut ajouter [le petit dernier sur le CGroup
faut ajouter [le petit dernier sur le `cgroup`
*namespace*](https://lwn.net/Articles/621006/).
Cet article
[de Michael Crosby montrant l'utilisation de clone(2)](http://crosbymichael.com/creating-containers-part-1.html).
[Cet article de Michael Crosby montrant l'utilisation de clone(2)](http://crosbymichael.com/creating-containers-part-1.html)
est également des plus intéressants, pour ce qui concerne la programmation
plus bas-niveau.