Je suis tombé sur un truc un peu étrange récemment, sur mes serveurs de production.
Au moment de faire une mise en production, notre programme de déploiement a remonté une erreur inhabituelle. En regardant de plus près, je me suis rendu compte qu’il n’avait pas pu écrire sur le disque les fichiers correspondant aux nouvelles versions des projets. Pourtant, il restait encore énormément de place sur le disque dur (à peine 15% d’espace disque utilisé).
Le soucis venait du fait que tous les inodes du système de fichiers avaient été consommés. Wow, la vache. Avoir bouffé tous les inodes, alors que 85% d’espace disque est encore libre, ça voulait dire qu’un nombre incroyable de minuscules fichiers avait été créé.
Le premier réflexe est de se demander quel bout de code on a bien pu écrire, qui génère cette merde. Et pour analyser l’origine du problème, la première étape est déjà d’essayer de savoir dans quel répertoire ces fichiers ont été créés. Donc on a fait un script pour compter le nombre de fichiers dans chaque répertoire. La fête, quoi.
On a découvert qu’il y avait plus de 16 millions de fichiers dans le répertoire /var/lib/php5. Attendez… qu’est-ce qui peut bien avoir créé autant de fichiers dans ce répertoire ?
Pas besoin de chercher très longtemps pour s’apercevoir qu’il s’agit de l’endroit où PHP écrit les fichiers servant à stocker les sessions utilisateurs.
Mais normalement, les sessions sont effacées au bout d’un certain temps. Tout le monde sait ça. C’est d’autant plus étonnant que notre framework Temma utilise son propre système de sessions (stockées dans Memcache), et que donc nous ne devrions pas créer ces fichiers car les sessions PHP sont contournées.
J’ai commencé par essayer de comprendre pourquoi les fichiers de sessions n’ont pas été effacés après un certain délai. Ce comportement est géré dans le fichier de configuration de PHP, par les directives session.gc_maxlifetime, session.gc_probability et session.gc_divisor. Pour faire simple, une installation standard sous Ubuntu (et, semble-t-il, aussi sous Debian) prévoit que les sessions aient une durée de 24 minutes.
Par contre, il existe deux manières différentes de faire le ménage dans les sessions.
La première est de configurer PHP pour qu’à chaque requête entrante, il se donne une chance de faire le ménage parmi les sessions ouvertes. Cela est calculé avec les paramètres que j’ai cités juste avant. Par exemple, le ménage dans les sessions pourra être fait toutes les mille requêtes (ce qui veut dire qu’une requête sur mille sera ralentie par ce traitement supplémentaire).
La seconde manière est de faire tourner régulièrement un script, en le lançant par crontab, dont le rôle est de faire le ménage pour effacer les fichiers correspondant aux sessions qui sont arrivées à expiration.
Sur Ubuntu (et donc sûrement sur Debian aussi), c’est la seconde solution qui a été choisie. Mais le truc complètement incompréhensible, c’est que le script lancé par crontab est commenté !
Pour être exact, il s’agit du fichier /etc/cron.d/php5. Il ne contient qu’une seule ligne, dont la commande est censée s’exécuter toutes les demi-heures. Mais comme je le disais, cette ligne est commentée. Donc le ménage n’est jamais fait.
Dans notre cas, le truc particulièrement con, c’est que les sessions PHP n’auraient pas dû être utilisées. Comme je l’ai dit plus haut, notre framework possède son propre système de session. Le hic, c’est que lorsque j’ai ouvert ce framework sous licence libre, j’ai ajouté une petite évolution : C’est bien beau de gérer nos sessions dans Memcache, mais cette solution ne peut pas satisfaire tout le monde. Donc il possède un fallback qui l’amène à utiliser les sessions PHP si les sessions n’ont pas explicitement été configurées pour utiliser le cache.
Partant de là, le code qui s’occupe de nos médias (principalement l’affichage des images sur nos sites) a connu un petit bug de configuration, qui activait les sessions. Cela ne sert à rien, on est d’accord ; il n’y a pas besoin de session utilisateur pour servir une image.
Mais comme il n’était pas prévu que les sessions soient activées, elles n’étaient évidemment pas configurées pour être stockées en cache. Donc elles se sont retrouvées à passer par le mécanisme des sessions PHP.
C’est ainsi que nous nous sommes retrouvés à écrire des fichiers sur le disque dur pour chaque visiteur de passage sur nos sites…
Alors si jamais vous vous retrouvez à cours d’inode, commencez par regarder du côté des sessions, on ne sait jamais.