Optimisez vos nœuds : Désactivez le swap sans risquer le crash

Vos applications ralentissent mystérieusement sous forte charge ? Apprenez à configurer correctement le cgroup v2 et l'éviction mémoire pour éviter que Linux ne sature votre CPU avec du swap inutile.

Comprendre l'enfer du thrashing mémoire et l'environnement de contrôle

Illustration conceptuelle d'un processeur surchargé par des cycles d'écriture en écriture de swap, représenté sous la forme de rouages mécaniques s'échauffant et d'une jauge de mémoire saturée.

Le piège mortel de la swap incontrôlée

Le thrashing mémoire est comparable à un cuisinier qui n'aurait plus de place sur son plan de travail. Au lieu de cuisiner, il passe tout son temps à faire des allers-retours entre sa cuisine et la cave pour stocker et récupérer ses ingrédients. En production, ce phénomène détruit les performances de vos applications. La latence s'envole, le CPU s'asphyxie et le serveur ne répond plus du tout, tout en restant techniquement "actif" pour vos outils de monitoring traditionnels qui ne détectent pas de panne franche.

Pour éviter cette paralysie silencieuse, nous devons confier la surveillance de nos conteneurs et de nos services système à cgroup v2. Contrairement à la première version, cette réécriture moderne du noyau Linux introduit une gestion unifiée des ressources et intègre nativement le concept de Pressure Stall Information (PSI). Le PSI agit comme un indicateur d'effort qui remonte en temps réel le pourcentage de temps CPU perdu à attendre de la mémoire ou des entrées-sorties disque.

Pourquoi cgroup v2 change la donne

Le modèle cgroup v1 gérait les ressources dans des hiérarchies séparées, rendant impossible la corrélation entre l'utilisation du processeur, de la mémoire et des disques. La version v2 unifie cette structure, permettant enfin d'appliquer des règles d'éviction intelligentes basées sur la souffrance réelle du système plutôt que sur des seuils de consommation arbitraires.

Préparation de l'environnement sous cgroup v2

Avant de déployer nos politiques d'éviction, nous devons nous assurer que votre système d'exploitation utilise bien la hiérarchie unifiée cgroup v2 et que le PSI est activé au niveau du noyau. Les distributions d'entreprise récentes l'activent par défaut, mais une vérification rigoureuse s'impose.

Exécutez la commande suivante dans votre terminal de production pour confirmer la configuration actuelle de votre système :

grep -E "cgroup2|psi" /proc/filesystems /proc/cmdline

Résultat:

nodev	cgroup2
BOOT_IMAGE=/vmlinuz-linux root=UUID=... rw psi=1

Si la ligne contenant psi=1 n'apparaît pas ou si cgroup2 n'est pas répertorié, vous devez modifier les paramètres d'amorçage de votre noyau. Ouvrez le fichier de configuration du chargeur de démarrage /etc/default/grub et ajoutez les paramètres requis à la ligne des arguments par défaut.

# Modification des paramètres du chargeur de démarrage
sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="systemd.unified_cgroup_hierarchy=1 psi=1 /' /etc/default/grub
sudo update-grub

Le paramètre systemd.unified_cgroup_hierarchy=1 force l'activation de la hiérarchie unique de cgroup v2, tandis que psi=1 ordonne au noyau de calculer les statistiques de blocage pour le processeur, la mémoire et les entrées-sorties. Un redémarrage de la machine est indispensable pour appliquer ces modifications de bas niveau.

Configuration initiale et validation des limites système

Schéma abstrait de conteneurs de ressources étiquetés avec des limites de mémoire strictes, illustrant la barrière infranchissable de la mémoire de secours.

Implémentation de base avec systemd-run

Pour comprendre comment le noyau réagit lorsque les limites physiques sont atteintes, nous allons exécuter un processus isolé au sein d'un cgroup temporaire managé par systemd. Cette approche évite d'écrire manuellement dans les dossiers virtuels de /sys/fs/cgroup, une pratique risquée en production.

Nous allons lancer une commande qui consomme volontairement de la mémoire tout en lui imposant une limite stricte de 500 Mo. Si le processus tente de dépasser cette valeur, le noyau refusera de lui allouer des pages physiques supplémentaires.

systemd-run --user --scope -p MemoryMax=500M -p MemorySwapMax=0M --unit=test-limite-ram tail /dev/zero

Examinons de près les paramètres appliqués à cette commande pour en comprendre la portée :

  • --scope : Exécute le processus de manière synchrone dans la session actuelle de l'utilisateur tout en l'associant à un cgroup dédié.
  • MemoryMax=500M : Définit la limite absolue de mémoire physique de travail. Au-delà, l'allocation échoue immédiatement.
  • MemorySwapMax=0M : Désactive complètement l'utilisation du swap pour ce cgroup. C'est l'instruction clé pour empêcher le thrashing.
  • --unit=test-limite-ram : Assigne un nom explicite au service pour faciliter son suivi et sa gestion par le gestionnaire d'initialisation.

Vérifions maintenant la création effective du groupe de contrôle et l'application des restrictions en lisant directement les fichiers d'état gérés par le système d'exploitation :

cat /sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/app.slice/test-limite-ram.scope/memory.max

Résultat:

524288000

La valeur retournée correspond exactement à la limite de 500 Mo convertie en octets. Le noyau Linux appliquera désormais une surveillance matérielle stricte sur ce groupe de processus, interdisant tout débordement mémoire et éliminant tout recours au stockage sur disque.

Architecture résiliente et éviction mémoire de production

Visualisation de flux de données montrant l'analyse de la pression mémoire et la résiliation ciblée des processus non prioritaires pour sauver le noyau.

Configuration avancée de systemd-oomd pour la production

Le tueur de processus par défaut du noyau, le fameux OOM Killer (Out Of Memory Killer), intervient souvent trop tard. Lorsque la mémoire physique est totalement épuisée, il choisit sa cible de manière brutale, tuant parfois votre base de données principale ou votre serveur web. Pour remédier à cela, nous allons configurer systemd-oomd, un démon moderne qui utilise les métriques PSI de cgroup v2 pour tuer les services en souffrance bien avant que le noyau ne panique.

Nous allons créer une configuration de production pour nos applications en définissant une tranche de ressources (slice) personnalisée qui protégera les services critiques tout en sacrifiant les tâches secondaires sous forte pression.

Créez le fichier de configuration de la tranche d'application à l'emplacement /etc/systemd/system/production-apps.slice :

[Unit]
Description=Tranche de production pour applications conteneurisees
Before=slices.target

[Slice]
# Activation de la comptabilite des ressources
MemoryAccounting=yes
CPUAccounting=yes

# Seuils de regulation memoire
MemoryHigh=3.5G
MemoryMax=4G
MemorySwapMax=500M

# Configuration de systemd-oomd
ManagedOOMMemoryPressure=kill
ManagedOOMMemoryPressureLimit=80%
ManagedOOMSwap=kill

Décortiquons chaque directive technique pour en assimiler le comportement opérationnel :

  • MemoryHigh : Seuil de sécurité à partir duquel le noyau commence à ralentir activement les allocations de mémoire du processus et à récupérer agressivement du cache disque.
  • MemoryMax : Limite physique absolue. Si le service l'atteint, l'allocation est stoppée et le processus peut être éliminé.
  • ManagedOOMMemoryPressure=kill : Autorise systemd-oomd à surveiller cette tranche et à tuer les cgroups enfants si la pression sur la mémoire devient insupportable.
  • ManagedOOMMemoryPressureLimit=80% : Seuil de déclenchement. Si les processus perdent plus de 80% de leur temps d'exécution à cause d'attentes d'allocation mémoire pendant plus de 20 secondes, l'éviction s'active.
  • ManagedOOMSwap=kill : Ordonne la destruction immédiate des processus de cette tranche si l'utilisation globale de la swap du système dépasse 90% et que cette tranche y contribue fortement.

Analyse des flux d'éviction mémoire

Le diagramme ci-dessous illustre le mécanisme de décision et d'escalade des ressources lorsque la mémoire physique d'un serveur commence à saturer sous l'effet d'une charge applicative intense.

Schéma décrivant la transition de la mémoire d'un état sain vers l'activation de la protection de systemd-oomd.

Le flux démontre que la surveillance basée sur le PSI (Pressure Stall Information) offre une zone tampon essentielle. Le service en souffrance est éliminé proprement dès que la perte d'efficacité dépasse le seuil tolérable de 80 %, évitant ainsi d'atteindre la zone fatale où le système d'exploitation complet perd sa réactivité.

Monitoring et validation des alertes d'éviction

Mettre en place des politiques d'éviction automatiques n'a de sens que si vous êtes immédiatement informé de leur exécution. Un serveur qui tue silencieusement ses propres conteneurs en boucle est une bombe à retardement pour votre infrastructure de production.

Simulation d'une tempête mémoire et lecture des logs de production

Pour valider notre configuration de production sans risquer de perturber d'autres services, nous allons forcer un dépassement de mémoire contrôlé. Nous utiliserons l'outil de test de charge stress-ng au sein d'un service système rattaché à notre tranche protectrice.

Déclarons un service éphémère et injectons-lui une charge mémoire supérieure à sa limite autorisée :

systemd-run --slice=production-apps.slice --unit=api-simule stress-ng --vm 1 --vm-bytes 5G --timeout 60s

Dès que la consommation de la tâche fictive dépasse les seuils définis, systemd-oomd intercepte l'anomalie grâce aux statistiques PSI et met fin à l'exécution du processus hors limites. Inspectons les journaux d'événements du système pour analyser la signature de cette éviction propre.

journalctl -u systemd-oomd.service --since "5 minutes ago" --no-pager

Résultat:

systemd-oomd[450]: Killed /production-apps.slice/api-simule.service due to memory pressure being 84.12% > 80.00% for 20s!
systemd-oomd[450]: Sent kill signal to 12945 (stress-ng-vm)
systemd[1]: api-simule.service: Main process exited, code=killed, status=9/KILL
systemd[1]: api-simule.service: Failed with result 'oom-kill'.

L'analyse des journaux démontre que l'éviction s'est déroulée de manière chirurgicale. Contrairement à l'OOM Killer du noyau qui aurait pu cibler un service aléatoire, le démon systemd-oomd a identifié précisément l'unité api-simule.service au sein de la tranche supervisée. Le reste du système n'a subi aucun ralentissement, aucune écriture de swap intempestive, préservant ainsi la réactivité globale de votre serveur de production.

Maîtriser la mémoire pour garantir la haute disponibilité

Le contrôle de la mémoire sous Linux ne doit plus être un processus passif soumis aux réactions imprévisibles du noyau. En exploitant la puissance de cgroup v2 et les métriques de blocage temps réel de systemd-oomd, vous transformez vos serveurs d'une posture de survie réactive en une plateforme d'exécution prévisible et résiliente.

Appliquez ces configurations de tranches de ressources sur vos orchestrateurs et vos serveurs bare-metal pour isoler vos charges de travail critiques. Vous éliminerez ainsi définitivement le spectre des pannes de CPU causées par l'écriture frénétique sur le disque de swap, garantissant une expérience utilisateur fluide même sous des tempêtes de trafic imprévues.

Espace commentaire

Écrire un commentaire

Rejoignez la discussion

Vous devez être connecté pour poster un message.

28 commentaires

xavier87
Membre
Avatar de xavier87
xavier87
Membre

Génial, j'ai enfin compris pourquoi mon serveur web mouline dans le vide en pleine nuit.

11/06/2026 à 15:02
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

Oui, obligatoire. Les paramètres psi=1 et systemd.unified_cgroup_hierarchy=1 sont passés au noyau au boot.

11/06/2026 à 05:30
jmace
Membre Actif
Avatar de jmace
jmace
Membre Actif

Est-ce que je dois reboot après le changement de grub ?

10/06/2026 à 18:10

Merci pour le guide, enfin une explication simple sur le PSI.

10/06/2026 à 06:32
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

Oui, Go gère sa propre mémoire, mais si tu le limites avec MemoryMax, le runtime Go s'adaptera (GC plus agressif) ou mourra proprement.

10/06/2026 à 00:20

Est-ce que ça fonctionne avec les applications en Go ?

09/06/2026 à 13:06
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

Vérifie que tu as bien les droits root ou que ton utilisateur est dans le bon groupe. Sinon, essaie sans --user pour voir si c'est une limite de session.

09/06/2026 à 02:47
vadam
Membre
Avatar de vadam
vadam
Membre

J'ai testé systemd-run et j'ai une erreur Unit test-limite-ram.scope failed to load.

08/06/2026 à 16:56
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

80% c'est le sweet spot. Si tu montes trop, tu laisses le temps au thrashing de s'installer. Si tu baisses, tu risques des faux positifs.

08/06/2026 à 05:01
anouk-carre
Membre
Avatar de anouk-carre
anouk-carre
Membre

Petite question : ManagedOOMMemoryPressureLimit=80%, c'est pas trop agressif ?

07/06/2026 à 21:38
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

Content que ça aide. C'est le but, arrêter de subir les décisions aléatoires du kernel.

07/06/2026 à 10:29
hardy-elise
Membre Actif
Avatar de hardy-elise
hardy-elise
Membre Actif

Article très clair. J'ai mis en place la slice sur mes nœuds k8s, ça a l'air de stabiliser le load average.

07/06/2026 à 05:04
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

Si t'as pas update-grub, édite directement /boot/grub/grub.cfg ou ton fichier de config EFI, mais fais gaffe à ne pas casser le boot.

06/06/2026 à 19:31
jerome25
Membre
Avatar de jerome25
jerome25
Membre

Mon update-grub ne trouve pas le fichier. Je suis sur une distrib custom.

06/06/2026 à 15:14
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

C'est tout l'intérêt. Mieux vaut un service qui crash proprement qu'un serveur entier qui freeze à cause du swap thrashing.

06/06/2026 à 06:19
leon60
Membre
Avatar de leon60
leon60
Membre

Le MemorySwapMax=0M est radical. J'ai peur que le système plante si j'ai un pic imprévu.

06/06/2026 à 00:11
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

Carrément. node_exporter récupère les métriques cgroup v2. Regarde du côté de container_memory_failures_total.

05/06/2026 à 14:36
gay-monique
Membre Actif
Avatar de gay-monique
gay-monique
Membre Actif

Est-ce que je peux monitorer ces évictions via Prometheus ?

05/06/2026 à 03:40
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

T'es sur quelle version de systemd ? Cette directive est apparue récemment. Fais un systemctl --version. Si tu es en dessous de la 247, c'est normal que ça passe pas.

04/06/2026 à 21:17
tessier-philippe
Membre Actif
Avatar de tessier-philippe
tessier-philippe
Membre Actif

J'ai une erreur sur ManagedOOMSwap=kill. Il me dit que c'est invalide.

04/06/2026 à 16:52
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

Utilise systemctl show sur ta slice :

systemctl show production-apps.slice -p MemoryMax
04/06/2026 à 12:36
tdelaunay
Membre
Avatar de tdelaunay
tdelaunay
Membre

Comment je vérifie que ma config de slice est bien prise en compte après le systemctl daemon-reload ?

04/06/2026 à 00:49
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

Ça dépend de ton heap. MemoryHigh est là pour déclencher le throttling avant le OOM. Ajuste selon tes besoins mais garde une marge par rapport à MemoryMax.

03/06/2026 à 18:34
monique59
Membre
Avatar de monique59
monique59
Membre

Merci pour l'astuce, ça m'a sauvé d'un crash de base de données. Par contre, MemoryHigh à 3.5G, c'est pas un peu bas pour une app Java ?

03/06/2026 à 10:15
kwagner
Auteur
Avatar de kwagner
kwagner
Auteur

Vérifie ta version de noyau. Le PSI nécessite au minimum un noyau 4.20. Si tu es sur une vieille Debian, c'est mort sans upgrade.

03/06/2026 à 02:34

Rejoindre la communauté

Recevoir les derniers articles gratuitement en créant un compte !

S'inscrire