Add content for missing namespaces
This commit is contained in:
parent
ab3341bc54
commit
822dc619b8
7 changed files with 1650 additions and 6 deletions
305
tutorial/4/cgroupns.md
Normal file
305
tutorial/4/cgroupns.md
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
Le *namespace* `cgroup` {#cgroupns}
|
||||
-----------------------
|
||||
|
||||
### Introduction
|
||||
|
||||
L'espace de noms `cgroup`, introduit dans Linux 4.6, permet de virtualiser la
|
||||
vue d'un processus sur l'arborescence des *Control Groups*. Au sein d'un
|
||||
*namespace* `cgroup`, la racine vue par le processus correspond en fait à un
|
||||
sous-groupe de l'arborescence globale.
|
||||
|
||||
|
||||
### Pourquoi virtualiser les *cgroups* ?
|
||||
|
||||
Sans le *namespace* `cgroup`, un processus dans un conteneur pourrait :
|
||||
|
||||
- Voir l'arborescence complète des *cgroups* du système hôte
|
||||
- Obtenir des informations sur d'autres conteneurs
|
||||
- Comprendre la structure organisationnelle du système
|
||||
|
||||
Le *namespace* `cgroup` permet de :
|
||||
|
||||
- Masquer l'arborescence globale des *cgroups*
|
||||
- Faciliter la migration de conteneurs (l'arborescence reste cohérente)
|
||||
- Renforcer l'isolation entre conteneurs
|
||||
|
||||
|
||||
### Utilisation pratique
|
||||
|
||||
#### Observer les *cgroups* sans isolation\
|
||||
|
||||
Commençons par observer notre position dans l'arborescence des *cgroups* :
|
||||
|
||||
<div lang="en-US">
|
||||
```bash
|
||||
42sh$ cat /proc/self/cgroup
|
||||
0::/user.slice/user-1000.slice/session-3.scope
|
||||
```
|
||||
</div>
|
||||
|
||||
Cette ligne nous indique que nous nous trouvons dans le *cgroup* unifié
|
||||
`/user.slice/user-1000.slice/session-3.scope`.
|
||||
|
||||
Nous pouvons également explorer l'arborescence complète :
|
||||
|
||||
<div lang="en-US">
|
||||
```bash
|
||||
42sh$ ls /sys/fs/cgroup/
|
||||
cgroup.controllers cpuset.cpus.effective memory.pressure
|
||||
cgroup.max.depth cpuset.mems.effective sys-fs-fuse-connections.mount
|
||||
cgroup.max.descendants dev-hugepages.mount sys-kernel-config.mount
|
||||
cgroup.procs dev-mqueue.mount sys-kernel-debug.mount
|
||||
cgroup.stat init.scope sys-kernel-tracing.mount
|
||||
cgroup.subtree_control io.pressure system.slice
|
||||
cgroup.threads memory.numa_stat user.slice
|
||||
cpu.pressure memory.stat
|
||||
```
|
||||
</div>
|
||||
|
||||
|
||||
#### S'isoler dans un nouveau *namespace* `cgroup`\
|
||||
|
||||
Créons maintenant un nouveau *namespace* `cgroup` :
|
||||
|
||||
<div lang="en-US">
|
||||
```bash
|
||||
42sh$ unshare --cgroup /bin/bash
|
||||
incgroupns$ cat /proc/self/cgroup
|
||||
0::/
|
||||
```
|
||||
</div>
|
||||
|
||||
Notre processus se voit maintenant à la racine de l'arborescence des *cgroups* !
|
||||
En réalité, il se trouve toujours au même endroit dans l'arborescence globale,
|
||||
mais sa vue a été virtualisée.
|
||||
|
||||
Si nous regardons le contenu de `/sys/fs/cgroup/` :
|
||||
|
||||
<div lang="en-US">
|
||||
```bash
|
||||
incgroupns$ ls /sys/fs/cgroup/
|
||||
cgroup.controllers cgroup.threads io.pressure
|
||||
cgroup.events cpu.pressure memory.numa_stat
|
||||
cgroup.freeze cpu.stat memory.pressure
|
||||
cgroup.max.depth cpuset.cpus memory.stat
|
||||
cgroup.max.descendants cpuset.cpus.effective
|
||||
cgroup.procs cpuset.mems
|
||||
cgroup.stat cpuset.mems.effective
|
||||
cgroup.subtree_control io.stat
|
||||
```
|
||||
</div>
|
||||
|
||||
Nous voyons seulement notre sous-arbre, pas l'arborescence complète du système.
|
||||
|
||||
|
||||
#### Créer des sous-*cgroups*\
|
||||
|
||||
Dans notre nouveau *namespace*, nous pouvons créer des sous-groupes :
|
||||
|
||||
<div lang="en-US">
|
||||
```bash
|
||||
incgroupns$ mkdir /sys/fs/cgroup/mysubgroup
|
||||
incgroupns$ ls /sys/fs/cgroup/
|
||||
cgroup.controllers cgroup.threads mysubgroup
|
||||
cgroup.events cpu.pressure ...
|
||||
...
|
||||
```
|
||||
</div>
|
||||
|
||||
Ces sous-groupes sont en réalité créés dans notre branche de l'arborescence
|
||||
globale. Depuis l'extérieur du *namespace*, ils apparaîtraient sous un chemin
|
||||
différent.
|
||||
|
||||
|
||||
### Exemple avec Docker
|
||||
|
||||
Docker utilise le *namespace* `cgroup` pour isoler la vue des *cgroups* dans
|
||||
les conteneurs. Observons cela :
|
||||
|
||||
<div lang="en-US">
|
||||
```bash
|
||||
42sh$ docker run --rm -it debian /bin/bash
|
||||
indocker# cat /proc/self/cgroup
|
||||
0::/
|
||||
|
||||
indocker# ls /sys/fs/cgroup/
|
||||
cgroup.controllers cpu.max memory.events
|
||||
cgroup.events cpu.pressure memory.high
|
||||
cgroup.freeze cpu.stat memory.low
|
||||
cgroup.kill cpu.weight memory.max
|
||||
cgroup.max.depth io.max memory.min
|
||||
cgroup.max.descendants io.pressure memory.numa_stat
|
||||
cgroup.procs io.stat memory.oom.group
|
||||
cgroup.stat io.weight memory.pressure
|
||||
cgroup.subtree_control memory.current memory.stat
|
||||
cgroup.threads memory.events.local memory.swap.current
|
||||
cgroup.type ...
|
||||
```
|
||||
</div>
|
||||
|
||||
Le conteneur se voit à la racine, bien qu'il soit en réalité placé dans un
|
||||
sous-groupe dédié de l'arborescence globale.
|
||||
|
||||
|
||||
### Vérifier la position réelle dans l'arborescence
|
||||
|
||||
Depuis l'hôte, nous pouvons voir la véritable position d'un conteneur :
|
||||
|
||||
<div lang="en-US">
|
||||
```bash
|
||||
42sh$ docker inspect mycontainer | grep -A5 CgroupParent
|
||||
"CgroupParent": "",
|
||||
"CgroupDriver": "systemd",
|
||||
|
||||
42sh$ ps aux | grep containerd-shim
|
||||
root 12345 /usr/bin/containerd-shim-runc-v2 ...
|
||||
|
||||
42sh$ cat /proc/12345/cgroup
|
||||
0::/system.slice/docker-abc123def456.scope
|
||||
```
|
||||
</div>
|
||||
|
||||
Le processus du conteneur se trouve dans un sous-groupe bien spécifique, mais
|
||||
grâce au *namespace* `cgroup`, il se perçoit à la racine.
|
||||
|
||||
|
||||
### Combinaison avec le *namespace* `mount`
|
||||
|
||||
Pour que l'isolation soit complète, il faut généralement combiner le *namespace*
|
||||
`cgroup` avec le *namespace* `mount` afin de remonter `/sys/fs/cgroup` :
|
||||
|
||||
<div lang="en-US">
|
||||
```bash
|
||||
42sh# unshare --cgroup --mount /bin/bash
|
||||
incgroupns# mount -t cgroup2 none /sys/fs/cgroup
|
||||
incgroupns# ls /sys/fs/cgroup/
|
||||
# Vue limitée à notre sous-arbre
|
||||
```
|
||||
</div>
|
||||
|
||||
Sans remonter le *cgroup*, nous verrions toujours l'arborescence complète via
|
||||
l'ancien point de montage.
|
||||
|
||||
|
||||
### Cas d'usage
|
||||
|
||||
#### Migration de conteneurs\
|
||||
|
||||
Lors de la migration d'un conteneur avec des outils comme CRIU (Checkpoint/Restore
|
||||
In Userspace), le *namespace* `cgroup` garantit que l'arborescence des *cgroups*
|
||||
reste cohérente du point de vue du processus, même si la structure de l'hôte
|
||||
cible est différente.
|
||||
|
||||
|
||||
#### Isolation de sécurité\
|
||||
|
||||
Sans *namespace* `cgroup`, un processus malveillant dans un conteneur pourrait :
|
||||
|
||||
- Lire `/sys/fs/cgroup/` pour découvrir d'autres conteneurs
|
||||
- Analyser les métriques d'autres processus
|
||||
- Déduire des informations sur l'architecture du système
|
||||
|
||||
|
||||
#### Délégation de contrôle\
|
||||
|
||||
Le *namespace* `cgroup` permet de donner à un utilisateur non-privilégié le
|
||||
contrôle d'une sous-arborescence de *cgroups*, sans lui donner accès à
|
||||
l'arborescence globale.
|
||||
|
||||
|
||||
### Exemple en C
|
||||
|
||||
Voici un exemple de création d'un *namespace* `cgroup` en C :
|
||||
|
||||
<div lang="en-US">
|
||||
```c
|
||||
#define _GNU_SOURCE
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int child_func(void *arg)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[256];
|
||||
|
||||
printf("=== Dans le namespace cgroup ===\n");
|
||||
|
||||
fp = fopen("/proc/self/cgroup", "r");
|
||||
if (fp == NULL) {
|
||||
perror("fopen");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
printf(" %s", line);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define STACK_SIZE (1024 * 1024)
|
||||
static char child_stack[STACK_SIZE];
|
||||
|
||||
int main(void)
|
||||
{
|
||||
pid_t pid;
|
||||
FILE *fp;
|
||||
char line[256];
|
||||
|
||||
printf("=== Avant le namespace cgroup ===\n");
|
||||
|
||||
fp = fopen("/proc/self/cgroup", "r");
|
||||
if (fp != NULL) {
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
printf(" %s", line);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
pid = clone(child_func, child_stack + STACK_SIZE,
|
||||
CLONE_NEWCGROUP | SIGCHLD, NULL);
|
||||
if (pid == -1) {
|
||||
perror("clone");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
waitpid(pid, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</div>
|
||||
|
||||
::::: {.code}
|
||||
|
||||
Pour compiler et exécuter :
|
||||
|
||||
<div lang="en-US">
|
||||
```bash
|
||||
42sh$ gcc -o cgroup_ns_demo cgroup_ns_demo.c
|
||||
42sh$ ./cgroup_ns_demo
|
||||
=== Avant le namespace cgroup ===
|
||||
0::/user.slice/user-1000.slice/session-3.scope
|
||||
|
||||
=== Dans le namespace cgroup ===
|
||||
0::/
|
||||
```
|
||||
</div>
|
||||
|
||||
:::::
|
||||
|
||||
|
||||
### Aller plus loin {-}
|
||||
|
||||
Pour plus d'informations :
|
||||
|
||||
- `cgroup_namespaces(7)` - page de manuel
|
||||
- [Cgroup Namespace](https://lwn.net/Articles/621006/) : <https://lwn.net/Articles/621006/>
|
||||
- [Control Group v2](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html) : Documentation du noyau sur cgroup v2
|
||||
Loading…
Add table
Add a link
Reference in a new issue