305 lines
7.9 KiB
Markdown
305 lines
7.9 KiB
Markdown
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
|