Le nœud mort qui n'avait rien à dire
Dimanche matin, un message qui fait toujours le même effet : une LED rouge sur un des nœuds. Le reste du cluster tourne, la box va bien, et un Raspberry Pi 5 sur trois ne répond plus à rien — ni ping, ni SSH.
La bonne nouvelle est arrivée avant l’enquête : le travail de haute disponibilité a payé. Quorum Swarm conservé à 2 sur 3, les huit bases Patroni ont basculé leur rôle de leader sur leur second membre, Loki s’est redéployé tout seul sur un autre nœud, et l’authentification unique n’a pas cillé. Ce qui est tombé, c’est ce qui devait tomber : les services épinglés au nœud par leurs volumes locaux. Un cluster qui perd un tiers de son matériel et continue de servir ses pages, c’est exactement le contrat.
Reste la vraie question, celle qui ne se résout pas en redémarrant : pourquoi ?
Un arrêt sans témoin
J’ai d’abord cherché la trajectoire habituelle : une fuite mémoire, une montée en charge, un disque qui sature, une surchauffe. Les métriques jusqu’au dernier relevé, à 8 h 30 :
mémoire disponible 9 Go sur 16, stable sur 3 heures
charge (5 min) 0,5 — 1,3
température aucune montée
Un calme plat, puis plus rien. Pas de spirale : une falaise. Ce profil élimine d’un coup l’OOM, la surchauffe et la dérive logicielle — toutes laissent une pente avant la chute.
Le disque, lui, a parlé. SMART du NVMe après redémarrage :
unsafe_shutdowns 6
critical_warning 0
media_errors 0
température 31 °C
Le disque est en parfaite santé — ce n’est pas lui qui est mort. Mais il a enregistré l’arrêt comme unsafe : il a perdu son alimentation sans avoir pu vider ses caches. Donc la carte s’est arrêtée net, elle n’a pas fait de halte propre. Et il a fallu débrancher puis rebrancher pour la réveiller : elle est restée en veille, alimentée mais morte.
À ce stade, deux hypothèses survivent, et elles sont indiscernables : l’alimentation a lâché (bloc, câble, sous-tension sous les pics d’un HAT NVMe) ou le SoC s’est bloqué. Les deux produisent exactement cette signature.
Il existe un seul élément qui les sépare : la dernière phrase du noyau. Sur un Raspberry Pi,
c’est littéralement Under-voltage detected!.
Le post-mortem impossible
$ sudo journalctl --list-boots
IDX BOOT ID FIRST ENTRY LAST ENTRY
0 d77d3f28e9b5… Sun 2026-10-11 11:08:59 Sun 2026-10-11 11:11:31
Un seul démarrage connu : celui d’après. Tout ce que la machine avait à dire sur sa propre mort avait été effacé par son redémarrage.
Le plus humiliant, c’est que le dossier existait :
$ sudo du -sh /var/log/journal
4.0K /var/log/journal
/var/log/journal présent, créé en avril… et vide. Sur les trois nœuds. Six mois à construire
des métriques, des logs centralisés, des traces distribuées, des alertes qui poussent sur le
téléphone — et le journal système, lui, vivait en mémoire vive et s’évaporait à chaque
redémarrage. J’avais une observabilité magnifique pour tout ce qui tourne, et aucune pour ce qui
s’arrête.
Verdict rendu à l’utilisateur de mon propre homelab, c’est-à-dire à moi-même : je ne peux pas conclure, et la raison pour laquelle je ne peux pas conclure est elle-même le problème.
Le correctif qui ne fonctionnait pas, et ne le disait pas
Rendre le journal persistant est censé tenir en trois lignes :
# /etc/systemd/journald.conf.d/10-zebbox-persistent.conf
[Journal]
Storage=persistent
SystemMaxUse=100M
Appliqué, journald redémarré, journalctl --flush demandé. Et… /var/log/journal toujours à 4 Ko.
Aucune erreur. Aucun avertissement. Le fichier était là, bien formé, lisible.
$ sudo systemd-analyze cat-config systemd/journald.conf
…
# /etc/systemd/journald.conf.d/10-zebbox-persistent.conf
[Journal]
Storage=persistent
…
# /usr/lib/systemd/journald.conf.d/40-rpi-volatile-storage.conf
[Journal]
Storage=volatile
Voilà. Raspberry Pi OS impose Storage=volatile par son propre fragment de configuration. Et
journald fusionne ces fragments par ordre lexical, toutes directories confondues : /etc ne
l’emporte sur /usr/lib que pour un fichier de même nom. Mon 10- arrivait donc avant le
40- du système, et se faisait écraser par lui.
Renommé en 99-zebbox-persistent.conf :
$ sudo du -sh /var/log/journal
67M /var/log/journal # rpi4
21M # rpi51
83M # rpi52
La leçon de cet épisode n’est pas « connais ton ordre lexical ». C’est : une correction qui ne peut pas échouer bruyamment doit être vérifiée sur son résultat, jamais sur sa configuration. Mon script de déploiement ne relit plus le fichier qu’il vient d’écrire ; il mesure la taille du dossier de journal et crie si elle n’a pas bougé.
Transformer une ligne de log en série temporelle
Le journal persistant résout le prochain post-mortem. Mais il y a mieux à faire pour la sous-tension, parce que le micrologiciel du Pi ne la raconte pas seulement au noyau : il la garde dans un registre, avec des bits collants.
$ vcgencmd get_throttled
throttled=0x0
Le masque a deux moitiés, et c’est tout l’intérêt :
| bits | signification |
|---|---|
| 0–3 | sous-tension / bridage / throttling maintenant |
| 16–19 | les mêmes, survenus depuis le démarrage — effacés seulement au reboot |
La moitié haute est celle qui compte. Une chute de tension à 3 h du matin, personne ne la voit passer — mais son bit, lui, est encore levé à 9 h. Une question qui demandait un log demande maintenant une requête.
Un minuteur systemd écrit ces bits dans le collecteur textfile de node-exporter, et trois alertes en découlent : sous-tension maintenant (critique), sous-tension depuis le démarrage (avertissement, et elle reste affichée), et — la plus importante — métriques périmées.
Cette troisième mérite un mot. Elle surveille la fraîcheur de la mesure elle-même : si le minuteur meurt, l’alerte le dit, au lieu de laisser deux belles alertes sombrer dans un silence qu’on prendrait pour une bonne nouvelle. C’est exactement la panne qui a rendu ce post-mortem impossible, et je n’avais aucune envie de la reconstruire en plus joli.
Un détail d’implémentation qui change tout à l’usage : les expressions font une jointure sur
node_uname_info. Sans elle, l’alerte nomme une adresse IP d’overlay qui change à chaque
redémarrage de conteneur ; avec elle, elle nomme le Pi. Une alerte qui ne dit pas où regarder
coûte cinq minutes à chaque déclenchement.
La leçon
Mon observabilité était construite pour des choses qui tournent : des métriques, des traces, des journaux d’applications vivantes. Une machine qui s’arrête, elle, ne produit qu’une seule donnée utile — sa dernière phrase — et c’était précisément celle que je jetais à chaque redémarrage. Surveiller ce qui fonctionne est l’exercice facile ; ce qu’il faut préserver, c’est le témoignage de ce qui échoue. Et quand on y remédie, on vérifie le résultat et pas la configuration : la panne la plus coûteuse à chasser est celle qui répond « c’est bon ».
Le NVMe de rpi51 compte toujours ses six arrêts sales, et je n’ai pas la cause de celui de ce matin. Mais le prochain se racontera tout seul — et s’il s’agit bien de l’alimentation, je le saurai avant que la LED ne devienne rouge.
Le homelab est public, journal persistant et bits collants compris.
📬 La newsletter homelab
Les nouveaux articles + retours d'expérience self-hosting, sans spam. Désinscription en un clic.