Set-OPS-Public/docs/supervision-conception.md
Daniel Allaire 311f33d6f6 grafana : les panneaux declares deviennent des tableaux
La moitie qui manquait. Les roles declaraient leurs panneaux, Prometheus en
derivait ses cibles, les expressions repondaient, et rien ne les assemblait.

serveur_grafana lit les memes meta/metriques.yml que serveur_prometheus et en
assemble un tableau par role — la raison du panneau devient sa description dans
Grafana. Moisson des tableaux orphelins, prefixe setops-role- pour ne jamais
toucher un tableau ecrit a la main, JSON valide avant d etre pose (Grafana ne
tombe pas sur un tableau illisible : il le saute en silence).

client_metrique declare quatre panneaux sans exportateur — le cas symetrique.
Celui qui compte : la memoire disponible, depuis que le ballon est actif.

Eprouve sur le site : les deux tableaux charges dans le stockage unifie, un
temoin orphelin retire, les quatre panneaux de flotte a 10 series chacun.

Un panneau etait faux — /var/lib/lxcfs rapporte toujours zero octet libre et un
min() en meurt. Corrige en liste blanche : un montage inconnu manque au graphe,
ce qui se voit, au lieu de l ecraser.

P77 garde la table des unites : une unite inventee retombe sur short, lisible et
fausse. 76 OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-14 16:32:30 -04:00

251 lines
12 KiB
Markdown

# Supervision dérivée des rôles
> **Pour qui :** celui qui ajoute un rôle à Set-OPS et se demande comment sa supervision
> arrive dans Icinga — et celui qui exploite et veut savoir d'où sortent les services
> qu'il voit.
> **La règle en une phrase.** Un rôle déclare les sondes de sa propre supervision ; le
> moteur en dérive les objets Icinga, la permission d'API et les preuves. Comme
> `meta/flux.yml` engendre nftables *et* OPNsense.
## Pourquoi
Le 2026-09-09, mesure du dépôt sur lui-même :
```
au 2026-09-09 :
39 rôles déclarent leurs flux -> nftables + OPNsense, dérivés
32 déclarent leur empreinte -> ressources des VM, dérivées
32 déclarent leur authentification -> habilitations, dérivées
19 groupes déclarent `surveillance:` -> RIEN
```
Au 2026-09-09, les dix-neuf lignes `surveillance:` de `docs/dependances-groupes.yml` sont écrites,
versionnées, relues — et **aucune n'est exécutée**. Icinga surveillait deux choses :
`sauvegarde` et `sante`.
C'est la classe d'échec que ce dépôt nomme partout ailleurs, en version documentaire : la
carte dit ce qui est surveillé, et personne ne surveille. *Une intention écrite n'est pas
une mesure.*
## Le contrat d'une sonde : c'est celui des greffons Nagios
Une sonde est un **script local**, déposé par le rôle qui la possède, dans
`/usr/local/lib/setops/sondes/<nom>.sh` (0750, root).
| | |
|---|---|
| **sortie standard** | `TEXTE lisible` puis, optionnellement, `\| métriques` |
| **code de sortie** | `0` OK · `1` AVERTISSEMENT · `2` CRITIQUE · `3` INCONNU |
| **réseau** | aucun besoin : c'est le porteur qui pousse le résultat |
| **durée** | courte ; le porteur passe toutes les 15 min |
> **Ce contrat n'est pas de nous.** C'est l'**API des greffons Nagios**, que
> `monitoring-plugins` implémente depuis vingt ans et qu'Icinga parle nativement. La
> première rédaction de ce document la décrivait sans la nommer — autrement dit la
> réinventait. `positionnement.md` dit l'inverse : *adopter aux seuils, ne pas
> réimplémenter*.
**La conséquence pratique est grande.** Un greffon standard **est** une sonde valide, sans
la moindre colle : `check_disk`, `check_load`, `check_procs`, `check_ntp_time`,
`check_file_age`, `check_smtp`… Une sonde ne s'écrit à la main que lorsque la vérité à
mesurer est propre à Set-OPS — et c'était le cas pour `client_pki/certificat`, qui compare
l'empreinte *servie* à celle du disque : aucun greffon ne sait ça.
### Ce que la flotte a vraiment sous la main — et ce qu'elle n'a pas
`client_sante` installe **`monitoring-plugins-basic`** : **53** greffons, mesurés le
2026-09-14 sur la flotte. Ce document a d'abord annoncé « 54 » et cité `check_pgsql` en
exemple. **`check_pgsql` n'en fait pas partie**, ni `check_dns`, ni `check_ldap` : ils sont
dans `monitoring-plugins-standard`.
Et ce paquet-là ne s'ajoute pas :
apt-get install -s monitoring-plugins-standard
→ samba, python3-samba, smbclient, snmp, tdb-tools…
Une pile Samba et une pile SNMP sur **chaque machine** de la flotte, pour obtenir trois
greffons. Le durcissement dit le contraire de ça.
**La conséquence pour qui écrit une sonde :** vérifier que le greffon appelé est dans
`-basic` avant de s'appuyer dessus. **Un greffon absent sort en 127** — qui n'est pas un
code Nagios (0/1/2/3) : la sonde devient illisible au lieu d'échouer proprement. Quand le
greffon manque, prendre l'outil natif du service (`dig` sur un résolveur, `psql` sur une
base) ; c'est déjà ce que fait `serveur_resolveur/resolution`.
Écrire du shell là où un greffon existe, c'est se donner du code à maintenir *et* se priver
de vingt ans de cas limites déjà rencontrés par d'autres.
Le rôle qui possède la sonde la **déploie lui-même** : il connaît ses chemins, ses
secrets, sa vérité de terrain. Il la **déclare** dans `meta/supervision.yml`, et c'est
cette déclaration que le moteur lit.
```yaml
# roles/<rôle>/meta/supervision.yml
sondes:
- nom: certificat
ttl: 5400
raison: "..."
```
## Passif, et à durée de vie — jamais actif
Un contrôle **actif** ne voit pas la machine **muette** : si elle ne répond plus, la sonde
échoue et on met ça sur le compte du réseau. Ici c'est le nœud qui parle, et le `ttl` de
son envoi fait la fraîcheur — sans nouvelle, Icinga périme le service tout seul.
**Le silence alerte autant que l'échec.** C'est exactement ce qui a manqué à
`openipmi.service` : une unité en échec à chaque démarrage pendant une semaine, et
`systemctl --failed` à zéro partout parce qu'aucune machine n'avait redémarré.
C'est aussi ce qu'impose *la vérification suit la clé* : la sonde tourne là où vit la
vérité, pas sur le superviseur. Un dépôt de sauvegarde chiffré côté client ne peut être
jugé que par qui détient la clé.
## Ce qui n'a rien à faire ici
Une **métrique à seuil** — durée de collecte, volume de journaux, taux d'occupation —
appartient à Prometheus et Grafana. Icinga répond à une seule question : *est-ce cassé ?*
Mélanger les deux rendrait les deux moins lisibles.
## L'autre moitié : `meta/metriques.yml`
Cette phrase appelle son pendant, et il porte le même patron — **le rôle déclare, le
moteur dérive**.
| fichier | ce qu'il produit | la question |
| --- | --- | --- |
| `meta/supervision.yml` | une **sonde** → un verdict, avec un TTL | *est-ce cassé ?* |
| `meta/metriques.yml` | un **exportateur** → une série ; des **panneaux** → un tableau | *depuis quand, et vers où ?* |
**Deux fichiers, deux consommateurs.** `serveur_prometheus` lit l'`exportateur` pour en
tirer sa cible de scrutation ; `serveur_grafana` lit les `panneaux` pour en assembler un
tableau par rôle. Les deux moitiés sont indépendantes : un rôle peut déclarer un
exportateur sans panneau (des séries collectées, regardées ailleurs), et des panneaux sans
exportateur (`client_metrique` — le job `node` est universel, écrit une fois, et le
dériver le ferait exister deux fois).
**Le flux n'est pas dérivé d'ici, et c'est voulu.** Le port de l'exportateur doit s'ouvrir
depuis l'observatoire, et c'est `meta/flux.yml` qui le déclare — là où vivent déjà tous les
flux du rôle. Deux fichiers pour un même fait finiraient par diverger.
### Le critère d'un panneau
*Une série a sa place si elle **précède** un verdict, ou si elle n'en aura **jamais**.*
Ce qui bascule d'un coup appartient à Icinga ; ce qui dérive lentement n'a que le graphe
pour se faire voir. Une base qui passe de 20 à 60 connexions en trois semaines n'a rien
cassé — elle annonce la date où elle cassera.
### La `raison` devient la description du panneau
C'est le seul champ qui ne produit aucun pixel de graphe, et c'est le plus important.
Grafana l'affiche au survol du titre. Sans elle, celui qui regarde six mois plus tard voit
une courbe sans savoir ce qu'elle annonce.
### Agréger, toujours
node_exporter publie une série par interface, par point de montage, par cœur : **339
séries** pour le seul trafic réseau du site, mesuré le 2026-09-14. Un panneau qui les
montre toutes ne montre rien. Et se méfier de `min()` / `max()` : un seul montage
pathologique — `/var/lib/lxcfs`, qui rapporte toujours zéro — suffit à rendre un panneau
faux **pour toujours**, avec une courbe plate parfaitement lisible. Filtrer par **liste
blanche** : un montage inconnu manque au graphe, ce qui se voit, au lieu de l'écraser, ce
qui ne se voit pas.
### Ce que les gardes tiennent
**P77** exige de chaque panneau un titre, une expression, une unité et une raison — et que
l'unité figure dans la table de traduction de `serveur_grafana`. Une unité inventée ne
casse rien : le panneau retombe sur `short`, et un graphe d'octets gradué en unités brutes
reste parfaitement lisible et parfaitement faux.
Ce que P77 **ne** dit pas : si l'expression répond. Aucune lecture statique ne le dira. Un
panneau se prouve comme une sonde — en le regardant rendre des données, et en le notant au
CHANGELOG.
## Chaque sonde vient avec son contrôle négatif
Une sonde qu'on n'a jamais vue échouer n'est pas une sonde, c'est une habitude. Dix-neuf
voyants verts non éprouvés seraient un recul par rapport à deux voyants prouvés : ils
rassureraient.
Toute sonde ajoutée doit donc être accompagnée de la manière de la faire échouer
**pour de vrai**, et cette manière doit être rejouée au moins une fois, sur une machine
réelle. Le CHANGELOG en porte la trace.
**Et contre un état sain, tout autant.** La première version de la sonde du certificat a
rendu **14 machines sur 14 en CRITIQUE** — sur une PKI qui se portait très bien. Elle
utilisait `openssl verify -CAfile racine`, alors que nos certificats sont signés par un
*intermédiaire* que seul `step certificate verify` sait retrouver. Une alarme toujours
allumée ne vaut pas mieux qu'une alarme jamais allumée : elle apprend à ne plus regarder.
Une sonde se prouve donc **deux fois** — verte sur le sain, rouge sur le cassé.
## Combien de sondes : une par CAUSE D'ACTION
Ni une par rôle, ni une par vérification. **Une par geste que le verdict appelle.**
Les quatorze premières sondes agrègent chacune de deux à six chemins d'échec, et ce choix
avait été fait **sans être écrit** — d'où cette section, ajoutée le 2026-09-10 après la
question : *« est-ce parce qu'elles agrègent plusieurs vérifications ? »*
**Ce que coûte une agrégation abusive**, dans le modèle d'Icinga : un service = **un état,
un historique, un acquittement**. Fondre deux causes qui appellent des gestes différents,
c'est ne plus pouvoir acquitter l'une pendant que l'autre alerte — et lire comme *un
service instable* ce qui est en réalité *deux faits distincts qui alternent*.
**Ce que coûte l'excès inverse** : un voyant de plus est un voyant de moins regardé. Le
dépôt le répète assez — *une supervision creuse est pire qu'aucune*.
Le test pratique, à appliquer sans état d'âme :
> Les deux échecs appellent-ils **le même geste, de la même personne, dans le même délai** ?
> Si oui, une seule sonde, et le message nomme lequel des deux a parlé.
> Si non, deux sondes.
**Exemple d'agrégation légitime** — `moteur` : `icinga2` mort, `icingadb` mort, état figé en
base. Trois causes, un seul geste : *va regarder la supervision*. Le message dit laquelle.
**Exemple d'agrégation abusive** — `cache-apt` : « ne répond pas » arrête tout `apt` de
l'écosystème, « volume à 90 % » est un billet pour demain. Même personne, gestes et
**délais** différents : deux sondes.
## Une sonde doit pouvoir être mise en défaut *par paramètre*
Corollaire des deux règles précédentes, et c'est ce qui rend la discipline tenable à
l'échelle : une sonde dont on ne peut prouver le rouge qu'en **cassant un service** ne sera
prouvée qu'une fois, puis plus jamais.
Chaque sonde expose donc sa cible et ses seuils en variables du rôle. On la met en défaut
en lui donnant un port fermé, un nom qui n'existe pas, un seuil impossible — sur une
machine réelle, sans rien abîmer, et aussi souvent qu'on veut.
## Où vit la sonde : là où vit la vérité, pas là où vit le symptôme
*« La vérification suit la clé. »* Un nœud sait qu'il a **lancé** sa sauvegarde ; seul le
dépôt sait qu'elle a **abouti**. Un nœud sait qu'il **expose** ses métriques ; seul
Prometheus sait qu'il les **collecte**.
D'où un choix qui peut surprendre : *« ce nœud est-il bien collecté ? »* est une sonde de
**`serveur_prometheus`**, pas de `client_metrique`. Une seule sonde y voit les N nœuds à la
fois — et surtout, elle voit le cas **silencieux** : celui qui a cessé d'être collecté et
qui, par définition, ne peut pas s'en plaindre lui-même.
## Un contrôle négatif ne doit pas pouvoir abîmer
Éprouver la sonde du certificat, le 2026-09-09, a consisté à **remplacer le certificat
d'hôte** par un auto-signé. La sonde a bien viré au rouge — et le script de synchronisation
a propagé ce certificat vers `node_exporter`, dont la clé était restée l'ancienne :
```
failed to load X509KeyPair: tls: private key does not match public key
```
Un service réel est tombé pour éprouver une sonde. Le contrôle avait un **rayon d'action**
que je ne lui avais pas donné volontairement.
La règle qui en découle : un contrôle négatif se fait sur une **copie**, ou sur un chemin
que la sonde accepte en paramètre — jamais en substituant l'artefact que d'autres
consomment. Quand c'est impossible, on le fait sur la machine la moins critique, on
l'annonce, et on vérifie l'état des consommateurs **après**, pas seulement celui de la
sonde.