Le 19 mai 2026, un de mes serveurs sature sa mémoire. Pas de partition d’échange, le noyau commence à tuer des processus, et les poignées de main TLS expirent. Incident classique, correctif classique : quatre gigaoctets d’échange, et une tâche planifiée qui surveille la mémoire et m’écrit si elle dépasse un seuil. J’ajoute dans la foulée une seconde tâche qui surveille l’expiration de mes certificats.
Le 27 juin, mon fournisseur de messagerie rejette le mot de passe du compte d’envoi. Les deux tâches continuent de s’exécuter à l’heure, de détecter correctement, et d’échouer silencieusement à l’envoi. Je ne reçois plus rien. Je n’en conclus rien, parce qu’il n’y avait rien à conclure : ne rien recevoir, c’était déjà l’état normal.
Je l’apprends le 25 septembre, en déployant un site sans rapport, parce qu’un formulaire de contact refuse d’envoyer un courriel. Quatre-vingt-dix jours. L’alerte que j’avais construite en réaction à un incident avait vécu trente-neuf jours avant de mourir sans bruit.
Et le détail que je trouve le plus inconfortable : parmi les tâches muettes figurait la surveillance d’expiration de mes certificats. Ils sont dix-neuf, émis individuellement, et ils arrivaient à échéance pendant cette fenêtre précise. L’alerte conçue pour ce risque était hors service au moment exact du risque.
Le défaut tient en une ligne
Voici la forme réduite de ce que faisait ma tâche. Elle détecte correctement. C’est la dernière instruction qui pose problème.
USED=$(free | awk '/Mem:/ {printf "%.0f", $3/$2*100}')
[ "$USED" -lt 90 ] && exit 0
curl --ssl-reqd smtps://smtp.exemple.ca:465 \
--user "$MAIL_USER:$MAIL_PASS" \
--mail-from "$MAIL_FROM" --mail-rcpt "$ALERT_TO" \
--upload-file /tmp/alerte.txt
# Le code de retour de curl n'est jamais lu.
# L'envoi echoue, le script sort 0, cron ne signale rien.Le serveur de messagerie refuse l’authentification, curl sort en erreur, et personne ne lit ce code de retour. La tâche se termine en succès apparent. Du point de vue du planificateur, tout s’est bien passé cinq minutes durant, quatre-vingt-dix jours de suite.
Une classe de défaillance, pas un accident
Il serait confortable de ranger ça parmi les étourderies, et de conclure qu’il suffisait de tester le code de retour. Ce serait passer à côté. Supposons que je l’aie testé :
if ! curl --fail --silent ... ; then
echo "$(date -Is) envoi d'alerte en echec" >> /var/log/alertes.log
fi
# Mieux : la panne laisse une trace.
# Mais la trace est ecrite SUR la machine en difficulte,
# et personne ne va la lire tant que rien ne l'annonce.On a déplacé le problème sans le résoudre. La preuve de la panne existe désormais, mais elle reste à l’intérieur du périmètre, et sa lecture dépend d’un signalement qui ne viendra pas. C’est le même défaut, d’un cran plus profond.
L’ingénierie de la fiabilité a un nom pour cette famille. Une défaillance de mode commun est une cause unique qui met en échec plusieurs composants que l’on croyait indépendants. Le concept vient des industries où la redondance est vitale : on y a appris qu’aligner trois systèmes de secours ne sert à rien s’ils partagent la même alimentation.
Mes deux tâches étaient indépendantes l’une de l’autre. Elles surveillaient des objets différents, selon des logiques différentes, à des rythmes différents. Mais elles empruntaient le même canal de sortie, et ce canal appartenait au périmètre qu’elles étaient censées surveiller. Une panne unique les a donc neutralisées toutes les deux, simultanément, sans produire le moindre signal.
Quatre défauts, un seul symptôme
| Défaut | Conséquence |
|---|---|
| Le canal d’alerte partage une dépendance avec le système surveillé | Une panne unique supprime la détection et le signalement |
| Le canal d’alerte n’est surveillé par rien | Sa mort ne produit aucun événement |
| L’envoi est une poussée sans accusé de réception | L’émetteur ignore s’il a été entendu |
| La santé est déduite de l’absence de plainte | Le silence est interprété comme une confirmation |
Les trois premiers sont des défauts d’architecture et ils se corrigent. Le quatrième est un défaut de raisonnement, et c’est le plus coûteux : il transforme chacun des trois autres en angle mort durable.
L’inversion
Presque toute surveillance repose sur une assertion positive de panne : quelque chose casse, le système me prévient. Ce modèle porte une faille native — il exige que le composant défaillant soit encore assez sain pour signaler sa propre défaillance. On demande au malade de composer lui-même le numéro des secours.
La réponse connue est une assertion négative de santé. Le système n’annonce plus ses pannes, il annonce sa vie. Ce qui déclenche l’alarme n’est plus l’arrivée d’un message, c’est son absence — et l’observateur qui constate cette absence se tient hors du périmètre surveillé.
Le silence d’un dispositif de surveillance n’est pas une bonne nouvelle. C’est une absence d’information, et la plupart des architectures la traitent comme une confirmation de santé.
L’interrupteur d’homme mort
Ce renversement est outillé depuis longtemps. Prometheus, l’un des systèmes de surveillance les plus répandus, embarque une règle dont la condition est toujours vraie :
groups:
- name: meta
rules:
- alert: Watchdog
expr: vector(1) # toujours vraie
annotations:
summary: "Preuve de vie de la chaine d'alerte."Cette alerte n’a aucune valeur prise isolément : elle est routée vers un service de battement externe, qui attend de la recevoir à intervalle régulier. Tant qu’elle arrive, la chaîne vit. Si elle s’interrompt, c’est le service externe qui prévient, par un autre chemin.
Le routage réserve un piège qui mérite d’être cité, parce qu’il montre que la pratique a été éprouvée par des gens qui s’y sont brûlés :
route:
group_interval: 5m # <- c'est cette valeur qui s'applique
routes:
- matchers: [ alertname = "Watchdog" ]
receiver: battement-externe
repeat_interval: 1m # ignore : Alertmanager retient la plus grandeUn seuil de détection calibré sur la minute annoncée se déclencherait donc à tort, toutes les cinq minutes. Le dispositif censé détecter les silences se mettrait à crier faux — et on finirait par l’ignorer, ce qui ramène exactement au point de départ.
Quatre parades, et ce qu’elles coûtent
| Parade | Ce qu’elle règle | Ce qu’elle coûte |
|---|---|---|
| Second canal indépendant | Le mode commun | Un abonnement ; reste une poussée non confirmée |
| Interrupteur d’homme mort externe | La détection du silence | Une dépendance et une frontière de confiance de plus |
| Battement auto-hébergé ailleurs | Le silence, sans tiers | Une seconde machine, à surveiller à son tour |
| Message de vie périodique attendu | Le cas réel, à peu près | Rien, sinon une habitude humaine |
La version la plus simple tient sur une ligne de planificateur. Le signal de vie n’est émis que si la tâche s’est réellement bien terminée, et il part vers une adresse qui n’a rien à voir avec la machine surveillée.
*/5 * * * * /opt/scripts/ram-watch.sh \
&& curl -fsS https://battement.exemple.ca/ping/<jeton>
# Le && est le coeur du dispositif : un echec de la tache
# supprime le battement, et c'est l'absence qui alerte.La dernière ligne du tableau mérite qu’on s’y arrête, parce qu’elle est la moins élégante et la plus efficace à court terme. Un message envoyé chaque lundi, qu’on s’attend à recevoir, transforme une personne en détecteur de silence. C’est fragile, ça ne passe pas à l’échelle, et ça aurait réduit mes quatre-vingt-dix jours à sept.
Les deux parades du milieu sont les bonnes réponses durables, et elles s’opposent sur un seul point : accepter ou non une dépendance externe. Un service tiers vous sort du périmètre — c’est précisément ce qu’on cherche — mais il ajoute un fournisseur à votre chaîne critique. Un battement auto-hébergé vous garde maître de tout, au prix d’une seconde machine qui devient à son tour un composant à surveiller.
La règle
De cet incident je retiens une seule contrainte, et elle se vérifie sur un schéma, sans écrire une ligne de code : toute chaîne de surveillance doit se terminer hors du système qu’elle surveille. Chez un tiers, sur une autre machine, ou dans une attente humaine — mais dehors.
Si elle boucle sur elle-même, son silence devient indistinguable de sa santé. Et une information qu’on ne peut pas distinguer de son contraire n’est pas une information.