Parfois, quand vous auto-hébergez vos services web ou quand vous utilisez une machine avec très peu de RAM, il arrive que vous arriviez devant des comportements d'applications qui semblent inexplicables. Un cas parmi tant d'autre est celui du service coupé, du processus tué alors qu'on n'a rien fait de spécial pour que ça arrive. Il n'empêche que la plus importante chose pour les applications (surtout en production) est la stabilité et donc qu'il serait bien de pouvoir comprendre ce qu'il se passe, dans ces cas-là.

Il existe de plus en plus de plates-formes d'hébergement qui louent des serveurs virtuels, où vous pouvez exécuter les services que vous voulez. Le nombre de processus ne sera pas limité. Par contre, les ressources y seront. Un des point faible de ces environnements est généralement la mémoire vive (RAM) qui, une fois dépassée, entraîne une instabilité du serveur, voire un plantage total.

Out Of Memory Killer (OOM Killer)

OOM KIller est un ensemble de processus du noyau Linux qui est chargé d'empêcher la surcharge de la mémoire sur le système. Il se déclenche donc quand il n'y a plus aucune mémoire disponible sur le système.

Quand un processus a besoin de mémoire, le système alloue tout d'abord la mémoire physique du système. Une fois que cette mémoire est "pleine", le système tentera de remplir la mémoire swap : un fichier d'échange ou une partition dédiée qui joue le rôle de mémoire d'échange du système. Une fois que cette mémoire est également pleine, il ne reste plus du tout de place pour que le système alloue de la mémoire.

Quand il n'y a plus du tout de ressources mémoires disponibles à allouer, le processus du Out Of Memory Killer se déclenche et essaye de faire un peu de ménage. Ce processus va essayer de deviner quelles sont les applications qui consomment le plus de mémoire, ou qui seraient les plus susceptibles d'être arrêtées via un ensemble d'algorithme, et les tue jusqu'à ce que le système retrouve suffisamment de ressources pour fonctionner.

Système instable

Le problème avec ce système, c'est qu'on ne peut pas deviner à l'avance quelles seront les applications qui seront tuées avant les autres. D'ailleurs, l'OOM Killer visera vraisemblablement plus facilement les applications ayant besoin de beaucoup de mémoire pour fonctionner. Dans le cas d'applications de bureau, à la limite, le problème n'est pas bien grave. Dans le pire des cas, on se retrouvera avec un Firefox ou, pour les développeurs, avec un IDE de programmation, qui se ferme (oui, ces deux softs consomment énormément de mémoire).

Le vrai soucis est plutôt pour les applications métiers ou dans les systèmes critiques, où on n'aura pas forcément envie de voir les applications qui se coupent en plein milieu d'un processus délicat. Pensez par exemple à un robot qui s'arrêterait de travailler en plein milieu de la confection d'une pièce dans une usine ou alors à un robot médecin (pourquoi pas, c'est la mode, il me semble) qui s'arrêterait au beau milieu d'une opération.

Problèmes mémoire

Dans les faits, quand l'OOM Killer est appelé, c'est qu'il y a un problème de mémoire sur la machine. On peut pour cela trouver plusieurs raisons qui sont toutes aussi valables :

  • Un programme qui a été mal codé : il peut arriver qu'un développeur fasse une erreur dans son code et que celui-ci se retrouve à consommer plus de mémoire que ne le devrait le programme puisque, par exemple, il ne libérerait pas la mémoire lorsqu'il n'en a plus besoin.
  • Un programme qui utilisera effectivement beaucoup de mémoire : certains processus ont besoin de beaucoup de mémoire pour fonctionner. Typiquement un IDE de développement utilisera beaucoup de mémoire car, pour que la fonctionnalité d'auto-complétion fonctionne, il devra stocker tout ce qui concerne toutes les bibliothèques qu'on est susceptibles d'utiliser. De façon générale, tout programme qui gère beaucoup de données est amené à utiliser beaucoup de mémoire.
  • Une machine avec de la RAM sous-dimensionnée : lorsque le système ne possède pas suffisamment de mémoire vive pour faire fonctionner les applications.
  • Une machine avec de la SWAP sous-dimensionnée : lorsque le système n'a plus de mémoire RAM, s'il a besoin de plus de mémoire, il utilisera la mémoire SWAP, c'est à dire une mémoire d'échange qui se trouve sur le disque de la machine (que ce soit sur une partition séparée ou dans un fichier d'échange).

Dans tous les cas, il faudra penser à dimensionner la machine (même virtuelle) sur laquelle le processus s'exécutera de façon à ce qu'il fonctionne correctement. Pour savoir la quantité de RAM nécessaires aux processus qui tournent sur le serveur, référez-vous aux manuels ou faites des tests de charge, ce ne sera jamais du temps de perdu.

Quels processus sont impactés

Lorsque la mémoire du système est épuisée à tel point qu'il y a une menace sur la stabilité du système, le processus d'optimisation de la mémoire s'exécutera : il tuera et continuera de tuer des processus jusqu'à ce que suffisamment de mémoire soit libérée pour que le reste du système fonctionne correctement - en tout cas, au moins les processus que le noyau exécutera.

Dans un premier temps, la sélection se fera sur le meilleur processus à tuer. Meilleur parce qu'il libérera le plus de mémoire, mais aussi parce qu'il ne sera pas indispensable à la stabilité du système. L'objectif ici sera de tuer un minimum de processus tout en maximisant la quantité de mémoire libérée. Ainsi, le système sera impacté au minimum.

Pour faciliter le choix des processus qu'il peut tuer, le noyau Linux garde un oom_score pour chacun des processus qu'il lance. Vous pouvez d'ailleurs voir le oom_score d'un processus en tapant la commande :

cat /proc/{pid}/oom_score

Plus la valeur de l'oom_score du processus sera élevé, plus il aura de chance d'être tué par l'OOM Killer.

Précision : calcul du oom_score

Il y a quelque temps maintenant, le calcul du oom_score était un processus plutôt hasardeux. A l'heure actuelle, il repose essentiellement sur la question de savoir quel pourcentage de la mémoire disponible est utilisé par le processus.

Il y a deux types de dépassement de mémoire :

  • Au niveau du système : la mémoire disponible est la somme de toute la RAM et de l'espace d'échange disponible pour le système ;
  • Au niveau d'un cpuset ou d'un groupe de contrôle : la mémoire disponible est la montant total de mémoire alloué à ce processus. Le montant de mémoire disponible est donc calculée par rapport à la politique mise en place dans le système.

Quelque soit le cas, l'utilisation de mémoire d'un processus est la somme de la RAM qu'il occupe et de son utilisation de SWAP.

Une fois qu'on sait quelle est la quantité de mémoire utilisée par un processus, on peut calculer son oom_score sur une référence 1000 :un processus qui utilise tous les octets de la mémoire disponible aura un score de 1000 ; un processus qui n'utilise aucune mémoire aura un score de 0 (il y a très peu de chance, donc). J'ai sciemment simplifié le calcul, mais dans l'ensemble, c'est comme ça que ça marque. Il y a ensuite quelques ajustements qui sont faits dans ce calcul, mais sur de très faibles valeurs (maximum 30).

Empêcher certains processus d'être arrêtés par OOM Killer

Ceci étant dit, il est possible d'adapter cette valeurs aux besoins de notre système. Il existe une variable nommée oom_adj_score qui peut être ajustée et qui rentrera dans le calcul du oom_score. Cette variable, que chaque processus possède et dont la valeur doit être comprise entre -1000 et 1000, est ajoutée au calcul du oom_score : si le oom_score d'un processus donné, calculé d'après la formule précédente, est de 300, par exemple et que la variable oom_adj_score est de -500, l'oom_score réel du processus sera de -200, faisant de lui un processus qui ne sera pas tué tout de suite en cas de problème mémoire.

Pour ajuster cette variable, vous pouvez le faire via /proc, mais il vous faudra savoir le PID de votre processus.

Prenons un exemple. S'il est absolument primordial pour vous que le processus sshd ne soit jamais arrêté (c'est bien pour un serveur lointain, par exemple, ça évite d'être obligé d'éteindre le courant pour avoir à nouveau accès à la machine). Commencez par trouver le PID de votre processus :

ps aux | grep sshd

Vous devriez voir apparaître quelque chose dans ce goût-là :

root    2623  0.0  0.0  49948  1232 ?      Ss   Feb08   0:00 /usr/sbin/sshd

Ce qui veut dire que votre application a un PID de 2623. Toutes les informations de ce processus sont donc situées, dans le système, dans le dossier /proc/2623. Et en effet, quand on liste le contenu de ce dossier, on obtient :

[code][...]
-r--r--r-- 1 root root 0 Feb 11 01:09 cmdline
[...]
lrwxrwxrwx 1 root root 0 Feb 11 01:09 exe -> /usr/sbin/sshd
[...]
-rw-r--r-- 1 root root 0 Feb 12 00:22 oom_adj
-r--r--r-- 1 root root 0 Feb 12 00:22 oom_score
-rw-r--r-- 1 root root 0 Feb 12 00:22 oom_score_adj
[...]
-r--r--r-- 1 root root 0 Feb 11 06:25 status
[...][/code]

C'est la liste des fichiers associés au processus en cours (je n'ai pas tout retranscrit par soucis de lisibilité). Remarquez les 3 fichiers oom qui nous intéressent :

  • oom_adj
  • oom_score
  • oom_score_adj

Si vous voulez connaître leur valeur, faites un :

cat /proc/<PID>/oom_*

Si on veut être sûr que ce processus ne s'arrête pas tant qu'on ne lui a pas demandé de s'arrêter, on pourra mettre la variable oom_score_adj à -1000 :

echo -1000 > /proc/<PID>/oom_score_adj

Ainsi, lors du calcul du oom_score, le processus de calcul soustraira 1000 à la valeur calculée et il y aura donc de très fortes chances que votre processus ne s'éteigne jamais.

Si vous voulez faire l'inverse, c'est à dire donner un score maximum à un process pour être sûr que ce soit lui qui s'arrête avant les autres, la valeur -1000 sera remplacée par 1000 pour qu'à la fin du calcul, le processus ajoute 1000 au score.

Configuration d'OOM Killer

Bien sûr, comme quasiment n'importe quelle fonctionnalité du noyau Linux, on peut configurer à notre convenance la façon dont OOM Killer fonctionnera..

Il y a deux variables que nous pouvons modifier :

  • /proc/sys/vm/overcommit_memory qui peut prendre 3 valeurs :
    • 0 : Comportement par défaut, les réservations de mémoire seront acceptées ou refusées selon le calcul précédemment présenté
    • 1 : Toujours accepter
    • 2 : Accepter les demandes si elles ne dépassent pas la quantité de ressources définies dans le paramètre overcommit_ratio
    • /proc/sys/vm/overcommit_ratio : pourcentage de mémoire physique (RAM) que l'on autorise à allouer au processus en plus de la SWAP. Le calcul de la mémoire effectivement autorisé pour le processus et donc allouée) sera donc de : SWAP + ((RAM*overcommit_ratio)/100)

Désactiver OOM Killer

Quand on y réfléchit, un système ne peut gérer un dépassement de mémoire que de deux façons : tuer certains processus qui prennent trop de place dans la mémoire ou planter purement et simplement. Par soucis de stabilité, par défaut, le noyau Linux préférera couper certains processus que de totalement planter avec un kernel panic. Si toutefois votre préférence va pour le kernel panic, aucun soucis, vous pouvez désactiver complètement l'effet du OOM Killer en modifiant la valeur de la variable du noyau /proc/sys/vm/panic_on_oom :

  • 0, la valeur par défaut, pour que le noyau fasse appel à OOM Killer quand le besoin de mémoire se fera ressentir ;
  • 1 pour que le kernel fasse un kernel panic quand la mémoire sera saturée.

Conclusion

Attention, la stabilité d'un système ne tient souvent pas à grand chose. La gestion de la mémoire du noyau Linux est compliquée et modifier la façon dont le système fonctionne peut entraîner une grande instabilité du système. Chaque machine étant différente, il convient de bien comprendre la façon dont la gestion de la mémoire se fait avant de toucher quoi que ce soit. Garder toujours à l'esprit qu'il vaut mieux dimensionner son système correctement, voire modifier notre système (ajouter de la RAM, de la SWAP…) que de modifier la façon dont le système gérera les dépassement de mémoire.

Cet article est pour information, il relate ce que j'ai compris de la configuration du kernel Linux face au manque de mémoire. Faites-moi savoir si vous voulez plus de précisions ou si j'ai fait des erreurs.

Sitographie