CGROUPS : Premiers pas vers le noyau Linux

I. Vous avez dit cgroups ?

Avec la montée en popularité de Docker et l’utilisation de plus en plus courante d’images dans le développement et le test d’applications, il convient d’introduire un outil du noyau Linux puissant et trop mal connu : les cgroups.

Les cgroups, pour “control groups” sont au cœur du système de conteneurisation, ils définissent des groupes de processus soumis à des règles précises. L’objectif est simple : limiter, compter et isoler l’utilisation des ressources.

Ainsi, lors du lancement d’un nouveau conteneur, le système doit partager une partie de ses ressources (CPU, mémoire…) avec ce nouvel arrivant. C’est ici que le système de règles intervient. Les identifiants des processus (pid) liés au conteneur sont regroupés dans un cgroup qui limitera leurs libertés dans le système hôte.

Il existe deux modèles de cgroups dans le noyau, les cgroups v1 et les cgroups v2. Bien que les v2 soient plus récent, mieux conçus et destinés à remplacer progressivement les cgroups v1, ces derniers ne sont pour autant pas obsolètes. En effet, Docker comme d’autres services de conteneurisation utilise les cgroups v1, et c’est uniquement d’eux dont nous parlerons ici.

Dans ces cgroups v1, on trouve notamment :

cpu / cpuacct / cpuset : Partage et limitation des ressources CPU.

memory : Partage et limitations des ressources mémoires.

devices : Autorisations d’accès aux différents devices.

freezer : Suspension ou relance de tous les processus d’un cgroup.

pids : Contrôle du nombre de processus maximum dans un cgroup.

II. Un peu de manipulation

Sur un terminal linux, le répertoire cgroup, regroupant tous les cgroups est facilement accessible, dans :

sys/fs/cgroup

Pour en apprendre un peu plus, jouons un peu avec les règles limitant les accès à la mémoire du système.

Rendons-nous à l’intérieur du répertoire memory :

Nous constatons la présence de nombreux fichiers, chaque fichier possède un rôle bien précis.

Par exemple, le fichier memory.limit_in_bytes nous renseigne sur la limite en bytes d’accès mémoire pour tous les processus de ce cgroup.

Mais comment savoir quels sont les processus affectés par cette règle ? Là encore, un fichier à un rôle particulier : le fichier cgroup.procs qui regroupe tous les pids des processus.

Bien sûr, ils sont ici très nombreux. Pour nos essais, nous allons créer notre propre répertoire de test dans memory, appelons le foo :

On peut constater la présence automatique de tous les fichiers de gestion dans le nouveau répertoire. Regardons du côté de cgroup.procs et memory.limit_in_bytes :

Aucun pid n’est encore affilié à ce cgroup, normal puisque nous venons de le créer. Créons notre propre processus de test, pour cela, codons un petit programme en C :

Ici, le processus dort un certain temps, puis créé un buffer de 10000 octets (bytes).

Si on exécute ce script de manière classique, on obtient la sortie suivante : 

Le système n’a en effet aucun problème à attribuer 10000 bytes à une «chaîne».

Maintenant, limitons la mémoire de notre cgroup en modifiant le contenu du fichier memory.limit_in_bytes par la valeur 8000.

Relançons notre script, et cette fois écrivons son pid dans le fichier cgroup.procs pendant qu’il dort, avant l’allocation mémoire au String dans notre script.

Pour afficher la liste des pids des processus en cours, on peut utiliser la commande :

ps aux

Ici, le pid de notre programme est 14857, on ajoute donc cette valeur dans cgroup.procs via la commande echo.
Observons ce qu’il se passe :

On remarque que le système a interrompu l’exécution du processus, car celui-ci a enfreint la règle de limitation mémoire.

Ici, nous avons réalisé ce test pour la mémoire, mais il est évidemment possible d’effectuer de nombreuses manipulations similaires pour le CPU, la gestion des devices etc..

Regardons maintenant du côté de Docker, et regardons comment il utilise et tire parti des cgroups.
Pour cela, lançons un conteneur de serveur nginx sur notre système :

Restons dans notre répertoire memory et regardons son contenu.

On constate la création d’un nouveau répertoire docker. En effet, un nouveau répertoire docker est créé pour chaque cgroup.
Si on regarde du côté de nos processus courant, on remarque notre serveur nginx :

Ici, les pids 16563, 16585 et 16641 correspondent à notre serveur. 

Le processus de pid 16565 est le conteneur Docker faisant tourner le serveur nginx, observons-le de plus près.

Il est possible de se focus sur un processus pour y voir plus clair, grâce à la commande :

ps –pid <PID> -F

On remarque un identifiant à la fin de la ligne de commande, cet identifiant est aussi le nom d’un répertoire dans le répertoire docker fraîchement créé :

Dans ce répertoire, on remarque à nouveau une copie de tous les fichiers de contrôle.

Le fichier cgroup.procs de ce répertoire contient les pids associés au serveur nginx.

III. CONCLUSION

Les cgroups offrent des possibilités considérable en l’isolation de ressources au sein d’un même système. Docker tire partit de cet outil formidable pour offrir à l’utilisateur le lancement facile et rapide de conteneurs, sans se soucier des inconvénients habituels liés à la virtualisation lourde comme la vitesse de déploiement.

Nous avons vu dans cette première partie comment fonctionnent les cgroups en ligne de commande, à un haut niveau. Dans la suite nous verrons à un plus bas niveau et en explorant une partie du noyau, comment cela se passe.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.