...

Pression mémoire dans le noyau Linux : conséquences sur les systèmes d'hébergement

La pression mémoire dans le noyau Linux affecte directement les systèmes d'hébergement : lorsque la pression augmente, le temps CPU et les E/S se déplacent vers des opérations plus gourmandes en ressources Reclaim, les temps de réponse augmentent et les risques d'OOM s'accroissent. Je vais vous expliquer clairement comment je détecte, mesure et résous les problèmes de mémoire, afin que Hébergement- Réagir en permanence aux charges de travail.

Points centraux

Je me concentre sur les paramètres clés qui déterminent les performances et les pannes dans les environnements d'hébergement. Les points suivants constituent le fil conducteur sur lequel j'oriente mon diagnostic et mon optimisation. Grâce à cette vue d'ensemble, j'évite les interprétations erronées du message „ RAM pleine “ et j'identifie les véritables Pression en temps voulu.

  • Indicateurs PSI Ils indiquent les temps d'attente plutôt que la simple occupation et permettent de détecter rapidement les retards.
  • Charge de swap indique des problèmes de récupération qui aggravent les E/S et les latences.
  • Limites des cgroups Contrôler la limitation, la protection et le comportement en cas d'épuisement de la mémoire (OOM) pour chaque service.
  • Éviction du cache Cela a une incidence directe sur les performances du site Web et de la base de données.
  • Planification des capacités et le réglage permettent de conserver une marge dynamique et d'éviter le « thrashing ».

C'est ainsi que je structure mes analyses, du noyau jusqu'à l'application, et que je mets en œuvre les mesures appropriées par ordre de priorité. L'accent reste mis sur des éléments mesurables Effets, et non pas le travail à la pièce.

Que signifie « Memory Pressure » dans le noyau Linux ?

L'impression „ mémoire “ signifie que le noyau consacre un temps notable à libérer de la mémoire au lieu de poursuivre le travail des processus utilisateurs ; le processeur consacre alors davantage de temps aux opérations de lecture, d'écriture et d'éviction, tandis que les requêtes restent en attente. Je fais une distinction claire entre „ RAM pleine » et « manque de mémoire utilisable ». marge“ : Un cache „ plein “ est un bon signe ; les goulots d'étranglement n'apparaissent que lorsque les investissements en récupération augmentent. Le noyau analyse les listes inactives, écrit cochon-supprime les pages, vide le cache de fichiers et déplace les pages anonymes dès que les seuils de Watermarks ne sont plus respectés. Le temps consacré à ces opérations est déterminant ; il se traduit par des phases d'attente des tâches et des temps de réponse allongés. Un hôte peut fonctionner de manière fluide avec une utilisation de 95 % tant que le cache est facilement récupérable, mais il peut se bloquer fortement en cas de faible charge de travail si des pages anonymes actives doivent être évincées.

Comprendre et mesurer le PSI

Le système PSI (Pressure Stall Information) permet de visualiser la pression sur la mémoire, car je mesure non pas l'occupation, mais les temps de latence. Dans /proc/pressure/memory Je vois „ some “ et „ full “ : „ some “ décrit les moments où au moins une tâche attend de la mémoire, tandis que „ full “ indique les phases où toutes les tâches sont bloquées. Exemple : „ some avg10=4,67 “ signifie qu’au cours des 10 dernières secondes, 4,67 % du temps ont été consacrés à des blocages dus à des goulots d’étranglement mémoire ; „ full avg10=0,30 “ indique de rares blocages complets. Je corrèle très tôt les valeurs „ some “ en hausse avec les temps de réponse et je procède à une mise à l’échelle, à un réglage ou à une réduction de la charge avant que des OOM sévères n’apparaissent. Cette approche m’évite de me laisser tromper par une mémoire RAM apparemment „ très libre “, car les pages libres sans accès rapide Reclaim- Ils ne tirent guère parti de cette possibilité.

Grandeur de mesure valeur indicative Symptôme Action
Mémoire PSI (moyenne sur 10) > 2–3 % en continu Les délais de réponse s'allongent Vérifier la marge de mémoire vive (RAM), ajuster avec précision les limites des cgroups
Mémoire PSI pleine (avg10) > 0,1 % perceptible Temps d'arrêt courts Identifier la cause, mettre fin au « thrashing »
MemAvailable < 10 % de la mémoire vive Faible marge de sécurité Alléger la charge du cache/de la charge de travail, planifier la capacité
vmstat si/so permanent > 0 Pression d'échange Swappiness/Ajuster le swap, protéger le Hotset

Symptômes dans les environnements d'hébergement

Sur les serveurs très sollicités, je constate d'abord des pics de latence, alors que la charge CPU semble rester modérée ; le noyau est pris dans des boucles de récupération, les E/S s'accumulent et les requêtes sont en attente. La charge moyenne grimpe, bien que les cœurs semblent disponibles, car de nombreuses tâches sont bloquées par la mémoire ou les E/S ; c'est un signe caractéristique d'une augmentation de Impressions. Des valeurs si/so persistantes dans vmstat indiquent que le système utilise activement l'espace d'échange, ce qui ralentit les handshakes TLS, les contenus dynamiques et les chemins de requête. Si rien n’est fait, le système bascule en « thrashing » : le processeur passe la majeure partie de son temps à gérer la pagination et l’échange de mémoire, au lieu d’effectuer des tâches utiles. À ce stade, l’OOM-Killer intervient et termine les processus ayant un score élevé ; une approche ciblée Analyse de l'OOM Killer m'aide à repérer les schémas récurrents et les erreurs de configuration.

Implications pour les charges de travail d'hébergement et les cgroups

Dans les environnements partagés, il suffit de quelques applications gourmandes en mémoire pour augmenter les latences pour de nombreux clients ; les cgroups atténuent cet effet, mais ils ne peuvent pas remédier à un dimensionnement inadéquat Instances. Dans les instances VPS et cloud, une mémoire vive insuffisante ou une mauvaise stratégie d'échange entraîne plus rapidement des pics de charge ; l'isolation protège les autres services, mais pas le vôtre. Les bases de données reposent sur de grands pools de tampons ; lorsque Reclaim les évince ou que l’espace d’échange intervient, les temps de requête augmentent et le débit diminue considérablement. Les systèmes d’orchestration de conteneurs utilisent les paramètres `memory.low`, `memory.high` et `memory.max` pour protéger les services essentiels, limiter les blocages et, en cas d’urgence, les arrêter de manière ciblée. Je choisis donc délibérément ces limites et surveille le PSI par service afin de prendre des mesures correctives à temps et de conserver des réserves pour les services critiques Charges de travail à garder dégagé.

Stratégie de suivi et indicateurs

Je consulte les valeurs MemAvailable, Buffers et Cached pour évaluer la quantité de mémoire récupérable à court terme ; les valeurs MemFree seules peuvent facilement induire en erreur. En parallèle, je consulte vmstat : des valeurs si/so persistantes indiquent une pression sur l'espace d'échange, ce qui stimule considérablement les E/S et fait grimper les latences ; pour plus d'informations sur Utilisation du swap J'utilise des modèles de diagnostic éprouvés. PSI m'apporte l'élément manquant, car „ some “ et „ full “ quantifient les retards réels ; je déclenche des alertes lorsque les seuils sont atteints et je distingue les pics de charge des goulots d'étranglement chroniques. Les séries chronologiques issues de `sar` ou de la pile d’observabilité mettent en évidence des schémas et m’aident à valider les résultats de l’optimisation. `dmesg` révèle les événements OOM qui indiquent des limites strictes ou des erreurs de configuration ; je construis ainsi une image cohérente à partir de la perspective du noyau, du comportement des E/S et Application.

Charges de travail typiques soumises à des contraintes

Les serveurs web tels que Nginx ou Apache fournissent le contenu plus lentement lorsque Reclaim et Swap s'exécutent en arrière-plan ; les connexions Keep-Alive restent ouvertes plus longtemps, ce qui aggrave les files d'attente. Les piles PHP et Python occupent de la mémoire vive (RAM) en raison des caches des frameworks, des composants JIT et des données de session ; en cas de remplacement, ces données font la navette entre la mémoire vive et le stockage, ce qui allonge considérablement les temps de réponse. Les bases de données perdent en vitesse dès que les pools de tampons diminuent ou que certaines parties se retrouvent sur l'espace d'échange ; même une faible latence supplémentaire par E/S finit par s'accumuler lorsque les opérations sont nombreuses Requêtes. Les services de mise en cache tels que Redis ou Memcached ont besoin d'accès en RAM ; si des zones de clés se retrouvent dans la mémoire swap, l'avantage disparaît et le risque d'être arrêté en cas de surcharge augmente. Dans tous les cas, les métriques PSI et swap fournissent les indications les plus claires montrant que la mémoire est devenue un goulot d'étranglement, et non pas la CPU.

Optimisation du système et paramètres du noyau

Je commence par vm.swappiness : un réglage modérément réduit empêche un recours excessif à la mémoire swap sans pour autant bloquer les opérations de récupération nécessaires ; je mesure systématiquement les effets à l'aide de PSI. Ensuite, j’optimise vm.dirty_ratio et les limites associées afin de ne pas déclencher de longues vagues de vidage tout en évitant de provoquer des pics d’écriture mineurs ; ces deux paramètres ont des effets perceptibles Conséquences sur les latences. Avec les Cgroups v2, je définis `memory.low` pour les services critiques, `memory.high` pour la limitation en cas de surcharge et `memory.max` comme limite stricte avec des OOM contrôlables. Je porte une attention particulière aux topologies NUMA : une pression locale peut apparaître alors qu’il reste encore de la RAM disponible au niveau global ; le verrouillage des processus et de la mémoire permet d’éviter ce genre de pièges. Enfin, je vérifie le comportement du cache de page ; les évictions inutiles font baisser les taux de réussite et entraînent une perte de temps immédiate pour les charges de travail Web et de bases de données, ce qui Optimisation du cache de pages fournit des informations utiles.

Analyse approfondie des chemins de récupération

Afin de choisir les mesures les plus adaptées, je fais la distinction entre kswapd et Récupération directe. kswapd fonctionne de manière asynchrone lorsque les seuils sont franchis à la baisse ; il est relativement doux tant qu’il existe suffisamment de mémoire cache facilement récupérable. Direct Reclaim intervient de manière synchrone dans les contextes d’exécution lorsque les threads ont un besoin urgent de pages – c’est là que surviennent les ralentissements perceptibles par les utilisateurs. J’observe si la récupération touche davantage le cache de fichiers ou les pages anonymes : si le noyau évacue principalement le cache de fichiers, les échecs de cache augmentent ; s’il évacue la mémoire anonyme (par exemple le tas), on risque des arrêts brutaux et une activité d’échange. Les mécanismes modernes de « working set » tiennent compte des distances de « refault » afin de conserver plus longtemps les pages utiles ; si je constate malgré tout de nombreux « refaults » répétés, je sais que les « hotsets » sont plus volumineux que l’espace disponible marge sont devenus.

Je tiens également compte du compactage et de la défragmentation : kcompactd tente de créer des zones contiguës, par exemple pour les allocations importantes ou THP. Si la compression prend du retard, je constate une augmentation de l'utilisation du processeur par kcompactd, une hausse des latences et une augmentation de la proportion de PSI „ full “ lors des pics de charge. Dans de tels cas, il est souvent plus judicieux de réduire la charge ou d’ajuster les politiques THP plutôt que de se contenter d’augmenter l’espace d’échange.

Les stratégies de swap en détail

Le swap n'est pas un ennemi, mais un outil – qui, s'il est mal utilisé, peut toutefois aggraver les problèmes de latence. Je fais la distinction suivante :

  • Pas de swap: À l'abri des retards de swap, mais risqué en cas de pics – les OOM surviennent plus tôt, Reclaim ne dispose d'aucune marge de sécurité.
  • Swap modéré sur un SSD rapide : idéal pour déporter les pages anonymes peu utilisées ; protège les ensembles actifs en RAM si les paramètres de swappiness et les limites cgroup sont correctement définis.
  • zswap/zram: La compression allège la charge d'E/S ; elle convient aux hôtes ayant peu d'E/S ou sert de tampon contre les pics de charge momentanés. Je vérifie la charge CPU et le taux de compression afin d'éviter que le système ne soit ralenti par le CPU.

Je ne règle pas systématiquement la valeur de swappiness à un niveau bas ; pour les charges de travail impliquant un cache de fichiers important, il est judicieux d'opter pour une valeur de swappiness légèrement plus élevée afin d'évacuer les pages anonymes « froides » et de maintenir la stabilité du cache de fichiers. Je protège les services critiques (par exemple les bases de données) à l'aide de `memory.low` et, si nécessaire, en verrouillant leurs hotsets en RAM, afin que l'espace d'échange ne touche pas les éléments indésirables. Il est essentiel que vmstat si/so et que le PSI baisse de manière constante lorsque j'ajuste la stratégie ; sinon, j'apporte des corrections.

THP, compactage et fragmentation

Transparent Huge Pages (THP) Elles permettent d'économiser des accès TLB et facilitent le fonctionnement des applications gourmandes en ressources CPU et en mémoire. Sous charge, elles génèrent toutefois un travail de compactage ; le paramètre „ always “ peut alors entraîner d'importants blocages. J'utilise „ madvise “ de manière ciblée pour les charges de travail qui en tirent profit (par exemple, certains moteurs en mémoire), et pour les piles Web sensibles à la latence, je préfère désactiver THP de manière ciblée ou ne l'activer que via madvise. En complément, j'observe vm.compaction_proactiveness et vérifie si le compactage proactif repousse la formation de la crasse ou s'il la réduit réellement. Si les pages THP sont souvent découpées ou si le compactage s'accélère, cela indique une quantité insuffisante de marge ou des modèles d'allocation inadaptés dans l'application.

Pièges NUMA et pression locale

Sur les hôtes NUMA, la „ mémoire vive libre “ globale est trompeuse : un socket peut être sollicité tandis qu’un autre reste inutilisé. Je vérifie les statistiques NUMA et j’ancrage les processus localement (liaison CPU/mémoire) afin que les ensembles actifs restent proches de la charge de calcul. Une récupération directe sur un nœud malgré des réserves globales signale des déséquilibres NUMA ; dans ce cas, des allocations entrelacées pour les services largement dispersés ou une liaison stricte pour les charges de travail monolithiques s’avèrent utiles. Le PSI par cgroup, combiné aux statistiques NUMA, me permet de déterminer si un nœud unique est à l’origine des files d’attente.

Mesures liées à l'application

J'analyse les profils de mémoire à l'aide de ps, top, htop et d'outils de profilage afin d'identifier les véritables « gouffres » de mémoire et les fuites ; ce faisant, j'observe l'évolution des hotsets au fil du temps. Je choisis délibérément la taille des caches d'application : s'ils sont trop volumineux, cela génère une pression ; s'ils sont trop petits, cela nuit aux performances ; j’effectue mes ajustements en me basant sur les indicateurs PSI et les temps de réponse, et non sur mon intuition. Lorsque des signaux de pression sont détectés, l’application peut libérer volontairement des caches moins critiques ou des données temporaires ; je réduis ainsi les blocages sans toucher aux limites globales. J’ajuste les paramètres de démarrage et le réglage du GC (par exemple pour les JVM) de manière à ce que les ensembles de travail restent bien dans la RAM ; j’atténue les schémas d’allocation agressifs par le traitement par lots. Je garde également un œil sur les artefacts de compilation et les symboles de débogage, car les résidus négligés coûtent cher sans qu’on s’en rende compte Mémoire et augmentent le risque de décrochage ultérieur.

Subtilités de Cgroups v2 et stratégies OOM

Avec Cgroups v2, je sépare clairement la protection, la limitation et les limites strictes : memory.low réserve de la marge pour les services critiques ; le reste est réaffecté aux groupes moins importants. memory.high en cas de dépassement, il limite les ressources de manière ciblée et oblige les applications à libérer de la mémoire avant que le système n'en pâtisse. memory.max C'est la dernière ligne de défense : si elle est franchie, cela signifie un OOM dans un cadre contrôlé. Je règle PSI par cgroup, afin que les alertes se déclenchent là où les blocages surviennent ; le PSI global reste stable tandis qu’un service isolé s’effondre – c’est précisément ce schéma que je souhaite détecter. En combinaison avec les priorités OOM, je définis des règles de sacrifice claires : les workers de traitement par lots non essentiels sont les premiers à être arrêtés, tandis que les API centrales conservent leur marge.

Virtualisation : « ballooning », KSM et « overcommit »

Dans les environnements virtualisés, je suis confronté à une double contrainte : l'invité détecte apparemment de la mémoire vive libre, tandis que l'hyperviseur, via Ballooning prive. Ce jeu augmente les coûts de récupération des deux côtés. Je mesure le PSI sur l'invité et je le recoupe avec les métriques de l'hyperviseur ; si le PSI augmente lors d'événements de « ballooning », la VM a besoin de plus de capacité garantie ou de meilleures politiques de Cgroup sur l'hôte. KSM permet d'économiser de la mémoire vive grâce à la déduplication des pages identiques, mais sollicite le processeur ; dans les environnements d'hébergement comportant de nombreuses machines virtuelles similaires, cela peut s'avérer intéressant, à condition que la charge supplémentaire sur le processeur ne compromette pas les SLO. Je ne prévois de recourir à l’overcommit (par exemple, l’allocation agressive d’un grand nombre de petites machines virtuelles) qu’avec des réserves SLO fixes et une gestion stricte memory.low pour les systèmes où la latence est un facteur critique.

Choix architecturaux en matière d'hébergement

Je mise sur la répartition horizontale afin que les instances individuelles subissent moins de pics de charge ; les pools évolutifs atténuent les pics et maintiennent les latences à un niveau plus bas. Je sépare clairement les rôles : les bases de données, les applications et la mise en cache disposent de leurs propres pools de ressources, afin que les processus de récupération ne génèrent pas d’effets secondaires inattendus au-delà des limites du système. Je choisis le stockage en tenant compte de la latence d’écriture, car les vidages de pages sales ont un impact direct sur les temps de réponse ; un chemin d’accès rapide réduit sensiblement les temps de récupération. Dans les clusters, je prévois des réserves de RAM par nœud et j’utilise des politiques de planification pour que la charge et la consommation de mémoire restent réparties de manière plus homogène. J’automatise la mise à l’échelle à l’aide de seuils PSI, afin que l’augmentation des valeurs „ some “ déclenche des actions avant qu’il ne y ait des freinages brusques et Tuer- des événements.

Planification des capacités et modèles de marge de manœuvre

Je définis la marge de sécurité de manière quantifiable : je prévois des réserves suffisantes pour que le „ some “ PSI reste en dessous de seuils définis lors des pics normaux et que le „ full “ ne se produise pratiquement jamais. Pour cela, j’utilise des centiles (par exemple, le 99e centile de la charge horaire) et je prévois 10 à 30 % de RAM supplémentaire en fonction de la volatilité de la charge de travail. Les bases de données bénéficient de réserves fixes plus importantes, tandis que les interfaces Web s’adaptent de manière plus dynamique. Je calibre les temps de rebond : à quelle vitesse le PSI et le si/so redescendent-ils après un pic ? S’ils restent élevés, c’est le signe que les réserves sont insuffisantes ou que la stratégie de swap/dirty est inadaptée. La planification des capacités devient ainsi un processus continu plutôt qu’une estimation annuelle.

Alerte et réglage piloté par SLO

Je relie PSI aux SLO des utilisateurs : si „ some avg10 “ augmente en même temps que les latences de l'API, j'interviens. Je classe les alertes en deux niveaux : „ jaune “ (2 à 3 % „ some “ persistants, „ full “ proche de 0) et „ rouge “ (plus de 5 % „ some “ ou „ full “ > 0,1 %). Les alertes basées sur les cgroups aident à isoler la minorité bruyante. De plus, je mets en place des alertes en cas d’augmentation des files d’attente « dirty » et des temps d’attente d’écriture, afin de lisser les vagues « dirty » à temps. L’objectif est que les mesures de réglage (swappiness, memory.high, tailles de cache) soient observables et réversibles ; je déploie les modifications progressivement et je compare les résultats « avant/après » à l’aide des mêmes métriques.

Diagnostic étape par étape au quotidien

Je commence par vérifier `free -h` et `MemAvailable` : si la valeur baisse nettement, je recherche les caches qui peuvent être libérés de manière judicieuse, ainsi que les services dont les hotsets augmentent. Ensuite, je lance `vmstat` à intervalles courts pour identifier les tendances „ si/so “ ; un swapping persistant confirme la pression et m’oriente vers la piste des E/S. Je consulte ensuite /proc/pressure/memory et j’analyse les valeurs „ some “ et « full » sur 10, 60 et 300 secondes ; je relie directement les moyennes en hausse aux latences observées. dmesg m’indique des traces d’OOM et révèle quels processus ont récemment déclenché ou subi des crises de mémoire ; j’en déduis les limites et les priorités pour les Cgroups. À partir de tout cela, je formule une hypothèse, je mets en œuvre de petites mesures d’optimisation, je vérifie à l’aide de PSI et je conserve la Temps de réponse en vue.

Guides d'intervention et chaînes de causes typiques

Il y a certains schémas que je retrouve sans cesse :

  • Les tâches de sauvegarde ou d'analyse évincent le cache de pages: Les taux de réussite du cache chutent soudainement, le Web et la base de données ralentissent. Mesure à prendre : limiter les tâches (priorité d'E/S), décaler les créneaux horaires, définir « memory.high » pour le cgroup des tâches, protéger le budget du cache de pages des services critiques.
  • Fuites dans les processus « worker »: Augmentation progressive de la mémoire anonyme, le PSI „ some “ grimpe au fil des heures. Mesures à prendre : identifier la fuite, mettre en place des politiques de redémarrage automatique et de recyclage, définir des limites de mémoire afin que les fuites ne mettent pas en danger l'ensemble de l'hôte.
  • Décrochages dus au THP: la charge de kcompactd augmente lors des pics de trafic. Mesure à prendre : régler THP sur „ madvise “, adapter les services concernés, vérifier les paramètres de compactage, augmenter la marge de manœuvre.
  • Impression locale NUMA: Un socket est saturé alors que de la mémoire RAM globale est disponible. Mesures à prendre : corriger les affinités, activer l'interleave pour les charges de travail très dispersées, ajuster la répartition de la charge dans le planificateur.
  • Fichier d'échange sur des disques lents: si/so augmente, les temps de réponse explosent. Mesure : déplacer le swap vers un stockage plus rapide, évaluer zswap/zram, affiner les paramètres de swappiness et les politiques cgroup.

Chaque runbook se termine par une validation : les valeurs „ some/full “ apparaissent-elles et les latences se stabilisent-elles ? Si ce n'est pas le cas, l'hypothèse était erronée ou incomplète – je continue alors à itérer.

Outils et traçabilité en production

Outre les outils classiques, je m'appuie sur une analyse plus approfondie : j'observe les rapports entre le cache de page et les données anonymes, les taux de défauts de page, les schémas de réapparition des défauts et les files d'attente de réécriture. Les approches eBPF et de traçage m’indiquent avec précision où surviennent les temps d’attente – par exemple le long des chemins de récupération, lors du writeback ou lors de l’allocation de grands blocs. Je privilégie une instrumentation légère et adaptée à la production : des fenêtres d’activation courtes, un échantillonnage plutôt qu’un enregistrement en continu, et une corrélation claire avec les métriques de l’application. Cela me permet d’identifier les causes avant de modifier les paramètres à grande échelle.

Points clés et prochaines étapes

La « pression mémoire » désigne le temps perdu en raison d'une pénurie de mémoire, et non simplement de la RAM occupée ; je la mesure à l'aide de PSI, j'identifie les tendances à un stade précoce et j'agis en m'appuyant sur les données. En combinant les informations fournies par MemAvailable, vmstat si/so, PSI et dmesg, on identifie les véritables causes des pics de latence et du thrashing. Grâce au réglage des paramètres Swappiness, Dirty et Cgroup, je réduis de manière ciblée les blocages et garantis aux services essentiels leur marge. Au niveau de l'architecture, la répartition horizontale, la répartition claire des rôles et les chemins d'accès rapides au stockage atténuent les conséquences de chaque pic de charge. En fin de compte, ce qui compte, c'est que je relie en permanence le diagnostic et les mesures correctives : mesurer, ajuster, mesurer à nouveau – jusqu'à ce que les performances et Stabilité à nouveau convenir.

Derniers articles

Rack de serveurs équipé de systèmes Linux et affichant l'utilisation de la mémoire
Serveurs et machines virtuelles

Comprendre l'OOM Killer : quand Linux met fin à des processus

Découvrez comment fonctionne l'OOM Killer sous Linux en cas de manque de mémoire, comment il met fin aux processus, et comment, en tant qu'administrateur dans des environnements d'hébergement, vous pouvez éviter les problèmes de mémoire insuffisante grâce au mot-clé « oom killer linux ».