Dans ce guide sur cgroups Hébergement je montre concrètement comment isoler les ressources du serveur avec des groupes de contrôle Linux de manière à ce que les „voisins bruyants“ ne ralentissent pas les services. Tu apprendras comment je limite, priorise et surveille de manière fiable le CPU, la RAM, les E/S de bloc et le réseau par site web, conteneur ou utilisateur - de manière pratique et réalisable.
Points centraux
Les aspects clés suivants te guideront à travers les décisions et les étapes les plus importantes.
- IsolationSéparer proprement les processus et maîtriser les voisins
- ContrôleLimiter le CPU, la RAM, les E/S et les périphériques de manière ciblée
- Priorité: Pondérer et protéger les services premium
- TransparenceMesurer la charge, utiliser les alarmes et les tendances
- Mise à niveau: de la v1 à la v2 pour une gestion claire
Comment cgroups sépare les ressources du serveur
Les groupes de contrôle classent les processus dans des groupes et relient ces groupes à des pilotes de ressources, ce qui me permet d'avoir une vue d'ensemble de la situation. Ressources par groupe. Sur un serveur partagé, cela évite qu'un seul site web ne consomme du temps de CPU ou ne remplisse la mémoire à ras bord. Pour cela, je mets en place des hiérarchies dans lesquelles les groupes de parents définissent des limites supérieures qui sont transmises aux groupes d'enfants. Ainsi, la répartition de la charge reste cohérente et je maîtrise les goulets d'étranglement. Je désamorce ainsi sensiblement le problème des „voisins bruyants“, car les pics forts fonctionnent sur des rails isolés.
Contrôleur et système de fichiers : les outils à main
Le travail pratique commence dans le système de fichiers cgroup, sous /sys/fs/cgroup, où je crée des groupes et fixe des limites, c'est-à-dire la configuration concrète. Contrôle de l'ordinateur. Grâce à des contrôleurs tels que cpu, memory, blkio, cpuset et devices, j'alloue des tranches de temps, je couvre la RAM, je freine les E/S, j'épingle les cœurs ou je verrouille les périphériques. Je combine ces éléments en fonction de l'application : par exemple, les applications gourmandes en mémoire avec des limites de RAM strictes, les tâches de construction avec des poids CPU et les bases de données avec des bandes passantes I/O. Il est important de disposer d'un schéma clair pour les désignations, afin de pouvoir retrouver rapidement les groupes par la suite. Cela permet de garder une vue d'ensemble de l'administration et de ne pas perdre de vue les modifications.
| Sous-système | Objectif |
|---|---|
| cpu / cpuacct | Allouer du temps CPU, Poids et fixer des quotas |
| mémoire | Limiter la RAM, éviter les OOM-Kills |
| blkio | Etrangler les E/S de bloc, contrôler le taux de lecture/d'écriture |
| cpuset | Attribuer des cœurs de CPU et des nœuds NUMA |
| devices | Autoriser ou bloquer l'accès aux appareils |
| net_cls / net_prio | Marquer et prioriser les classes de réseau |
cgroups v1 vs. v2 dans l'hébergement
L'ancienne v1 sépare les contrôleurs en plusieurs arborescences, ce que j'ai rapidement trouvé peu clair dans les grandes configurations, et c'est pourquoi je Conversion à la v2. cgroups v2 regroupe tout dans une arborescence claire et simplifie ainsi la gestion, le débogage et l'héritage. De plus, cpu.max, cpu.weight et memory.max dans v2 fournissent un ensemble cohérent de leviers qui interagissent proprement. Les orchestrateurs de conteneurs préfèrent également utiliser la v2, car la sémantique est plus homogène. Pour les environnements d'hébergement avec de nombreux clients, la v2 est donc un choix plus léger et plus fiable.
Contrôle fin dans cgroups v2 : io, memory, pids et PSI
Dans la v2, j'active explicitement les contrôleurs par sous-arbre et j'obtiens ainsi un meilleur contrôle de l'héritage. Ce n'est que lorsque je les autorise dans le nœud parent que je peux les utiliser dans les groupes enfants - cela évite la prolifération et garantit des politiques propres.
# Activer le contrôleur dans le nœud racine pour les enfants
echo "+cpu +io +memory +pids" > /sys/fs/cgroup/cgroup.subtree_control
# Créer un sous-groupe et l'utiliser comme espace de travail
mkdir /sys/fs/cgroup/prod-web
Pour les E/S, j'utilise le contrôleur io dans la v2 (au lieu de blkio). Je fixe les limites de bande passante ou d'IOPS par appareil via les indications Major:Minor. Je m'assure ainsi que les logs, les index ou les sauvegardes n'encombrent pas le disque.
# Limiter le périphérique pour /dev/sda (8:0) à 50 MiB/s en lecture, 20 MiB/s en écriture
echo "8:0 rbps=50M wbps=20M" > /sys/fs/cgroup/prod-web/io.max
# Allouer les poids de manière relative (au lieu d'un couvercle dur, 100-10000, par défaut 100)
echo 500 > /sys/fs/cgroup/prod-web/io.weight
Pour la mémoire, j'opte pour deux niveaux : memory.high freine très tôt, memory.max est une butée dure. De plus, je désactive le swap pour les services critiques afin que les latences n'explosent pas.
# Frein doux (softlimit) et couvercle dur
echo $((400*1024*1024)) > /sys/fs/cgroup/prod-web/memory.high
echo $((512*1024*1024)) > /sys/fs/cgroup/prod-web/memory.max
# Interdire le swap (0) ou le limiter (par ex. 128 MiB)
echo 0 > /sys/fs/cgroup/prod-web/memory.swap.max
Avec le contrôleur pids, j'évite les tempêtes de fork et je respecte une limite supérieure pour les processus et les threads par client - c'est une protection efficace contre les mauvaises configurations et les abus dans les environnements partagés.
# 512 processus/threads maximum dans le groupe
echo 512 > /sys/fs/cgroup/prod-web/pids.max
Pour les diagnostics, j'utilise cgroup.events ainsi que PSI (Pressure Stall Information) par groupe. PSI me montre si le CPU, la mémoire ou les E/S sont régulièrement à court et provoquent des temps d'attente. C'est plus précieux que la simple utilisation, car cela permet de voir les goulots d'étranglement avant que les utilisateurs ne les ressentent.
# Lire les événements et les pressions
cat /sys/fs/cgroup/prod-web/cgroup.events
cat /sys/fs/cgroup/prod-web/cpu.pressure
cat /sys/fs/cgroup/prod-web/memory.pressure
cat /sys/fs/croup/prod-web/io.pressure
Dans les situations OOM, je regroupe les kills de manière ciblée avec memory.oom.group, afin que les processus associés (par exemple Worker + Helfer) soient terminés de manière cohérente, au lieu de mettre le système dans un état de paralysie partielle. Pour les fenêtres de maintenance, je gèle brièvement les groupes avec cgroup.freeze et les redémarre ensuite - utile pour les migrations de bases de données ou les déploiements atomiques.
# Comportement OOM à l'échelle du groupe
echo 1 > /sys/fs/cgroup/prod-web/memory.oom.group
# Suspendre / reprendre le groupe
echo 1 > /sys/fs/cgroup/prod-web/cgroup.freeze
echo 0 > /sys/fs/cgroup/prod-web/cgroup.freeze
Pas à pas : Fixer des limites sous Linux
Sur les serveurs avec un noyau récent, j'active cgroup v2, puis je crée des groupes avec des plafonds appropriés, ce qui me permet de Contrôle directement dans le système. Les commandes suivantes montrent un exemple minimaliste avec une limite de CPU et de RAM. Je choisis des quotas de manière conservatrice, j'observe la charge et j'ajuste par itérations. Ainsi, je maintiens des temps de réponse bas sans couper inutilement des phases de burst utiles. L'implémentation reste proche du noyau et fonctionne indépendamment des logiciels de panel.
# Monter cgroup v2 (si pas automatiquement via systemd)
mount -t cgroup2 none /sys/fs/cgroup
Créer un groupe #
mkdir /sys/fs/cgroup/prod-web
Assigner un processus # (exemple : PID 1234) au groupe
echo 1234 > /sys/fs/cgroup/prod-web/cgroup.procs
# Quota CPU : 100 ms de 200 ms => 50%
echo "100000 200000" > /sys/fs/cgroup/prod-web/cpu.max
# Limite dure de RAM : 512 MiB
echo $((512*1024*1024)) > /sys/fs/cgroup/prod-web/memory.max
Intégration de Systemd et tranches persistantes
Au quotidien, je laisse systemd gérer l'arborescence cgroup. Ainsi, les limites restent persistant et sont documentées de manière transparente par service. Je travaille avec des tranches (system.slice, user.slice, machine.slice) et je définis mes propres hiérarchies pour les mandants ou les rôles. Par le biais de fichiers drop-in, je définis des poids et des couvercles sans avoir à écrire manuellement dans /sys/fs/cgroup.
# Exemple : créer sa propre tranche pour les services web
cat > /etc/systemd/system/web.slice < /etc/systemd/system/php-fpm.service.d/10-slice.conf <<'EOF'
[Service]
Slice=web.slice
EOF
systemctl daemon-reload
systemctl restart php-fpm.service
Pour les tests ad hoc, je lance des processus dans des scopes, sans écrire de fichiers unitaires. Cela permet de simuler des pics de charge ou de tester des limites pendant une courte période.
# Définir les ressources à court terme et démarrer le processus sous contrôle
systemd-run --scope -p CPUWeight=200 -p MemoryMax=512M \
-p IOReadBandwidthMax=/dev/sda:50M -p IOWriteBandwidthMax=/dev/sda:20M \
stress-ng --vm 1 --vm-bytes 300M --cpu 1
Les runtimes de conteneurs gèrent leurs propres tranches (machine.slice) sous systemd. La délégation est ici importante : j'autorise le service d'exécution à gérer les sous-arbres (Delegate) afin que les pods/conteneurs soient proprement isolés. Ainsi, les politiques de l'hôte et du conteneur n'interfèrent pas entre elles.
Appliquer les limites des conteneurs en toute sécurité
Dans les environnements de conteneurs, les cgroups agissent comme des garde-fous invisibles, que je définis via des paramètres d'exécution et qui me permettent d'utiliser les ressources de manière plus efficace. Conteneur à des limites justes. Docker mappe par exemple les options -cpus, -memory et -blkio directement sur cgroups, Kubernetes traduit les requests et les limits en paramètres v2. La cohérence est essentielle : les limites doivent correspondre à la charge réelle, afin que les pods et les conteneurs ne ralentissent pas inutilement ou ne provoquent pas d'erreurs OOM. Je garde les services de production plus étroits, tandis que les tâches de construction ou de test ne reçoivent plus de budget que si nécessaire. Ainsi, les déploiements restent prévisibles et les déploiements ne sont pas bloqués.
Éviter l'hébergement partagé et le voisin bruyant
Dans les environnements partagés, je fixe des limites au niveau du paquet ou de l'abonnement afin de pouvoir Équité entre les clients. Des panels comme Plesk facilitent cela grâce à un gestionnaire de groupes de travail qui alloue le CPU, la RAM et les E/S par abonnement et les représente visuellement. En outre, j'active des notifications afin de détecter immédiatement les pics de charge et de réagir. Ceux qui souhaitent comparer Tenant-Isolation en détail peuvent jeter un coup d'œil sur Isolation Tenant de la page. Ainsi, tous les sites web restent réactifs, même si certains clients génèrent plus de trafic par moments.
Bien définir les limites : cgroups vs. ulimits
cgroups plafonnent l'utilisation réelle, tandis que ulimits s'applique surtout par processus ou limites dures de shell, c'est pourquoi j'utilise Combinaisons de manière ciblée. Pour le CPU, la RAM et les E/S, je définis des cgroups clairs, pour les fichiers ouverts ou les processus, je les contrôle en plus par ulimit. J'évite ainsi les goulots d'étranglement des descripteurs de fichiers tout en maintenant l'utilisation globale sous contrôle. Si vous voulez rafraîchir vos connaissances sur les limites du système, vous trouverez un bon aperçu à l'adresse suivante Limites d'utilisation dans l'hébergement. Ces deux niveaux fournissent ensemble les vis de réglage les plus fines pour une séparation équitable des clients.
Surveillance et alerte
Sans mesure, je prends des décisions à l'aveuglette. Seuils pour les alertes. Des outils comme systemd-cgtop, ps, pidstat ou Prometheus-Exporter me montrent en direct quel groupe utilise actuellement des ressources. Dans les tableaux de bord, je relie les cgroups à des tableaux de bord qui marquent les valeurs limites et rendent les tendances visibles. Des alertes par e-mail ou par chat m'informent lorsque des groupes dépassent des limites ou réduisent fréquemment leurs débits. Cela me permet de détecter rapidement les goulots d'étranglement et d'adapter les limites, d'étendre le matériel ou d'optimiser les chemins de code.
Approfondissement du monitoring : indicateurs, PSI et alertes pertinentes
J'observe non seulement l„“utilisation„, mais aussi la “pression". Dans cgroups v2, je lis cpu.stat (entre autres throttled_us), memory.current, memory.high, memory.events, io.stat et les fichiers pressure. À partir de là, je construis des alertes qui réagissent rapidement aux pénuries de ressources, sans être agaçantes lors de brefs pics.
- CPU : m'alerter lorsque throttled_us augmente en permanence et que les latences se dégradent en même temps. J'augmente alors CPUWeight ou desserre cpu.max.
- Mémoire : memory.current proche de memory.high est un signal d'alerte. Si memory.events signale souvent „high“, j'augmente high ou j'optimise les caches.
- I/O : io.stat indique les rbps/wbps et les temps d'attente. Je corrige les ralentissements permanents via io.weight ou des périphériques dédiés.
- PSI : Une pression „some“/„full“ persistante est un indicateur que les charges de travail attendent régulièrement des ressources. Je m'en sers pour la planification des capacités.
Meilleures pratiques pour une configuration propre
Je commence toujours avec des valeurs conservatrices, parce que les couvercles trop pointus, justement aux heures de pointe Performance les coûts. Ensuite, je charge le service de manière ciblée avec des benchmarks comme ab, siege ou wrk afin d'augmenter progressivement les quotas. Pour les hôtes multi-applications, j'organise des groupes dans des tranches afin que les services importants aient la priorité sans que les autres soient privés de tout. Je fixe des limites d'E/S de manière à ce que les pics courts passent, mais que les phases plus longues soient freinées. Des révisions régulières évitent que les limites ne deviennent obsolètes lorsque les profils de charge changent.
Migration de la v1 vers la v2 : voici comment je procède
Je planifie la transition comme une mise à niveau régulière de l'infrastructure. Je vérifie d'abord si le noyau et systemd activent v2 par défaut. Si nécessaire, je démarre avec des paramètres de démarrage appropriés et je valide que l'arborescence unifiée est active. Ensuite, je teste toutes les intégrations (panels, agents, sauvegardes, runtime de conteneurs) dans un environnement de staging.
- Détection : mount | grep cgroup2 ou systemd-cgls me montre si v2 est en cours d'exécution.
- Paramètres de démarrage : Si nécessaire, je définis cgroup_no_v1=all ou des options unifiées pour que seule v2 soit active.
- Mapping des contrôleurs : blkio devient io ; je remplace certaines fonctionnalités v1 (net_cls/prio) par du contrôle de trafic avec des classificateurs cgroup ou BPF.
- Faire migrer les politiques : Utiliser des poids au lieu de quotas rigides, introduire memory.high, limiter le swap séparément.
- Adapter le monitoring : Appliquer les nouveaux chemins et champs (cgroup.events, cpu.stat) dans les tableaux de bord.
Compléter l'isolation du processus
cgroups résolvent le problème des ressources, mais pour l'accès au système, je sépare en plus Espaces de noms et les couches de fichiers. Chroot, CageFS, Namespaces et Jails cloisonnent les chemins, les objets du noyau et les périphériques afin que les mandants ne puissent pas s'atteindre mutuellement. Cette couche de protection complète judicieusement les limites, car elle réduit le rayon des dommages et la surface d'attaque. Tu trouveras ici un aperçu concentré des principales variantes : Isolation des processus. En combinaison avec cgroups, on obtient une séparation nette des mandants pour les configurations d'hébergement de toutes tailles.
Scénarios pratiques et tuning
En cas de pics de trafic dans les configurations CMS, je donne de l'air à l'unité centrale à court terme, mais je maintiens la RAM afin de pouvoir Sécurité contre les pannes d'OOM. Pour les boutiques gourmandes en données, je règle blkio pour que les indexations ne ralentissent pas tous les autres processus de lecture. J'épingle les processus d'analyse ou de travail sur quelques cœurs avec cpuset, afin que les travailleurs du web puissent répondre sans être dérangés sur les autres cœurs. Je déplace les tâches d'arrière-plan dans des groupes avec des poids CPU plus faibles, afin que les requêtes frontales restent fluides. Pour les clients dédiés, j'autorise memory.min afin d'assurer à une application premium une petite base de RAM garantie.
Résolution des problèmes et écueils typiques
Certains schémas d'erreur se répètent. Je garde un œil sur les points suivants afin de gagner du temps lors de la recherche d'erreurs :
- Quotas CPU trop durs : le throttling permanent augmente la latence. Mieux vaut travailler avec cpu.weight et cpu.max uniquement comme ceinture de sécurité.
- Pression de la mémoire sans OOM : memory.high limite efficacement, mais si le cache de page est trop limité, les latences I/O augmentent. Équilibrer finement et ajuster les caches de manière ciblée.
- Les effets de la permutation : Trop de swap rend les systèmes lents. C'est pourquoi les services critiques fonctionnent avec memory.swap.max=0, mais l'ensemble du système est protégé par une petite mémoire tampon.
- Oublier les sous-arbres : Sans entrée dans cgroup.subtree_control, les limites enfants n'ont pas d'effet. Toujours commencer par activer les contrôleurs dans le nœud parent.
- Mauvais groupe : les processus atterrissent parfois dans la mauvaise tranche. Je vérifie avec systemd-cgls et corrige les options de l'unité de service (slice=, delegate=).
- pids.max trop bas : le démon avec de nombreux threads de travail échoue en silence. Choisir une mémoire tampon généreuse et la suivre dans le monitoring.
- Limites d'E/S par périphérique : pour RAID/LVM, prendre le bon Major:Minor ou définir des limites sur les périphériques de bloc visibles que la charge de travail utilise réellement.
- Priorisation du réseau : net_cls/prio sont v1-Legacy. En v2, je mise sur le contrôle de trafic avec des classificateurs cgroup ou BPF pour gérer les flux de trafic.
Rôles, profils et modèles d'équité
J'aime travailler avec des profils de service clairs, que je stocke sous forme de modèles et que je déploie de manière automatisée :
- Premium (or) : Poids élevé du CPU et des E/S, memory.min pour une base garantie, limite memory.max dure avec une réserve suffisante.
- Standard (argent) : Poids moyen, io.weight modéré, memory.high légèrement en dessous du pic pour éviter les excroissances de cache.
- Arrière-plan (bronze) : Faible poids CPU/I/O, cpu.max strict pour découpler les charges de travail interactives.
En outre, je réserve des noyaux et de la RAM pour l'hôte et l'infrastructure centrale (surveillance, journalisation, sauvegarde). J'évite ainsi que les clients ne sous-estiment la surcharge du système. Pour les hôtes NUMA, je veille avec cpuset à conserver la mémoire localement par rapport aux cœurs utilisés - cela réduit les pics de latence pour les services gourmands en mémoire.
Bref résumé
Avec cgroups, je définis des garde-fous clairs pour le CPU, la RAM, les E/S et le réseau, ce qui me permet de Équité entre les services et d'atténuer les goulets d'étranglement. L'architecture unifiée de cgroups v2 facilite la planification, l'exploitation et le dépannage par rapport à la v1. Dans les conteneurs, les hébergements partagés et les environnements mixtes, je peux ainsi contrôler les „voisins bruyants“ et protéger les charges de travail critiques. Le monitoring et les alarmes judicieuses me donnent des signaux précoces lorsque les limites ne correspondent plus aux profils de charge. En combinant cgroups avec l'isolation des processus, les ulimits et un tuning propre, on construit une plateforme d'hébergement fiable, aux performances constantes et qui traite les clients de manière équitable.


