Pour réduire les couts de production d’une infrastructure informatique, la virtualisation est une pratique couramment utilisée. Le principe consiste à avoir un gros serveur physique sur lequel nous déployons plusieurs systèmes d’exploitation isolés les uns des autres. De cette façon, la consommation énergétique est répartie entre toutes les applications qui du coup est unitairement très faibles en comparaison avec ce qu’il faudrait pour les alimenter sur des hôtes distincts.
Bien que cette technique permette déjà de sauvegarder pas mal de ressources, un nouveau procédé a commencé à émerger cette dernière décennie : la conteneurisation.
L’isolation entre les différentes entités est moins efficace qu’avec la virtualisation, mais elle a l’avantage notable de ne nécessiter qu’un seul noyau et donc de pousser l’économie à l’extrême. C’est un cloisonnement très intéressant pour tous les acteurs dans le marché du SAAS car le logiciel à déployer étant (normalement) bien connu du prestataire, il pourra veiller à concevoir des conteneurs fonctionnels et suffisamment isolés pour son infrastructure.
Les groupes de contrôle (ou encore « cgroups ») représentent une technologie du noyau Linux centrale pour la conteneurisation. Elle autorise le rationnement et le monitoring des ressources physiques par les processus. Ils permettent par exemple de fixer le temps CPU, l’utilisation de RAM ou même de bande passante allouées à certaines applications.
Concrètement parlant
Les cgroups se catégorisent par trois types d’objets : les tâches, correspondant en fait aux processus exécutés — les contrôleurs, qui regroupent un ensemble de points de contrôle logiquement répartis — et enfin, les cgroups qui définissent les seuils des points de contrôles et les tâches qui y seront soumises.
Par exemple, on pourrait créer le cgroup 1gb_max dans le contrôleur memory et y ajouter la tâche lighttpd pour limiter la consommation maximale en mémoire vive de notre serveur web.
Les cgroups se présentent à l’utilisateur sous la forme d’un arbre dont les enfants héritent des caractéristiques de leur parent. Cette approche nous permet de les considérer comme un système de fichers, ce qui tombe bien car sous Linux, tout est sensé être fichier. Ainsi, pour trouver la racine de chaque contrôleur actuellement monté, vous pouvez exécuter la commande cat /proc/mounts | grep ' cgroup'
. Ou encore, pour savoir lesquels sont implémentés chez vous, vous pouvez regarder le contenu de /proc/cgroups.
Le manuel des différents contrôleurs est d’ailleurs généralement disponible dans le paquet de documentation de votre noyau (sous Debian, celui-ci s’appelle linux-doc). Une fois installé, vous les trouverez avec find /usr/share/doc -path '*cgroup*'
.
Comment les utiliser ?
Cela dépend du degré d’implication de votre processus d’initialisation dans le domaine. En effet, systemd gère les cgroups de façon interne. Comme il est plus intéressant ludiquement parlant de faire sans, on va se contenter de la méthode « brute ». Vous pouvez néanmoins suivre les étapes décrites dans cet article avec celui-ci, mais il aura déjà généré pas mal de bruit pouvant nuire à la compréhension.
Comme dit précédemment, les cgroups sont représentés tels des systèmes de fichiers. On les gère donc de la même façon que pour ces derniers. Voici ci-dessous une liste de commandes permettant d’illustrer leur fonctionnement.
1.1 > perl -e '$s = "s"x(1<<25); exit 1' 1.2 > echo $? 1 1.3 > mkdir -p /cgroup/mem 1.4 > mount -t cgroup -o memory mem_fs /cgroup/mem 1.5 > cat /proc/mounts | grep ' cgroup ' mem_fs /cgroup/mem cgroup rw,relatime,memory 0 0 1.6 > cd /cgroup/mem/ && ls -1 | wc -l 18 1.7 > touch nouveau_fichier touch: impossible de faire un touch « nouveau_fichier »: Permission non accordée 1.8 > mkdir http && cd http 1.9 > ls -1 | wc -l 18 1.10 > echo 5000 >> /cgroup/mem/httpd/memory.limit_in_bytes 1.11 > perl -e '$s = "s"x(1<<25); sleep 1; exit 1' & echo $! > /cgroup/mem/httpd/tasks 1.12 > echo $? 0 1.13 > echo $$ > /cgroup/mem/httpd/tasks && perl -e '$s = "s"x(1<<25); sleep 1; exit 1' 1.14 > echo $? 137
La commande 1.1 n’est qu’un programme se contentant d’allouer environ 65 Mo de RAM. Comme nous le montre l’instruction 1.2, cette allocation s’est bien passée. En effet, le script Perl retourne bien 1, et donc ne plante pas avant.
C’est à présent que les choses deviennent intéressantes, car on va introduire un cgroup dans l’histoire. La première étape est de définir un endroit dans votre système de fichiers destiné à la représentation du contrôleur sur lequel on veut appliquer une restriction. C’est ce qu’on effectue avec les instructions 1.3 et 1.4 via la simple création d’un dossier et de l’utilisation de mount. La commande 1.5 nous sert de confirmation. D’ailleurs, vous verrez tous les autres contrôleurs déjà activés si vous avez recours à systemd. Dans ce cas, peut-être que notre opération précédente aura produit un doublon. Si oui, le contenu des deux dossiers sera forcément exactement le même.
Les commandes 1.6 à 1.9 sont présentes pour illustrer le fait que bien que notre contrôleur se présente sous la forme d’un système de fichiers, il n’en est rien techniquement parlant. Vous trouverez en effet un fichier pour chaque attribut lisible ou écrivable. Ceux commençants par memory.* appartiennent au contrôleur éponyme, ce qui évite les collisions car nous pouvons en monter plusieurs dans le même dossier.
Je vous invite à regarder la documentation dont j’ai parlé plus tôt pour savoir le rôle de chacun de ces fichiers, en précisant toute foi qu’il vous sera impossible d’en créer, comme le montre notre tentative avec touch (1.7). En effet, implémenter un nouvel attribut demande probablement un peu plus de travail. 😉 Par contre, l’ajout de dossiers est faisable. Vous remarquerez d’ailleurs que le dossier enfant possèdera par défaut un contenu similaire à celui du parent. Vous venez de fabriquer un cgroup.
La commande 1.10 définit une limite de consommation mémoire au cgroup mem-httpd que nous avons conçue. Pour cela, nous avons juste besoin d’écrire la valeur maximale en bytes que l’on tolère pour l’ensemble des processus du cgroup (ainsi qu’à ses enfants), et le tour est joué. Dans notre exemple, on est effectivement un peu radin en allouant que 5ko.
Enfin, les commandes 1.11 et 1.13 montrent deux techniques permettant d’assigner un processus à un cgroup. Vous aurez très vite remarqué que cette action est vraiment aisée à réaliser : il suffit d’inscrire le PID visé dans le fichier task du cgroup donné. Ici, ce procédé est illustré de deux façons : dans la première, l’ajout s’effectue après coup. Ça peut comporter des risques car pendant un laps de temps, certes très court, aucune limite n’est présente. La seconde technique résout ce problème en appliquant directement la restriction au processus chargé d’initialiser celui que nous voulons brider. En effet, les forks possèdent toujours par défaut le cgroup de son parent. Pour prouver le fonctionnement du rationnement, les commandes 1.12 et 1.14 montrent que notre allocation de 65Mo de RAM ne semble plus se passer comme prévu parce que les valeurs de retour de notre script ne sont plus 1. Dans le deuxième cas, vous remarquerez même qu’après exécution de la commande, votre shell commencera à dysfonctionner : c’est normal car s’ayant soumis lui-même au cgroup lui imposant que 5ko de mémoire, il a lui aussi des difficultés. 😀
Les Cgroups dans la vraie vie
Cette démonstration n’est pas complète, mais devrait néanmoins suffire pour vous donner une bonne vue d’ensemble sur le rôle des groupes de contrôle ainsi que sur leurs fonctionnements.
Après le redémarrage de votre ordinateur, vous constaterez que toute cette configuration aura disparu. C’est normal, car les cgroups ne sont pas persistants. En fait, en production on utilisera des commandes annexes plutôt que de manipuler le système de fichier à la main comme on vient de le faire. Celles-ci peuvent être par exemple celles du paquet cgroup-tools : cgexec, cgclassify, cgset, cgget, cgsnapshot, ou encore les fonctionnalités offertes de systemd. Pour plus d’informations, je vous invite à consulter les guides Red-Hat. Celui de la version 6 pour l’utilisation des cgroups « pures », et de la version 7 pour systemd.
Crédits images :
- La toupie en couverture par DesignbyDalton.
Publication originale :