357 lines
12 KiB
Markdown
357 lines
12 KiB
Markdown
\newpage
|
|
|
|
Les *cgroup*s
|
|
-------------
|
|
|
|
Les *cgroup*s (pour *Control Group*s) permettent de collecter des statistiques
|
|
sur des **groupes de processus** (voire même, des threads !) et de leur
|
|
attribuer des propriétés. Il est par exemple possible de leur imposer des
|
|
limites d'utilisation de ressources ou d'altérer leur comportement : quantité
|
|
de RAM, temps CPU, bande passante, ...
|
|
|
|
Apparue dès [Linux
|
|
2.6.24](https://kernelnewbies.org/Linux_2_6_24#Task_Control_Groups)
|
|
(en 2008 !), les *cgroup*s sont répartis en différents sous-systèmes
|
|
(*subsystem*), chacun étant responsable d'un type de ressources
|
|
spécifique :
|
|
|
|
- [`blkio` (`io` dans la v2)
|
|
:](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/blkio-controller.html)
|
|
limites et statistiques de bande passante sur les disques ;
|
|
- `cpu` : cycles CPU minimum garantis ;
|
|
- [`cpuacct` (inclus dans `cpu` dans la v2)
|
|
:](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cpuacct.html)
|
|
statistiques du temps CPU utilisé ;
|
|
- [`cpuset`
|
|
:](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cpusets.html)
|
|
associe des tâches à un/des CPU particuliers (par exemple pour dédier un cœur
|
|
du CPU à un programme, qui ne pourra alors utiliser que ce CPU et pas les
|
|
autres) ;
|
|
- [`devices`
|
|
:](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/devices.html)
|
|
règles de contrôle de création (`mknod`) et d'accès aux périphériques ;
|
|
- [`freezer`
|
|
:](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/freezer-subsystem.html)
|
|
pour suspendre et reprendre l'exécution d'un groupe de tâches ;
|
|
- [`hugetlb` :](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/hugetlb.html) statistiques et limitation de l'usage de la fonctionnalité `HugeTLB` (permettant d'obtenir des pages mémoires plus grande que 4 kB) ;
|
|
- [`memory` :](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/memory.html) statistiques et limitation d'usage de la mémoire vive et de la *swap* ;
|
|
- [`net_cls` (v1 seulement) :](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/net_cls.html) applique un `classid` à tous les paquets émis par les tâches du *cgroup*, pour filtrage par le pare-feu en sortie ;
|
|
- [`net_prio` (v1 seulement) :](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/net_prio.html) surcharge la valeur de l'option de priorité `SO_PRIORITY`, ordonant la file d'attente des paquets sortants ;
|
|
- [`pids` :](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/pids.html) statistiques et limitation du nombre de processus ;
|
|
- ...
|
|
|
|
Nous allons commencer par faire quelques tests avec le *cgroup* *freezer*, qui
|
|
permet d'interrompre l'exécution d'un groupe de processus, puis de la reprendre
|
|
lorsqu'on le décide.
|
|
|
|
|
|
### Montage du *freezer*
|
|
|
|
En fonction de la configuration de votre système, vous allez vous trouver dans
|
|
l'une de ces trois situations :
|
|
|
|
- Votre dossier `/sys/fs/cgroup` contient à la fois des fichiers `cgroup.*` et
|
|
éventuellement des dossiers : vous avez une distribution moderne qui utilise
|
|
la nouvelle version des `cgroup`s.
|
|
|
|
- Votre dossier `/sys/fs/cgroup` contient d'autres dossiers au nom des
|
|
sous-systèmes que l'on a listé ci-dessus : il s'agit des `cgroup`s v1.
|
|
|
|
- Votre dossier `/sys/fs/cgroup` est vide ou inexistant, vous pouvez choisir
|
|
d'utiliser la version de votre choix :
|
|
|
|
Pour utiliser la v1 :
|
|
|
|
<div lang="en-US">
|
|
```bash
|
|
mkdir /sys/fs/cgroup/freezer/
|
|
mount -t cgroup -o freezer none /sys/fs/cgroup/freezer/
|
|
```
|
|
</div>
|
|
|
|
|
|
Pour utiliser la v2 :
|
|
|
|
<div lang="en-US">
|
|
```bash
|
|
mkdir /sys/fs/cgroup/
|
|
mount -t cgroup2 none /sys/fs/cgroup/
|
|
```
|
|
</div>
|
|
|
|
::::: {.question}
|
|
Avant d'aller plus loin, notez que les exemples seront donnés pour les deux
|
|
versions des `cgroup`s à chaque fois.
|
|
|
|
La principale différence entre les deux est la fusion des différents
|
|
sous-systèmes au sein d'une même arborescence. Dans la première version, chaque
|
|
sous-système disposait de sa propre arborescence et il fallait créer les
|
|
groupes et associer les tâches pour chaque sous-système. Avec la seconde
|
|
version, une seule création est nécessaire, quelque soit le nombre de
|
|
sous-systèmes que l'on souhaite utiliser.
|
|
:::::
|
|
|
|
|
|
### Création d'un nouveau groupe
|
|
|
|
Les *cgroup*s sont organisé autour d'une arborescence de groupe, où chaque
|
|
groupe est représenté par un dossier. Il peut bien évidemment y avoir des
|
|
sous-groupes, en créant des dossiers dans les dossiers existants, etc.\
|
|
|
|
La première étape dans l'utilisation d'un *cgroup* est donc de créer un groupe.
|
|
|
|
Pour ce faire, il suffit de créer un nouveau dossier dans un groupe existant,
|
|
par exemple la racine.
|
|
|
|
On commence par se rendre à la racine :
|
|
|
|
<div lang="en-US">
|
|
```bash
|
|
cd /sys/fs/cgroup/freezer/ # v1
|
|
cd /sys/fs/cgroup/ # v2
|
|
```
|
|
</div>
|
|
|
|
Puis on crée notre groupe :
|
|
|
|
<div lang="en-US">
|
|
```bash
|
|
mkdir virli
|
|
ls virli/
|
|
```
|
|
</div>
|
|
|
|
Nous avons maintenant un nouveau groupe de processus `virli` dans le *cgroup*
|
|
*Freezer*. Comme il s'agit d'une hiérarchie, le groupe `virli` hérite des
|
|
propriétés de son (ses) père(s).
|
|
|
|
|
|
### Sélection de contrôleur (v2 seulement)
|
|
|
|
Du fait de l'unification de tous les sous-systèmes, si vous utilisez la seconde
|
|
version, vous allez devoir activer le ou les contrôleurs dont vous avez besoin
|
|
(tandis que dans la première version, on se rendait dans l'arborescence du
|
|
sous-système que l'on voulait).
|
|
|
|
Pour activer le contrôleur *memory* dans notre groupe `virli`, nous utilisons
|
|
la commande suivante :
|
|
|
|
<div lang="en-US">
|
|
```bash
|
|
echo "+memory" > virli/cgroup.subtree_control
|
|
```
|
|
</div>
|
|
|
|
::::: {.warning}
|
|
Si vous obtenez l'erreur `No such file or directory`, c'est sans doute que vous
|
|
avez les `cgroup`s v1 activé quelque part. Vous devriez plutôt utiliser la
|
|
première version, le fait qu'elle soit active empêche l'utilisation de la v2 en
|
|
parallèle.
|
|
:::::
|
|
|
|
On peut contrôler les contrôleurs actifs en consultant le fichier
|
|
`virli/cgroup.controllers`.
|
|
|
|
Le contrôleur *freezer* est activé par défaut, il n'y a pas besoin de
|
|
l'activer.
|
|
|
|
|
|
### Rattachement de processus
|
|
|
|
Pour le moment, ce nouveau groupe ne contient aucun processus, comme le montre
|
|
le fichier `cgroup.procs` de notre groupe. Ce fichier contient la liste des
|
|
processus rattachés à notre *cgroup*.
|
|
|
|
Ouvrons un nouveau terminal (c'est lui que l'on va geler), et récupérons son
|
|
PID : `echo $$`.
|
|
|
|
Pour ajouter une tâche à ce groupe, cela se passe de cette manière :
|
|
|
|
<div lang="en-US">
|
|
```bash
|
|
echo $PID > /sys/fs/cgroup/{,freezer/}virli/cgroup.procs
|
|
```
|
|
</div>
|
|
|
|
Il faut ici remplacer `$PID` par le PID du shell que l'on a relevé juste avant.
|
|
|
|
En validant cette commande, nous avons déplacé le processus dans ce groupe, il
|
|
n'est alors plus dans aucun autre groupe (pour ce *cgroup*, il ne bouge pas
|
|
dans les autres *cgroup*s).
|
|
|
|
Malgré l'utilisation de la redirection `>` (et non `>>`), il s'agit bel et
|
|
bien d'un ajout au fichier et non d'un écrasement. Il faut garder en tête que
|
|
le système de fichier est entièrement simulé et que certains comportements sont
|
|
adaptés.
|
|
|
|
|
|
### Consultation de l'état
|
|
|
|
En affichant le contenu du dossier `virli`, nous pouvions constater que
|
|
celui-ci contenait déjà un certain nombre de fichiers. Certains d'entre-eux
|
|
sont en lecture seule et permettent de lire des statistiques instantanées sur
|
|
le groupe ; tandis que d'autres sont des propriétés que nous pouvons modifier.
|
|
|
|
Nous pouvons consulter l'état de gel du groupe en affichant le contenu du
|
|
fichier\
|
|
`/sys/fs/cgroup/freezer/virli/freezer.state` ou `/sys/fs/cgroup/virli/cgroup.freeze`.
|
|
|
|
Pour plus d'informations sur les différents fichiers présents dans ce *cgroup*,
|
|
consultez
|
|
[la documentation associée](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/freezer-subsystem.html).
|
|
|
|
|
|
### Changement d'état
|
|
|
|
Faisons exécuter à notre interpréteur une commande pour voir effectivement
|
|
l'exécution s'arrêter. Si vous manquez d'inspiration, utilisez :
|
|
|
|
<div lang="en-US">
|
|
```bash
|
|
for i in $(seq 9999); do echo -n $i; sleep .1; echo -n " - "; sleep .1; done
|
|
```
|
|
</div>
|
|
|
|
Maintenant, nous avons donné l'ordre au noyau de ne plus allouer de temps de
|
|
calcul à notre shell et ses fils :
|
|
|
|
<div lang="en-US">
|
|
```bash
|
|
echo FROZEN > /sys/fs/cgroup/freezer/virli/freezer.state # v1
|
|
echo 1 > /sys/fs/cgroup/virli/cgroup.freeze # v2
|
|
```
|
|
</div>
|
|
|
|
À cet instant, vous devriez voir votre compteur s'arrêter. Pour reprendre
|
|
l'exécution :
|
|
|
|
<div lang="en-US">
|
|
```bash
|
|
echo THAWED > /sys/fs/cgroup/freezer/virli/freezer.state # v1
|
|
echo 0 > /sys/fs/cgroup/virli/cgroup.freeze # v2
|
|
```
|
|
</div>
|
|
|
|
|
|
### Exercice : script de monitoring {- #script-monitoring}
|
|
|
|
À nous maintenant de concevoir un script qui va enregistrer vers une base de
|
|
données des statistiques issues des *cgroup*s, tel `telegraf`.
|
|
|
|
Dans un premier temps, commençons par afficher dans la console, la quantité de
|
|
mémoire utilisée par le groupe monitoré.
|
|
|
|
::::: {.code}
|
|
Vous pouvez utiliser un programme comme
|
|
[`memhog`](https://virli.nemunai.re/memhog.c) pour remplir rapidement votre
|
|
mémoire.
|
|
:::::
|
|
|
|
<div lang="en-US">
|
|
```
|
|
42sh# ./monitor group_name memhog 500
|
|
~~~ 13595 ~~~ Current memory usage: 75194368
|
|
~~~ 13595 ~~~ Current memory usage: 150290432
|
|
~~~ 13595 ~~~ Current memory usage: 223690752
|
|
~~~ 13595 ~~~ Current memory usage: 296828928
|
|
~~~ 13595 ~~~ Current memory usage: 368001024
|
|
~~~ 13595 ~~~ Current memory usage: 438517760
|
|
~~~ 13595 ~~~ Current memory usage: 480329728
|
|
~~~ 13595 ~~~ Current memory usage: 155648
|
|
```
|
|
</div>
|
|
|
|
::::: {.question}
|
|
Le modèle de sortie standard de votre script `monitor` n'a pas d'importance, il
|
|
doit cependant être possible d'y trouver des statistiques intéressantes, dont
|
|
la quantité de mémoire utilisée. Ici nous affichons au début le PID du
|
|
processus, ce qui peut simplifier le débogage du script.\
|
|
|
|
Il s'utilise de la manière suivante :
|
|
|
|
<div lang="en-US">
|
|
```bash
|
|
./monitor group_name prog [args [...]]
|
|
```
|
|
</div>
|
|
|
|
Où :
|
|
|
|
- `group_name` correspond au nom du/des *cgroup*(s) à créer/rejoindre.
|
|
- `prog [args [...]]` est la commande que l'on souhaite monitorer, à exécuter
|
|
dans le *cgroup*.
|
|
:::::
|
|
|
|
::::: {.warning}
|
|
Si vous n'avez pas le *cgroup* *memory*, il est possible qu'il ne soit pas
|
|
activé par défaut par votre système. Si vous êtes dans ce cas, essayez
|
|
d'ajouter `cgroup_enable=memory` à la ligne de commande de votre noyau.
|
|
:::::
|
|
|
|
|
|
### Fixer des limites {#Fixer-des-limites}
|
|
|
|
Au-delà de la simple consultation, les *cgroup*s peuvent servir à limiter la
|
|
quantité de ressources mises à disposition à un groupe de processus.
|
|
|
|
Pour définir une limite, nous allons écrire la valeur dans le fichier
|
|
correspondant à une valeur limite, comme par exemple
|
|
`memory.max_usage_in_bytes`/`memory.max`, qui limite le nombre d'octets que
|
|
notre groupe de processus va pouvoir allouer au maximum :
|
|
|
|
<div lang="en-US">
|
|
```
|
|
# cgroup v1
|
|
42sh$ cat /sys/fs/cgroup/memory/virli/memory.max_usage_in_bytes
|
|
0
|
|
# 0 = Aucune limite
|
|
42sh$ echo 4M > /sys/fs/cgroup/memory/virli/memory.max_usage_in_bytes
|
|
# Maintenant, la limite est à 4MB, vérifions...
|
|
42sh$ cat /sys/fs/cgroup/memory/virli/memory.max_usage_in_bytes
|
|
4194304
|
|
```
|
|
</div>
|
|
|
|
<div lang="en-US">
|
|
```
|
|
# cgroup v2
|
|
42sh$ cat /sys/fs/cgroup/virli/memory.max
|
|
max
|
|
# max = Aucune limite
|
|
42sh$ echo 4M > /sys/fs/cgroup/virli/memory.max
|
|
# Maintenant, la limite est à 4MB, vérifions...
|
|
42sh$ cat /sys/fs/cgroup/virli/memory.max
|
|
4194304
|
|
```
|
|
</div>
|
|
|
|
Chaque *cgroup*s définit de nombreux indicateurs et possède de nombreux
|
|
limiteurs, n'hésitez pas à consulter la documentation associée à chaque
|
|
*cgroup*.
|
|
|
|
Mettez à jour votre script de monitoring pour prendre en compte les
|
|
limites que vous avez définies :
|
|
|
|
<div lang="en-US">
|
|
```
|
|
42sh# mkdir /sys/fs/cgroup...
|
|
42sh# echo 512M > /sys/fs/cgroup.../memory.max_usage_in_bytes
|
|
42sh# ./monitor group_name memhog 500
|
|
~~~ 13595 ~~~ Current memory usage: 75194368/550502400 (13%)
|
|
~~~ 13595 ~~~ Current memory usage: 150290432/550502400 (27%)
|
|
~~~ 13595 ~~~ Current memory usage: 223690752/550502400 (40%)
|
|
~~~ 13595 ~~~ Current memory usage: 296828928/550502400 (53%)
|
|
~~~ 13595 ~~~ Current memory usage: 368001024/550502400 (66%)
|
|
~~~ 13595 ~~~ Current memory usage: 438517760/550502400 (79%)
|
|
~~~ 13595 ~~~ Current memory usage: 480329728/550502400 (87%)
|
|
~~~ 13595 ~~~ Current memory usage: 155648/550502400 (0%)
|
|
```
|
|
</div>
|
|
|
|
|
|
### Pour aller plus loin {-}
|
|
|
|
Pour tout connaître en détails, [la série d'articles de Neil Brown sur les
|
|
Control groups](https://lwn.net/Articles/604609/) est excellente ! Plus [cet
|
|
article sur la version 2](https://lwn.net/Articles/679786/).
|