...

Explication des paramètres « OOM Score » et « OOM Score Adjust » dans le cadre de l'hébergement

Je vais expliquer le Score OOM et OOM Score Adjust comme leviers de contrôle concrets dans l'exploitation d'un hébergement : ils permettent de définir quels processus le « Linux OOM Killer » va arrêter en cas de pénurie de mémoire et lesquels il va protéger. Cela me permet de garder le contrôle lorsque RAM vient à manquer et veille à ce que les services essentiels restent disponibles en ligne.

Points centraux

Pour vous permettre de vous y retrouver rapidement, je résume brièvement les idées principales.

  • Priorité En cas de pénurie : le score OOM permet de déterminer quel processus doit être interrompu en premier.
  • Commande de précision avec oom_score_adj : de -1 000 (protéger) à +1 000 (sacrifier).
  • Dynamique au lieu d'une valeur fixe : la valeur varie en fonction de la charge et de la configuration.
  • Pratique d'hébergement: Protéger les services critiques, et plutôt arrêter les processus non critiques.
  • Causes Résoudre : vérifier les limites, les Cgroups et la planification de la mémoire vive.

Comment fonctionne le « OOM-Killer » sous Linux

En cas de pression de stockage le noyau Linux décide quels processus il va arrêter afin de maintenir la réactivité du système. J'observe alors comment le Noyau attribue à chaque processus une sorte de „ niveau de gravité “ qui dépend fortement de sa consommation actuelle de mémoire. Si la quantité disponible de RAM ou d’espace d’échange est insuffisante, l’OOM-Killer intervient et met fin au processus ayant le score le plus élevé. Ce mécanisme empêche les blocages, mais ne remplace pas une planification rigoureuse des capacités au niveau de l’hôte et des services. Je consulte les entrées du journal OOM pour déterminer si un service s’est fait remarquer en raison d’une consommation excessive de mémoire ou d’une mauvaise configuration.

Comprendre l'OOM Score : dynamique et échelle

Je vérifie le Score OOM d'un processus dans /proc/PID/oom_score et évalue ainsi à quel point il est actuellement menacé. L'échelle va pratiquement de 0 à 1 000 : plus la valeur est proche de 1 000, plus le processus risque d'être victime du « killer ». Cette valeur ne constitue qu'un instantané, car les pics de charge, les limites des Cgroups et la taille des caches varient en permanence. C’est pourquoi je n’évalue jamais ce score de manière isolée, mais toujours dans le contexte de la mémoire vive, de l’espace d’échange, de l’overcommit et des processus parallèles. En observant régulièrement ce score, on identifie des schémas typiques et on peut anticiper les goulots d’étranglement avant qu’ils ne mettent des services hors service.

Utiliser l'ajustement du score OOM de manière ciblée

Avec oom_score_adj Je fais varier activement la valeur attribuée à un processus entre -1 000 et +1 000. Si je lui attribue la valeur -1 000, je protège entièrement le processus, tandis que des valeurs positives élevées le rendent délibérément susceptible d'être sacrifié. Je fais preuve de modération, car un trop grand nombre de processus protégés restreint la marge de manœuvre de l’OOM Killer. Les candidats typiques pour des valeurs faibles sont SSH, les outils de surveillance, les front-ends de proxy inverse et les contrôleurs de bases de données sensibles. Les tâches en arrière-plan, les outils de rapport ou les workers éphémères se voient plutôt attribuer un ajustement plus élevé, afin que l’interface utilisateur reste réactive lorsque les ressources viennent à manquer.

Définir les priorités en matière d'hébergement

Dans les environnements de production, je définis des Priorités entre le front-end, l'API, la base de données et le traitement par lots. Je commence par déterminer quels services doivent rester actifs du point de vue de l'utilisateur, puis je leur attribue un réglage OOM adapté. Dans systemd, je définis pour cela OOMScoreAdjust= dans le fichier d'unité de service et je documente l'objectif de chaque valeur. Ceux qui gèrent déjà leurs services via systemd peuvent rationaliser leurs processus ; voici une introduction à ce sujet : systemd dans l'hébergement. Je prévois ainsi les pannes au lieu de les laisser au hasard, et je garantis ainsi un accès en ligne fiable pour les utilisateurs.

Cgroups, conteneurs et limites

Je n'oublierai jamais la cgroups, car les conteneurs et les services évoluent dans des environnements de ressources distincts. Un processus présentant un score OOM modéré peut tout de même s'arrêter si son cgroup est soumis à une limite de mémoire stricte et qu'il la dépasse brièvement. C'est pourquoi je vérifie les limites dans cgroup v2 et j'organise des limites strictes et souples en fonction des profils de charge. Ceux qui exploitent des environnements multi-locataires ou d’hébergement mutualisé tirent profit de quotas et d’une comptabilité correctement définis ; pour plus d’informations, consultez cgroup v2 en hébergement. Lorsque la coordination est bonne, le réglage OOM et les limites agissent comme un duo bien rodé de vis de réglage.

Diagnostic et surveillance en cas d'événements OOM

Quand ça explose, j'ai besoin d'informations claires Signaux et la reproductibilité de l'analyse. J'analyse les fichiers dmesg, journald et /var/log/kern.log, je sauvegarde les lignes OOM et je récupère le PID de la victime ainsi que les valeurs oom_score et oom_score_adj. Pour les contrôles de routine, j’utilise des scripts qui répertorient les plus gros consommateurs de mémoire et déclenchent des seuils d’alerte. Ceux qui souhaitent approfondir le sujet trouveront une approche structurée dans le Analyse de l'OOM-Killer. Dans les configurations permanentes, j'intègre des indicateurs tels que le RSS, le cache, les entrées/sorties de la mémoire swap et les limites des conteneurs dans le système de surveillance, afin de pouvoir détecter les tendances à temps.

Aide-mémoire sous forme de tableau pour les administrateurs

J'utilise le tableau suivant comme un résumé concis Guide, lorsque je hiérarchise les rôles et que je consigne les ajustements. La colonne „ Justification “ explique pourquoi un service se voit attribuer la protection ou le sacrifice. J’adapte les chiffres au projet, mais cette approche facilite la prise de décision rapide. Ceux qui utilisent ce tableau comme point de départ y gagnent en clarté lors des analyses rétrospectives et des demandes de modification. Un point essentiel : je prévois toujours une marge de manœuvre dans l’ensemble du système, afin que les suppressions radicales soient rarement nécessaires.

Composant Destination typique Exemple : oom_score_adj Justification
SSH-Daemon Tireurs de -500 à -900 Garantir l'accès pour les interventions, même en cas de goulots d'étranglement.
Proxy inverse (nginx/HAProxy) Tireurs de -300 à -700 Gérer le trafic entrant, afficher les pages d'erreur.
DB-Contrôleur/Instance principale Tireurs de -200 à -600 Maintenir les connexions, sécuriser l'accès aux données.
PHP-FPM/Workers d'application Neutre à prêt à se sacrifier 0 à +300 Un grand nombre de workers parallèles peuvent s'arrêter.
Tâches planifiées / Sauvegardes / Rapports Prêt à faire des sacrifices de +300 à +800 Reprogrammable sans incidence pour l'utilisateur.
Indexeur/Consommateur de file d'attente Prêt à faire des sacrifices de +200 à +600 Possibilité de faire une courte pause, à rattraper plus tard.

Limiter correctement les workers WordPress et PHP

Avec WordPress, je fais attention à Travailleur- le nombre de processus, la limite de mémoire (memory_limit) et les opérations lourdes telles que le traitement d'images ou les importations. Je configure PHP-FPM de manière à ce que le nombre de processus actifs soit adapté à la mémoire vive et ne provoque pas d'effet d'avalanche. Côté base de données, j'additionne les tailles des tampons et du cache, tout en laissant une marge de manœuvre pour éviter que les pics de charge ne bloquent tout le système. Je surveille l’OpCache, le cache d’objets et l’optimiseur d’images, car ils font rapidement grimper la consommation de mémoire. Je m’assure ainsi que de brefs pics de charge ne compromettent pas immédiatement les processus front-end essentiels.

Dans la pratique : politiques et guides opérationnels

Je tiens mes Politiques Concis et applicable, afin que l'équipe n'hésite pas en cas d'urgence. Cela implique notamment : définir les candidats à la protection, désigner les rôles des victimes, ajouter l'option OOMScoreAdjust= aux unités systemd et documenter les valeurs dans le dépôt. Je vérifie l’efficacité à l’aide d’outils et de charges de test jusqu’à ce que l’ordre des cibles sacrifiées corresponde aux objectifs. Ensuite, je rédige un guide d’intervention décrivant les journaux, les alertes et les premières mesures à prendre. Ainsi, la réaction reste cohérente, même lorsque de nouveaux collègues prennent le relais.

# Exemple de fragment pour une unité systemd
[Service]
OOMScoreAdjust=-400
# Rechargement et redémarrage :
# systemctl daemon-reload && systemctl restart nginx

# Vérification en cours d'exécution :
cat /proc/$(pidof nginx)/oom_score
cat /proc/$(pidof nginx)/oom_score_adj

# Augmenter/réduire temporairement (root) :
echo 300 | sudo tee /proc//oom_score_adj

Erreurs fréquentes et mesures correctives

De nombreux problèmes surviennent parce que Limites ne sont pas adaptés : trop de workers PHP, des caches de base de données trop volumineux et aucune marge pour les pics de charge. L'OOM-Killer intervient alors régulièrement, alors que quelques ajustements suffiraient. Je commence par ajuster le nombre de workers, j'évalue l'effet obtenu et je n'augmente la mémoire vive que si le besoin s'en fait clairement sentir. De même, définir de nombreux processus à -1 000 est néfaste, car le noyau a besoin d'une marge de manœuvre. Je définis les priorités avec discernement afin que le système puisse réagir de manière ordonnée en cas d'urgence.

Overcommit, swap et niveaux de mémoire

Je présente ma Stratégie de surengagement Je la configure délibérément, car elle détermine à quelle vitesse un système entre en zone OOM. Avec vm.overcommit_memory=0 (heuristique), je fonctionne souvent de manière stable, car le noyau évalue la limite de commit en fonction de l'utilisation et de l'historique. La configuration devient plus stricte avec vm.overcommit_memory=2 associé à vm.overcommit_ratio, qui définit l’occupation virtuelle maximale autorisée. Ceux qui définissent systématiquement vm.overcommit_memory=1 risquent que les réservations de mémoire aboutissent dans un premier temps, puis échouent complètement lors de l’allocation ultérieure – ce qui constitue souvent un terrain propice aux événements OOM sous charge.

Je calibre Swap de manière à ce qu’il offre une marge de manœuvre sans pour autant devenir un frein à la latence. Une valeur modérée de vm.swappiness permet de réserver de la mémoire vive pour les chemins d’accès fréquents, tandis que les pages rarement utilisées sont déplacées vers le swap. Je peux utiliser zswap ou zram comme réserve élastique lorsque les E/S sont lentes – cela réduit le risque d’OOM, mais coûte du CPU. Les niveaux de mémoire sont également importants : vm.min_free_kbytes doit être suffisamment élevé pour que le noyau puisse récupérer de la mémoire à temps. Si l’on définit des valeurs trop basses, on force le système à effectuer des récupérations frénétiques et on provoque des problèmes de chemins d’accès qui aboutissent à des situations OOM.

# Exemple : overcommit prudent et swap modéré
sysctl -w vm.overcommit_memory=2
sysctl -w vm.overcommit_ratio=90
sysctl -w vm.swappiness=30
# Pour les tests, enregistrer ces paramètres de manière persistante dans /etc/sysctl.d/

Options de systemd au-delà de OOMScoreAdjust

En plus d'OOMScoreAdjust, j'utilise systemd pour Glissières de sécurité à accumulation d'énergie à appliquer directement au service. Avec MemoryMax=, je fixe une limite stricte (cgroup memory.max) ; MemoryHigh= freine en douceur en cas de charge élevée et MemorySwapMax= limite les opérations de pagination. MemoryLow= et MemoryMin= donnent la priorité aux parts de cache d'un service en cas de pression, afin que les processus importants ne se « refroidissent » pas trop rapidement. Avec OOMPolicy=, je contrôle ce que systemd entreprend en cas d’OOM au niveau de l’unité (par exemple, arrêter uniquement le service ou mettre fin à l’ensemble des dépendances). Dans les « slices », j’agrège les rôles – front-end web, traitement par lots, base de données – et j’en déduis des règles uniformes afin qu’aucune valeur aberrante isolée ne déstabilise l’ensemble.

Je constate que la protection n'est jamais absolue : même les processus dotés d'une valeur de -1 000 peuvent être contraints de céder dans des situations désespérées. C'est pourquoi je fixe des valeurs généreuses, mais réalistes Minima (MemoryLow/Min) uniquement pour un très petit nombre de services essentiels, et je vérifie que la somme de toutes les allocations reste inférieure à la mémoire physiquement disponible. J'évite ainsi que des mesures de protection bien intentionnées ne rendent l'OOM-Killer aveugle.

Kubernetes et l'orchestration des conteneurs

Dans les environnements d'orchestration tels que Kubernetes, la logique OOM intervient à plusieurs niveaux. Je mets Requêtes et Limites de manière à ce que les pods soient classés dans la classe de qualité de service souhaitée : « Guaranteed » offre la meilleure protection, « Burstable » assure une certaine souplesse, tandis que « BestEffort » est le plus susceptible d'être affecté. Le kubelet attribue automatiquement les valeurs OOMScoreAdjust qui en résultent ; je planifie donc en fonction des spécifications de ressources plutôt qu’à l’aide de valeurs de réglage manuelles dans les conteneurs. Si un conteneur atteint sa limite de mémoire (memory.limit), il est arrêté au sein de son cgroup, même si l’hôte dispose encore de mémoire disponible ; il ne s’agit pas d’un OOM classique de l’hôte, mais d’une autodéfense ciblée de la limite.

Je prends en compte parts de mémoire native en dehors des configurations de tas (par exemple pour la JVM/le nœud), afin d’éviter que les conteneurs ne s’écrasent de manière inattendue lorsqu’ils atteignent leurs limites. De plus, je prévois des marges pour les pods en cas de pics de charge et je ne planifie l’overcommit des nœuds qu’avec modération, afin que les expulsions soient rares. Lorsque cgroup v2 est activé, j’utilise memory.oom.group de manière ciblée afin qu’en cas d’urgence, un groupe de processus entier soit arrêté de manière ordonnée, plutôt que de laisser des workers isolés dans un pod « zombie ». Cela permet de maintenir le système propre et de rendre la récupération prévisible.

Niveau de précision du diagnostic : SMaps, PSI et tests reproductibles

Pour les analyses approfondies, j'utilise /proc-Informations et indicateurs de charge. /proc/PID/status affiche les valeurs VmRSS, VmSwap et le nombre de threads ; /proc/PID/smaps_rollup regroupe des catégories telles que Anon, File et Shmem, sans que je me perde dans les détails. Cela me permet de déterminer si le cache de page est trompeur ou si les pages anonymes (véritable charge de travail) augmentent. Avec /proc/pressure/memory, je mesure PSI- les signaux indiquant le temps pendant lequel le système subit des opérations de récupération active ou des blocages. Je génère des alertes sur ces valeurs bien avant que l'OOM ne se déclenche – ce qui est idéal pour déclencher automatiquement des contre-mesures (limitation, mise à l'échelle, réduction du nombre de workers).

# Instantanés pertinents
journalctl -k -g "Out of memory|oom-killer"
cat /proc/pressure/memory
grep -E "VmRSS|VmSwap|Threads" /proc//status
cat /proc//smaps_rollup

# Reproduire un OOM (environnement de test !)
stress-ng --vm 2 --vm-bytes 80% --timeout 30s

Cas particuliers : JVM, Node.js et PHP dans des conteneurs

JVM-Les services nécessitent une attention particulière, car outre le tas, il faut également tenir compte du métaspace, des piles de threads, des tampons directs et du comportement de l'allocateur natif. Je gère les conteneurs de manière optimale à l’aide de MaxRAMPercentage et je configure un tas qui laisse une marge suffisante pour ces éléments. En cas de parallélisme élevé, je limite les pools de threads, car de nombreuses petites piles s’accumulent de manière problématique. Pour Node.js j'ajuste la valeur de –max-old-space-size en fonction de la limite du conteneur afin d'éviter les arrêts forcés. Et dans le cas de PHP-FPM Je calcule la valeur pm.max_children en fonction de la mémoire vive (RAM), de la consommation moyenne par requête et de la valeur memory_limit, en ajoutant une réserve pour les caches et le serveur web. Cela me permet d'éviter les « avalanches » insidieuses qui ne se manifestent qu'aux moments de pointe.

Je garde les Stratégie d'allocation À noter : glibc, lorsqu'elle utilise de nombreuses arènes, peut fragmenter la mémoire et faire grimper sa consommation dans les charges de travail comportant de nombreux threads. Pour certains services, jemalloc ou tcmalloc offrent des pics plus stables ; je teste cela de manière ciblée, je documente l'effet et je procède à un déploiement contrôlé. De plus, je limite les répertoires tmpfs dans le conteneur afin que les téléchargements ou les fichiers temporaires ne vident pas la mémoire vive à l'insu de l'utilisateur.

Tmpfs, pages géantes et cache de pages

tmpfs On a tendance à l'oublier : sans limite de taille, il grandit jusqu'à occuper une partie de la mémoire vive, et soudain, on manque d'espace ailleurs. Je monte les tmpfs en spécifiant délibérément la valeur « size= », notamment pour les chemins de compilation ou de téléchargement. Transparent Huge Pages (THP) La fragmentation et la latence ont une incidence ; pour les services où la latence est critique, j'utilise souvent „ madvise “ afin que seules les allocations adaptées en bénéficient. KSM permet de dédupliquer et d'économiser de l'espace disque, mais cela sollicite le processeur : c'est utile sur les hôtes de développement ; dans les environnements de production, je vérifie son impact et sa surcharge.

Le Cache de pages Ce n'est pas de la mémoire „ gaspillée “ ; elle accélère les E/S. Si je la libère de manière trop agressive ou si j'utilise les caches de suppression (Drop-Caches) comme mesure permanente, je transfère les coûts vers des pics de latence. Il vaut mieux définir des objectifs de mémoire par rôle et imposer un équilibre équitable via les mécanismes cgroup (memory.high / memory.max). Ainsi, les ensembles actifs des services importants restent en RAM et les situations OOM deviennent plus rares.

Résumé pour la vie quotidienne

J'utilise le Score OOM Je m'en sers comme baromètre du risque et j'ajuste l'ordre de priorité des victimes à l'aide de `oom_score_adj`. Je protège les services ayant un impact sur les utilisateurs, je fais en sorte que les tâches reportables puissent être sacrifiées, et je documente chaque valeur de manière traçable. Je planifie les limites des cgroups, le nombre de workers et la taille des caches de manière globale, afin d’éviter que les pics ne se transforment en crise généralisée. Les journaux, la surveillance et un petit guide d’intervention me permettent de détecter rapidement les incidents OOM et de les résoudre de manière ciblée. Grâce à cette discipline, l’hôte reste fiable et j’évite les mauvaises surprises pendant les nuits de production.

Derniers articles