freedzhost.cloud

Un service sature un cœur et se fait tuer : trouver pourquoi en dix minutes

Niveau IntermédiaireTemps estimé 30 minÀ lire avant Homelab 1 : Proxmox, la base sous chaque serviceUn mot inconnu ? Lexique

Un service qui redémarre sans raison visible fait en général quelque chose de coûteux en boucle. Inutile de deviner : un processus Node en marche se profile à chaud, sans reconstruire ni redémarrer, et la réponse tient sur un écran.

Ce que vous obtenez. Une méthode reproductible pour distinguer « la machine est trop petite » de « le programme tourne à vide », un script de profilage d'une vingtaine de lignes, et le second piège croisé en chemin : l'hyperviseur qui reprend discrètement la mémoire que vous veniez d'ajouter.

1. Lire les symptômes avant de toucher à quoi que ce soit

systemctl --user status le-service       # compteur de redémarrages, « oom-kill »
journalctl -k | grep -i "out of memory"  # qui le noyau a choisi de tuer
free -m; cat /proc/swaps                 # le swap est-il déjà plein ?
ps -eo pid,pcpu,rss,args --sort=-rss | head

Notre cas : le service a été tué six fois par le noyau en une matinée, le swap était plein, et le processus serveur occupait un cœur entier avec 1,4 Go résidents. Un voisin gourmand en mémoire n'explique pas un cœur à 100 % : quelque chose calculait.

2. Profiler le processus en marche

Node ouvre son inspecteur à la réception de SIGUSR1. Un court script s'y attache, enregistre dix secondes de CPU et affiche où le temps est passé. Ni interface de débogage, ni redémarrage :

kill -USR1 <pid>
node profile.mjs 10

Le script fait une vingtaine de lignes : lister http://127.0.0.1:9229/json/list, ouvrir la WebSocket indiquée, envoyer Profiler.enable puis Profiler.start, attendre, envoyer Profiler.stop, puis additionner les timeDeltas par fonction. Deux chiffres par fonction : le temps propre (où est vraiment le CPU) et le temps inclusif (quel appelant en est responsable).

Le temps propre seul désigne le pilote de base de données ou le ramasse-miettes, où aboutit tout programme. La colonne inclusive est la bonne : elle montrait 34 % du temps total sous une seule fonction, performRefresh.

3. Remonter l'appelant jusqu'à la planification

Cette fonction relisait et revalidait un catalogue de greffons, puis le réécrivait dans SQLite. Elle aurait dû tourner toutes les deux heures. Le code qui arme le minuteur montrait que le délai se calculait à partir de la dernière tentative stockée en base :

délai = dernièreTentative === null ? 0 : max(0, INTERVALLE - (maintenant - dernièreTentative))

Dans notre configuration, le catalogue n'avait aucune adresse distante : le rafraîchissement sortait tôt et n'enregistrait jamais de tentative. Deux heures après le premier démarrage, maintenant - dernièreTentative dépassait l'intervalle pour toujours, le délai tombait à 0 et le minuteur se réarmait aussitôt, indéfiniment. Un cœur, des écritures constantes, une mémoire qui enfle, puis le tueur de mémoire du noyau.

Le correctif tient en une ligne : sans catalogue distant, attendre un intervalle. Pour arrêter l'hémorragie avant de reconstruire, mettre l'horodatage stocké à maintenant (cela tient un intervalle), puis corriger le code.

4. Le second voleur : le ballooning

Nous avions donné 2 Go de plus à la machine virtuelle, et une heure plus tard elle en montrait de nouveau moins. Cause : le pilote « balloon » de l'hyperviseur laisse l'hôte reprendre de la mémoire aux invités quand lui-même est sous pression, jusqu'au minimum configuré.

qm config <vmid> | grep -E "memory|balloon"
qm set <vmid> --balloon 0     # mémoire fixe ; effectif au prochain démarrage

Pour une VM qui doit garder un service debout, la mémoire fixe est le réglage honnête : un ballon qui rétrécit un invité déjà en train de swapper ne fait que déplacer la panne là où elle est plus dure à voir.

Pièges

swapon --show et plusieurs commandes de stockage n'affichent rien, et non une erreur, sans privilèges ; lisez plutôt /proc/swaps.

Un correctif qui ne vit que dans un build déployé est perdu au build suivant. Mettez-le dans le jeu de patchs qui produit le build, avec un test qui échoue sans lui, puis livrez ce build.

Le profilage coûte quelques pour cent d'un cœur le temps de la mesure. Faites-le sur un service déjà en difficulté, pas en routine.

À lire aussi : Inari, auto-hébergé (l'appli de cette histoire), Proxmox et le glossaire.

Utile ? Dites-nous ce qu'il faut mesurer ensuite.