Grâce à systemd resource, je gère de manière ciblée le CPU, la RAM, les E/S et les PID des services Linux, ce qui me permet de planifier efficacement les services productifs. Les étapes suivantes montrent de manière concrète comment je définis des limites dans les unités et les tranches, m'appuie sur les cgroups v2 et résous les conflits de ressources à l'aide de règles claires ; ainsi, chaque Instance prévisible.
Points centraux
Le tableau récapitulatif ci-dessous rassemble les principaux points que j'explique en détail dans l'article ; il sert de guide rapide Guide.
- cgroupes v2 sous la forme d'une hiérarchie unifiée avec systemd comme gestionnaire central
- Types d'unités Combiner de manière ciblée le service, le périmètre et la tranche
- Quota CPU et Poids du CPU pour une répartition équitable de la charge du processeur
- MemoryMax et MémoireHaute contre les erreurs OOM et la limitation de débit
- Tranches pour les limites par groupe et les priorités dans le fonctionnement du serveur
Pourquoi systemd et cgroups v2 fonctionnent ensemble
Je regroupe tous les processus dans des cgroups v2 et j'utilise systemd comme centre de contrôle. La hiérarchie uniforme sous /sys/fs/cgroup regroupe clairement les contrôleurs tels que cpu, memory, io et pids. Chaque unité dispose de son propre cgroup, ce qui me permet d’appliquer des limites de manière cohérente à toute une famille de services. Cette structure empêche les PID individuels de contourner les limites, car c’est le groupe dans son ensemble qui compte. Depuis la version 232 de systemd, celui-ci gère la hiérarchie de manière exclusive et enregistre les limites dans les interfaces du noyau ; je n’autorise la délégation que de manière délibérée, afin que rien ne passe à côté des contrôles. C’est ainsi que je maintiens mon Ressources maîtrisable à tout moment.
Comprendre les types d'unités : service, portée, tranche
J'encapsule les démons classiques dans Service-Unités et regroupe les processus lancés en externe dans des « scopes ». Pour la hiérarchie, je crée des « slices » qui, en tant que nœuds internes, définissent des ressources pour des groupes entiers. Les services et les scopes constituent les feuilles qui héritent des limites de la tranche correspondante. Je répartis ainsi les budgets CPU, mémoire et E/S le long de l’arborescence, au lieu de considérer chaque service de manière isolée. Pour les débutants, il est utile de consulter Gérer efficacement les services d'hébergement, afin de comprendre le rôle des unités dans le fonctionnement du serveur et de créer ses propres Tranches de planifier.
Vérifier les conditions préalables : hiérarchie et contrôleur uniformes
Je m'assure que le système fonctionne en mode unifié et que tous les contrôleurs nécessaires sont actifs. Je le vérifie via /sys/fs/cgroup (un point de montage) et en m'assurant que systemd gère l'arborescence. Si des contrôleurs manquent (par exemple « io »), je vérifie la configuration du noyau et, le cas échéant, les paramètres de démarrage. En particulier dans les environnements plus anciens, je procède délibérément à une migration de la version v1 vers la version v2 afin que les directives décrites, telles que IOWeight, MemoryHigh ou AllowedCPUs, prennent effet. Ce n’est que lorsque la comptabilité et les contrôleurs sont opérationnels qu’il vaut la peine de peaufiner les pondérations et les quotas.
Contrôle du processeur : utiliser correctement CPUWeight et CPUQuota
Je gère les parts de CPU via Quota CPU et les priorités relatives via CPUWeight. Un quota de 50% limite le service à la moitié du temps de processeur, tandis qu’un poids de 200 lui donne la priorité sur les services ayant des poids inférieurs. Cela me permet de réguler les tâches à charge continue sans ralentir les services interactifs. Dans la pratique, je commence par définir des quotas modérés, j'observe les latences et j'augmente le poids des services importants. C'est ainsi que je répartis la temps de calcul par ordre d'importance plutôt que de manière aléatoire.
Affinité CPU, AllowedCPUs et périodes de quotas
Lorsque je souhaite affecter des cœurs de manière fixe, j'utilise CPUAffinity ou le contrôle plus précis de cpuset via AllowedCPUs. Cela me permet, par exemple, de séparer les charges de travail par lots des services sensibles à la latence sur des cœurs distincts. Pour les pics de charge, j'ajuste la valeur de `CPUQuotaPeriodSec` : une période plus longue autorise des pics plus importants et de courte durée dans les limites d'un même quota moyen, ce qui améliore la latence P99 pour les services présentant des pics de charge.
[Service]
Sélection des cœurs # (sched_affinity) vs. cpuset (cgroup v2)
CPUAffinity=0 1 2 3
AllowedCPUs=0-3
# 150% Temps total avec une période de 200 ms (plus de marge pour les pics d'activité)
CPUQuota=150%
CPUQuotaPeriodSec=200 ms
# Pondération relative au sein d’une même tranche
CPUWeight=200
Limites de mémoire avec MemoryMax, MemoryHigh, MemoryLow
Je fixe une limite stricte avec MemoryMax, afin d'éviter les situations OOM dues à des valeurs aberrantes. Avec MemoryHigh, je limite l'accès à la mémoire avant même d'atteindre la limite maximale, ce qui améliore la stabilité globale. MemoryLow et MemoryMin offrent des zones de protection aux services, afin que le noyau récupère d’abord la mémoire d’autres groupes. Cette hiérarchisation empêche les effets en cascade lorsque plusieurs services grandissent simultanément. Si vous cherchez des informations complémentaires sur le contrôleur, vous en trouverez sur Le contrôleur de mémoire expliqué une introduction claire aux notions associées Mécanismes.
Définir délibérément la stratégie de swap et le comportement en cas d'épuisement de la mémoire (OOM)
Je définis clairement si une unité est autorisée à utiliser l’espace d’échange et dans quelle mesure. Avec MemorySwapMax, je fixe une limite maximale à l’utilisation combinée de la RAM et de l’espace d’échange. Pour les services sensibles à la latence, je limite souvent fortement l’utilisation de l’espace d’échange, voire je le désactive, afin d’éviter les sorties de page. De plus, avec OOMScoreAdjust, je modifie la probabilité que le noyau termine certains processus, et avec OOMPolicy, je détermine comment systemd réagit à une situation OOM dans l’unité (par exemple, arrêter l’unité entière ou la laisser fonctionner).
[Service]
# 2 Go maximum, swap compris ; la limite stricte de RAM reste MemoryMax
MemoryMax=1,5 Go
MemorySwapMax=2 Go
# Priorisation de la décision OOM (valeur plus petite = plus protégé)
OOMScoreAdjust=-500
# Réaction lorsque l'OOM Killer intervient au sein de l'unité
OOMPolicy=stop
Grâce à cette combinaison, j'évite les swaps incontrôlés, je mets en place des scénarios de basculement bien définis et je gère de manière fiable les bases de données ou les caches en mémoire dans un cadre prévisible.
Limites d'E/S et de processus : IOWeight, bandes passantes et TasksMax
Je limite les vitesses de lecture et d'écriture via IOReadBandwidthMax et IOWriteBandwidthMax lorsque les disques sont partagés. Pour la priorisation relative, j’utilise IOWeight afin que les charges de travail centrales aient la priorité sur les flux par lots. Avec TasksMax, je fixe une limite maximale claire pour les processus et les threads, ce qui permet de mettre efficacement un terme aux « fork bombs ». Ces contrôles stabilisent les environnements multi-serveurs dans lesquels certaines tâches risqueraient sinon de monopoliser l'ensemble des E/S. C'est notamment sur les serveurs de compilation que je garantis ainsi des résultats reproductibles Débits à partir de
Contrôler de manière ciblée les E/S par périphérique
Dans les configurations hétérogènes associant des disques NVMe et des disques durs, je règle les paramètres au cas par cas pour chaque périphérique. Cela évite que les SSD rapides ne soient ralentis par un voisin bruyant sur le disque dur. La combinaison de pondérations relatives et de limites absolues par périphérique couvre la plupart des cas pratiques.
[Service]
# Poids relatif pour tous les périphériques
IOWeight=300
# Poids par périphérique (par ex. priorité au NVMe)
IODeviceWeight=/dev/nvme0n1 500
IODeviceWeight=/dev/sda 100
# Limite absolue par périphérique (débit de lecture/écriture)
IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 30M
Important : IOWeight n'a qu'un effet relatif entre les cgroups actifs ; les directives „ max “ fixent des limites strictes. Je commence souvent par définir des pondérations et je n'ajoute des limites strictes que lorsque je dois absolument contenir les « voisins bruyants ».
Configuration : fichiers Unit, modules Drop-in et set-property
Je saisis les limites directement dans le Unité-Fichier ou j'utilise des « drop-ins » qui laissent les fichiers d'origine intacts. Avec la commande `systemctl edit NOM.service`, je crée un fragment qui ajoute les directives `CPUQuota`, `CPUWeight`, `MemoryMax` et d'autres. Pour des tests rapides, j’utilise la commande systemctl set-property ; systemd enregistre proprement la modification dans un fichier « drop-in ». Après les ajustements, je redémarre les démons et je vérifie leur état pour m’assurer que les modifications ont bien pris effet. Cette méthode de travail permet d’éviter les conflits lors des mises à jour et fournit à chaque Modification dont l'historique est clair.
Priorités « drop-in », préréglages et valeurs par défaut
Je fais attention à l'ordre des fichiers « drop-in » : systemd les charge par ordre numérique ; un fichier « 90-override.conf », par exemple, remplace les fichiers « 10-*.conf » antérieurs. Je ne modifie pas les préréglages des fournisseurs ; je les remplace dans /etc afin que les mises à jour des paquets ne posent pas de problème. Je définis délibérément les paramètres par défaut à l’échelle du système, tels que DefaultTasksMax, DefaultCPUAccounting ou DefaultMemoryAccounting, dans systemd.conf afin de garantir des métriques et des garde-fous cohérents, y compris pour les nouvelles unités.
# Vérification des valeurs actives
systemctl show NOM.service -p CPUQuota -p CPUWeight -p MemoryMax
systemd-analyze dump | grep -E "Default(TasksMax|CPUAccounting|MemoryAccounting)"
# Ouvrir/créer un fichier de remplacement persistant
systemctl edit NAME.service
Slices en pratique : limiter judicieusement la taille des groupes
Je regroupe les services connexes dans des dossiers dédiés Tranches, comme web.slice, db.slice et batch.slice. Dans batch.slice, par exemple, j'autorise 200% de CPU et 4 Go de RAM, afin que les tâches en arrière-plan disposent de suffisamment de ressources sans empiéter sur les front-ends. J’affecte les services à leur « slice » cible via Slice= ; les limites s’appliquent alors conjointement à tous les membres. Ce regroupement simplifie considérablement les directives : un nouveau projet d’équipe hérite automatiquement des politiques de sa « slice ». Pour les groupes de clients ou d’applications isolés, il est également utile de consulter Isolation cgroups, afin d'effectuer la séparation proprement planifier.
Segments par défaut : system.slice, user.slice, machine.slice
Je laisse les services système dans le system.slice et je n'y applique des plafonds globaux qu'avec prudence, afin que les services essentiels ne soient pas privés de ressources. Les processus utilisateur sont placés dans « user.slice », où je limite les sessions interactives sans bloquer complètement les shells. Je regroupe les virtualisations et les conteneurs dans machine.slice et j'attribue des budgets clairs par machine virtuelle ou conteneur. Cette structure standardise l'organisation et offre des points d'ancrage pertinents pour créer ses propres tranches. Une héritance bien gérée permet d'éviter de nombreuses règles individuelles et de maintenir la Transparence haut.
Délégation pour les conteneurs et les charges de travail dynamiques
Lorsque je transmets des sous-arborescences à des environnements d’exécution en conteneur ou à des outils pilotés par l’utilisateur, je définis délibérément Delegate=yes, et uniquement aux endroits où un contrôle est nécessaire. Ainsi, systemd conserve la maîtrise, tandis que le destinataire de la délégation est autorisé à créer ses propres cgroups au sein de sa sous-arborescence. En combinaison avec les Scopes, je peux regrouper, limiter et relâcher proprement des processus de courte durée (par exemple, des tâches CI) sans diluer les tranches.
[Service]
# Autorise la gestion de la sous-arborescence cgroup (par exemple par le runtime du conteneur)
Delegate=yes
Slice=machine.slice
MemoryMax=4G
CPUWeight=300
Surveillance et dépannage : Status, cgtop, cgls
Je commence par vérifier avec systemctl status NAME.service, pour connaître les limites actives et le fonctionnement du service. Avec systemd-cgtop, je peux voir en temps réel l'utilisation du processeur et de la mémoire par cgroup. systemd-cgls m'affiche l'arborescence et met en évidence l'héritage. En cas d’anomalies, je consulte les fichiers dans /sys/fs/cgroup afin de vérifier les valeurs définies pour les contrôleurs. Ensuite, j’ajuste progressivement les quotas, j’observe les métriques et je documente chaque Modification.
Approfondir le suivi : comptabilité, PSI et tests rapides
Pour obtenir des indicateurs pertinents, j’active CPUAccounting, MemoryAccounting et IOAccounting au niveau des unités ou par défaut. Je surveille également les pics de charge à l'aide des informations de pression (PSI) du noyau, afin de détecter si les limitations (memory.high) s'intensifient ou si les ressources d'E/S sont durablement insuffisantes. Pour des tests reproductibles, je lance des charges de travail avec systemd-run comme portée et j’attribue temporairement des limites avant de les intégrer dans un drop-in persistant.
# Portée temporaire avec pondération E/S et CPU
systemd-run --scope -p IOWeight=400 -p CPUWeight=300 --unit test-batch -- dd if=/dev/zero of=/tmp/out bs=1M count=1024
Activer la comptabilité # sur une unité existante
systemctl set-property NAME.service CPUAccounting=yes MemoryAccounting=yes IOAccounting=yes
Résolution des problèmes et écueils typiques
- Harsh Caps vs. Burst : un CPUQuota trop restrictif sans période adaptée provoque des saccades. J'augmente la valeur de CPUQuotaPeriodSec ou je ne réduis le quota que modérément, et j'utilise davantage le paramètre CPUWeight.
- Le limiteur de mémoire intervient trop tôt : la valeur MemoryHigh est-elle trop faible ? Je vais l'augmenter ou définir MemoryLow afin que les chemins critiques ne soient pas récupérés de manière trop agressive.
- Périphériques d'E/S mal adressés : les directives IO* s'attendent à des périphériques blocs. Je vérifie le chemin d'accès au périphérique à l'aide de la commande lsblk et je définis des règles par périphérique, et non par point de montage.
- Les threads atteignent leur limite : une valeur TasksMax trop faible ralentit les pools de workers. Je dimensionne en fonction du nombre maximal de threads plus une marge de sécurité, et je surveille la colonne « Tasks » à l'aide de systemd-cgtop.
- Les modifications à la volée ne fonctionnent pas : après avoir apporté des modifications, j'exécute la commande « systemctl daemon-reload » et je vérifie avec « systemctl show » si les propriétés sont bien définies.
Bonnes pratiques en matière de priorités et de limites
Je regroupe les services par rôle, j'attribue des valeurs CPUWeight et IOWeight en fonction de leur importance et je définis des limites de mémoire strictes via MemoryMax. Les bases de données critiques bénéficient d’une priorité élevée et de quotas moins stricts, tandis que les rapports et les tâches batch sont soumis à des restrictions plus strictes. Je définis TasksMax lorsque les applications utilisent de nombreux workers ou qu’il existe un risque d’explosion du nombre de threads. Chaque ajustement est enregistré avec un numéro de version dans le référentiel, ce qui me permet de le retracer et de revenir en arrière si nécessaire. En environnement de préproduction, je calibre les valeurs en fonction des profils de charge, puis je les transfère de manière prudente vers le Production.
Tableau récapitulatif des principales directives
Ce tableau concis récapitule les réglages habituels et m'aide à trouver les bons Valeurs à choisir.
| Objectif | directive | Exemple de valeur | Effet |
|---|---|---|---|
| Part du CPU | Poids du CPU | 200 | Accorde une priorité plus élevée aux unités ayant une pondération inférieure ; répartit CPU équitable. |
| Taux d'utilisation du processeur | Quota CPU | 50% | Limite le temps de travail effectif ; idéal pour les tâches exigeant une charge de travail constante Emplois. |
| Mémoire physique | MemoryMax | 1G | Limite absolue ; empêche les erreurs OOM dues à des valeurs aberrantes dans le même Slice. |
| Mémoire non volatile | MémoireHaute | 800 m | Ralentit avant Max ; réduit la pression sur le Système. |
| Priorité d'E/S | IOWeight | 500 | Privilégie les services centralisés sur des ressources partagées Disques. |
| PID/threads | TasksMax | 512 | Limite le nombre de processus/threads ; protège contre Forks- Les avalanches. |
Cas d'utilisation dans le domaine de l'hébergement et de l'exploitation de serveurs
Je crée des fichiers personnalisés pour mes clients Tranches Je définis et attribue des budgets CPU et RAM à chaque client. Dans les architectures de microservices, les services API et d’authentification se voient attribuer des pondérations plus élevées, tandis que le reporting fonctionne de manière asynchrone. Pour les runners CI/CD, je crée une tranche de batch afin que les builds ne prennent jamais le pas sur les front-ends. Dans les environnements de conteneurs et de machines virtuelles, j'encapsule les charges de travail dans le fichier machine.slice et je définis clairement les budgets par client. Cette répartition réduit les effets de « voisin bruyant » et garantit la reproductibilité Latence aux heures de pointe.
Résumé
Je gère les services Linux à l'aide de systemd et de cgroups v2 Unité plutôt que sur des processus individuels. CPUQuota, CPUWeight, MemoryMax, MemoryHigh, IOWeight et TasksMax constituent mon ensemble de base pour une répartition équitable et des limites claires. Les « slices » permettent de mettre de l'ordre, de regrouper les règles et facilitent l'exploitation ainsi que la mise en place de nouveaux services. La surveillance à l'aide de `systemctl status`, `cgtop` et `cgls` m'indique rapidement où je dois procéder à des ajustements. Ainsi, les performances et la disponibilité restent prévisibles, et je limite les conflits de ressources à Contrôle.


