La lutte pour les ressources, partie 6: cpuset ou partage n'est pas toujours juste

Lorsqu'ils parlent de cgroups, les utilisateurs de Red Hat posent souvent la même question: «J'ai une application qui est très sensible en termes de retards. Est-il possible d'utiliser des cgroups pour isoler cette application des autres en la liant à certains cœurs de processeur? »



Bien sûr que vous le pouvez. Sinon, nous ne choisirions pas cette question comme sujet de l'article d'aujourd'hui.

Dans l'enfance, on nous disait souvent que le partage est bon et juste. En gros, c'est comme ça. Mais il y a des exceptions.

Comme nous l'avons écrit dans le premier article de cette série , par défaut, Red Hat Enterprise Linux 7 se comporte comme une grand-mère aimable sphérique. En ce sens qu'elle essaie de répartir équitablement les ressources du système entre tous ceux qui les demandent. Cependant, dans la vraie vie, les grands-mères ont des animaux de compagnie qui en obtiennent plus. Traduit en sysadmin, cela signifie qu'il existe des situations où certaines applications ou certains services sont plus importants que d'autres, il convient donc de leur accorder toute l'attention possible afin qu'ils soient aussi réactifs que possible.

Red Hat Enterprise Linux 7 le fait en deux étapes:

  1. Nous isolons une partie des cœurs de processeur afin de les transférer à l'usage exclusif d'une telle application.
  2. Nous créons des groupes de cgroups et des fichiers d'unité qui lient cette application à des noyaux isolés.

Une petite digression concernant les exemples de ces articles


Hat Enterprise Linux 7.4 a modifié le fonctionnement des tranches de courte durée, telles que les sessions utilisateur. Par conséquent, ils ne peuvent plus modifier les paramètres de groupe de contrôle à la volée, apporter des modifications permanentes à la configuration ou créer des fichiers de dépôt à l'aide de la commande systemctl set-property. Oui, c'est dommage, mais la communauté de développement Linux en a décidé ainsi. La bonne nouvelle est que ces changements n'ont pas affecté le service. Autrement dit, si les applications démarrent et s'arrêtent via des fichiers d'unité (fonctionnent comme des démons), alors tous nos exemples fonctionnent. De plus, il reste possible de créer vos propres groupes de contrôle à l'aide d'outils anciens tels que cgcreate et cgset, puis de placer des sessions et des processus utilisateur dans ces groupes pour utiliser des boules de processeur et d'autres contrôles. Dans la vie, tout change, nous ne pouvons donc qu'adapter et inventer de nouvelles techniques. Et maintenant, nous passons au sujet d'aujourd'hui.

Construire le séparatisme avec Isolcpus


L'un des composants les plus importants du noyau Linux est le planificateur de processus. S'il est un peu plus profond, un processus est un code exécutable qui fait partie d'une application ou d'un service. En fait, le processus consiste en une série d'instructions que l'ordinateur exécute, faisant tel ou tel travail, qu'il s'agisse de sceaux ou de quelque chose de plus sérieux.

Ces instructions sont gérées par le processeur central, alias CPU. Sur les ordinateurs modernes, le processeur se compose généralement de plusieurs processeurs appelés cœurs.

Par défaut, l'ordonnanceur considère chaque cœur de processeur comme l'un des modules exécutifs auxquels il affecte de nouveaux processus à mesure qu'ils apparaissent. Dans ce cas, l'ordonnanceur essaie de répartir plus ou moins uniformément les processus émergents entre les cœurs, en tenant compte de la charge. Malheureusement, le planificateur ne peut pas être informé que ce processus particulier finira par donner lieu à un groupe entier de processus, et ce groupe devra être exécuté indépendamment des autres processus, dans le sens où ils ne devraient pas avoir de cœurs de processeur communs.

Par conséquent, nous devons en quelque sorte dire au planificateur de sorte qu'il ne touche pas une partie des cœurs de processeur, c'est-à-dire ne leur donne aucun processus qui frappe. Et puis nous-mêmes (ou avec l'aide d'un autre processus) allons forcer ces processus que nous considérons nécessaires pour être isolés du planificateur du noyau. Cela peut être fait en utilisant le paramètre isolcpus dans la ligne de démarrage du noyau dans le fichier de configuration grub. Dans l'exemple ci-dessous, nous avons une machine avec quatre noyaux sur laquelle il y a deux fichiers grub: l'un se trouve dans / etc / default et s'appelle grub.noiso (c'est la sauvegarde de configuration par défaut), et le second se trouve là et est simplement appelé grub pour qu'il ramassé grub2-mkconfig. Ce deuxième fichier a été modifié pour isoler les noyaux 1-3 du planificateur de processus.


AVERTISSEMENT: sur Red Hat Enterprise Linux 7, vous n'avez jamais besoin de modifier manuellement le fichier grub.conf dans le dossier / boot. À la place, apportez les modifications nécessaires à / etc / default / grub, puis reconstruisez le fichier grub.conf à l'aide de l'utilitaire approprié, par exemple, comme ceci:


Lorsque vous utilisez le paramètre isolcpus, il est nécessaire de répertorier les cœurs de processeur libérés séparés par des virgules, la numérotation commence à 0. Après le redémarrage du système, le planificateur de processus n'utilisera ces cœurs pour rien, sauf pour certains processus de niveau système qui DOIVENT ÊTRE sur chaque cœur. Pour vérifier si notre méthode a fonctionné, nous allons démarrer plusieurs processus de chargement puis nous verrons le chargement de chaque noyau au moyen de la commande top.


Comme vous pouvez le voir, tous les processus de chargement reposaient sur le CPU 0, au lieu d'être répartis uniformément sur les quatre cœurs. Nous avons donc enregistré le paramètre de démarrage correctement.

Lier les processus aux noyaux à l'aide de cpuset


Nous passons maintenant à des choses qu'il vaut mieux ne pas faire si vous ne comprenez pas pourquoi vous faites cela, et qui sont mieux déployées en production qu'après des tests approfondis .

À quoi servent ces avertissements? Au fait que nous ferons, en général, des choses simples en utilisant la boîte à outils libcgroup, qui a été décrite dans un article précédent. Si vous vous souvenez, ce n'est qu'un ensemble de commandes pour créer, modifier et détruire des groupes de contrôle. En fait, ils font partie de Red Hat Enterprise Linux 6, mais ils peuvent également être installés sur Red Hat Enterprise Linux 7, bien qu'il soit possible que cette possibilité disparaisse à l'avenir. Rappelez brièvement les principales recommandations d'utilisation de libcgroup:

  1. Utilisez systemd pour contrôler les contrôleurs cgroup qui sont sous le contrôle de systemd lui-même (ce sont le CPU, la mémoire et les E / S de bloc).
  2. Utilisez les outils libcgroup pour gérer tous les autres contrôleurs cgroup.
  3. Faites très attention aux conséquences imprévues de vos actions.

Tout est simple avec le concept cpuset - c'est une liste de cœurs de processeur (numérotation, rappel, commence à 0), qui accepte les tâches qui seront exécutées UNIQUEMENT sur ces cœurs. Ce sont les cœurs de processeur les plus courants, ils peuvent être contrôlés par un planificateur de processus (c'est ainsi que le système est configuré par défaut), ou, inversement, ils peuvent être isolés du planificateur (comme nous l'avons fait dans l'exemple ci-dessus).

Vérifions le répertoire / sys / fs / cgroup sur le système de notre exemple. Comme vous pouvez le voir, le répertoire cpuset existe déjà, car ce contrôleur fait partie du noyau (bien qu'il ne soit pas sous le contrôle de systemd). Cependant, il n'a pas encore de groupes de contrôle, donc nous ne voyons que les paramètres par défaut dans ce répertoire.


Vérifiez que la boîte à outils libcgroup est installée sur notre machine:


S'il n'est pas installé, cela peut être facilement résolu avec la commande yum install libcgroup, même un redémarrage n'est pas nécessaire.

Maintenant, créez cpuset. Pour ce faire, nous utiliserons les commandes suivantes pour créer un nouveau groupe de contrôle pour cpuset et enregistrer ses propriétés:


La commande Cgcreate crée un groupe de contrôle appelé testset et le place à l'intérieur du contrôleur cpuset. Ensuite, nous attribuons le troisième noyau de notre machine virtuelle à ce nouveau cpuset et lui attribuons la zone NUMA 0. Même si votre système n'utilise pas NUMA (et le nôtre ne l'utilise tout simplement pas), vous devez toujours enregistrer la zone, sinon vous ne pouvez pas assigner de tâches au groupe cgroup . Vérifiez maintenant que le répertoire testset a été créé sur le système de fichiers et voyez ce qu'il contient.


Comme vous pouvez le voir, nos modifications sont en place, mais jusqu'à présent aucun processus n'a été exécuté sur ce cpuset. Comment planter un processus ici?

Il existe plusieurs façons de procéder:

  • Vous pouvez conduire le PID d'un processus existant dans le fichier de tâches. Cela fonctionne, mais pas très joli.
  • Vous pouvez utiliser cgexec et spécifier le groupe au démarrage du processus. Cela fonctionne si l'application n'est pas un démon; De plus, tout cela peut être magnifiquement écrit dans le script de lancement de l'application.
  • Pour une application qui s'exécute en tant que démon exécutant systemd, vous pouvez créer un fichier de service.

Voyons l'option cgexec.


Nous avons lancé foo.exe, à son tour, il a lancé un processus enfant, qui ne fait que charger activement le processeur. L'option --sticky de la commande cgexec indique que "tout processus enfant doit rester dans le même groupe de contrôle que le processus parent". C'est donc une option importante, et il faut s'en souvenir. Nous voyons maintenant que deux processus tournent dans notre groupe de contrôle et nous connaissons leurs PID. Jetez un œil en haut:


Comme vous pouvez le voir, le CPU 3 est maintenant chargé dans les globes oculaires, et le reste se refroidit.

Et voici à quoi ressemble un fichier unité pour exécuter la même application qu'un service systemd:


Il y a trois commandes ExecStartPre dans le fichier d'unité qui effectuent les réglages que nous avons déjà réussi à faire avec nos mains. Vient ensuite la commande ExecStart, qui lance l'application. Et lorsque l'application s'arrête, la commande ExecStopPost se nettoie après elle-même, supprimant cgroup.


Comme vous pouvez le voir, dans le dernier exemple, nous avons créé un nouveau groupe de contrôle nommé set1. Nous l'avons fait pour montrer que vous pouvez avoir plusieurs groupes de contrôle actifs qui partagent le même processeur. À qui cela peut sembler utile, mais au contraire de confondre quelqu'un.

Eh bien, ça marche? Il semble que oui!


Et maintenant, nous allons terminer le travail de notre service et vérifier que cgroup est détruit:


ATTENTION: les groupes cgroup créés à l'aide de cgcreate ne sont pas enregistrés après le redémarrage. Par conséquent, la création de tels groupes doit être prescrite dans les scripts de démarrage et les fichiers d'unité.

Alors maintenant, dans votre arsenal, il y a quelques autres outils pour travailler avec des groupes de contrôle. Nous espérons qu'ils vous seront utiles!

D'autres articles cgroups de notre série Resource Fight sont disponibles sur:

Source: https://habr.com/ru/post/fr430748/


All Articles