feat(monitoring) : les métriques des conteneurs remontent par un relais #30

Merged
thystips merged 1 commit from feat/container-metrics-relay into main 2026-08-10 22:46:50 +02:00
Owner

Jumeau du relais de journaux (PR #26), pour les métriques. Déployé et mesuré le 10/08/2026 sur les quatre nœuds et leurs huit conteneurs — voir en bas. Il précède le démontage des coffres des nœuds : dans l'autre sens il existerait une fenêtre pendant laquelle un magasin de certificats tourne sans témoin.

Le problème, et l'option écartée

Le coffre est descendu en conteneur. Or openbao-metrics tourne sur le nœud et lit des fichiers locaux : certificat de listener, âge du token de stockage, instantanés du raft.

L'option bon marché — sonder les quatre coffres depuis le nœud, en DN42 — récupérait cinq métriques sur huit. Elle perdait l'âge des instantanés et l'âge du token, c'est-à-dire exactement les deux pannes muettes pour lesquelles ce collecteur avait été écrit :

« L'unité peut tourner sans renouveler quoi que ce soit — c'est même son mode de panne le plus probable. »

Écartée pour cette raison.

Ce que le relais achète

Les mêmes deux choses que celui des journaux : le mot de passe de l'ingest ne descend pas dans les conteneurs, et aucune destination du plan privé n'est ouverte — le pont étant NATé, une telle règle aurait valu pour tous les conteneurs du nœud, vers un service qui accepte des écritures.

Le découpage suit ce que chaque machine peut VOIR

Métrique Où elle est mesurée maintenant
coffre, certificat de listener, instantanés du raft conteneur openbao
token de stockage, l'agent qui l'écrit conteneur ingress, où Caddy le lit

Pas un découpage administratif : c'est le propriétaire du fichier qui décide. Les chemins étant identiques à l'intérieur d'un conteneur, les scripts n'ont pas eu à apprendre où ils tournent — ils ont cessé de chercher ce qui n'est plus chez eux. Les métriques gardent leurs noms ; seule instance change.

⚠ Un conteneur ne pousse QUE ses collecteurs textfile

set_collectors et non enable_collectors : le second ajoute aux défauts de node_exporter — cpu, mémoire, disque. Les prendre aurait fait remonter par conteneur une mesure que le nœud fait déjà, et à moitié fausse : un conteneur voit le CPU de son hôte.

Quatre défauts trouvés en déroulant, dont trois muets

Aucun ne se voyait au lint. Ils sont dans le second commit.

1. Le chemin du récepteur/api/v1/metrics/write, celui d'Alloy, et non /api/v1/write, celui de Prometheus. Le mauvais rend 404 et remote_write réessaie sans fin sans rien journaliser en niveau info : le conteneur avait l'air de pousser, le relais d'écouter, et aucune série n'arrivait. Trouvé en interrogeant l'endpoint, pas en lisant les journaux — qui ne disaient rien.

2. instance ne se pose pas dans external_labels. Un remote_write ne les applique qu'aux étiquettes absentes, et prometheus.scrape a déjà posé instance : le nom d'hôte. Sur un nœud ça ne se voyait pas — son nom d'hôte est son nom de nœud. Dans un conteneur c'est le nom du service, identique sur les quatre machines : les quatre coffres se seraient écrasés sous une seule série. Corrigé par un discovery.relabel, seul endroit où l'étiquette peut l'être.

3. Deux écouteurs gRPC sur le wildcard, que personne n'avait demandés :

LISTEN  *:43875   alloy    ← loki.source.api, présent depuis la PR #26
LISTEN  *:41783   alloy    ← prometheus.receive_http, ajouté par celle-ci

Les composants d'écoute d'Alloy reposent sur un serveur interne qui ouvre toujours un gRPC à côté de l'HTTP, sur * et un port éphémère si on ne dit rien. C'est ce que ce dépôt refuse partout ailleurs : tcp_l3mdev_accept vaut 1 pour Bird, donc toute socket wildcard est joignable depuis tout l'overlay. La politique drop les couvrait — mais compter sur le pare-feu pour rattraper une écoute qu'on ne voulait pas est l'inverse de l'invariant que looking_glass et openbao défendent chacun de leur côté. Épinglés sur la boucle locale, à ports fixes.

4. Le handler des minuteries bouclait sur la liste des nœuds : dans un conteneur il tentait de redémarrer des minuteries jamais installées. Celui-là échouait franchement.

Un contrôle trouvé en chemin

alloy validate accepte un répertoire et vérifie les références croisées entre fichiers — 30-container-relay nomme des récepteurs définis dans 00-base, ce qu'aucune validation fichier par fichier ne verrait.

Vérifié qu'il vérifie plutôt que supposé : un composant inventé le fait échouer. C'est la leçon de bao operator diagnose, qui rend 4 sur une configuration valide comme sur du HCL cassé. Sans lui, une erreur ne se manifestait qu'au redémarrage d'Alloy — par une télémétrie qui s'arrête en silence, c'est-à-dire la panne que la supervision devait dire.

Déploiement et vérification (10/08/2026)

Les quatre nœuds puis leurs huit conteneurs, nœud par nœud.

Contrôle Résultat
Récepteurs 10.42.0.1:3500 et :3501, jamais le wildcard
Écouteurs gRPC 127.0.0.1:3502 et :3503 — plus aucun wildcard dans ss -ltnp
Pare-feu ligne 19c, tcp dport { 3500, 3501 } sur incusbr0
Séries dans Mimir les 4 coffres et les 4 proxys, instance = <service>-<nœud>
Journaux inchangés, aucune erreur Alloy sur les 12 machines après redémarrage

Exemple mesuré : dn42_openbao_sealed{instance="openbao-fr-bod1"} 0, dn42_openbao_storage_token_age_seconds{instance="ingress-fr-rbx2"} 208.

Les règles Mimir devront suivre (dépôt ATNET/k8s-flux) : le groupe dn42-openbao matche des instance qui étaient des noms de nœuds. Rien ne casse pendant l'intervalle — le collecteur des nœuds tourne encore, les coffres y sont toujours — mais les deux jeux de séries coexistent jusqu'au démontage, qui est le bon moment pour les faire suivre.

Aucun changement de topology/ : pas de synchronisation NetBox à faire.

Jumeau du relais de journaux (PR #26), pour les métriques. **Déployé et mesuré le 10/08/2026** sur les quatre nœuds et leurs huit conteneurs — voir en bas. Il **précède** le démontage des coffres des nœuds : dans l'autre sens il existerait une fenêtre pendant laquelle un magasin de certificats tourne sans témoin. ## Le problème, et l'option écartée Le coffre est descendu en conteneur. Or `openbao-metrics` tourne sur le nœud et lit des **fichiers locaux** : certificat de listener, âge du token de stockage, instantanés du raft. L'option bon marché — sonder les quatre coffres depuis le nœud, en DN42 — récupérait cinq métriques sur huit. Elle perdait l'**âge des instantanés** et l'**âge du token**, c'est-à-dire exactement les deux pannes muettes pour lesquelles ce collecteur avait été écrit : > « L'unité peut tourner sans renouveler quoi que ce soit — c'est même son mode de panne le plus probable. » Écartée pour cette raison. ## Ce que le relais achète Les mêmes deux choses que celui des journaux : **le mot de passe de l'ingest ne descend pas** dans les conteneurs, et **aucune destination du plan privé n'est ouverte** — le pont étant NATé, une telle règle aurait valu pour tous les conteneurs du nœud, vers un service qui accepte des écritures. ## Le découpage suit ce que chaque machine peut VOIR | Métrique | Où elle est mesurée maintenant | |---|---| | coffre, certificat de listener, instantanés du raft | conteneur `openbao` | | token de stockage, l'agent qui l'écrit | conteneur `ingress`, où Caddy le lit | Pas un découpage administratif : c'est le propriétaire du fichier qui décide. Les chemins étant **identiques à l'intérieur d'un conteneur**, les scripts n'ont pas eu à apprendre où ils tournent — ils ont cessé de chercher ce qui n'est plus chez eux. Les métriques **gardent leurs noms** ; seule `instance` change. ## ⚠ Un conteneur ne pousse QUE ses collecteurs textfile `set_collectors` et non `enable_collectors` : le second **ajoute** aux défauts de node_exporter — cpu, mémoire, disque. Les prendre aurait fait remonter par conteneur une mesure que le nœud fait déjà, et à moitié fausse : un conteneur voit le CPU de son hôte. ## Quatre défauts trouvés en déroulant, dont trois muets Aucun ne se voyait au lint. Ils sont dans le second commit. **1. Le chemin du récepteur** — `/api/v1/metrics/write`, celui d'Alloy, et non `/api/v1/write`, celui de Prometheus. Le mauvais rend 404 et `remote_write` réessaie sans fin **sans rien journaliser** en niveau info : le conteneur avait l'air de pousser, le relais d'écouter, et aucune série n'arrivait. Trouvé en interrogeant l'endpoint, pas en lisant les journaux — qui ne disaient rien. **2. `instance` ne se pose pas dans `external_labels`.** Un `remote_write` ne les applique qu'aux étiquettes **absentes**, et `prometheus.scrape` a déjà posé `instance` : le **nom d'hôte**. Sur un nœud ça ne se voyait pas — son nom d'hôte *est* son nom de nœud. Dans un conteneur c'est le nom du service, **identique sur les quatre machines** : les quatre coffres se seraient écrasés sous une seule série. Corrigé par un `discovery.relabel`, seul endroit où l'étiquette peut l'être. **3. Deux écouteurs gRPC sur le wildcard**, que personne n'avait demandés : ``` LISTEN *:43875 alloy ← loki.source.api, présent depuis la PR #26 LISTEN *:41783 alloy ← prometheus.receive_http, ajouté par celle-ci ``` Les composants d'écoute d'Alloy reposent sur un serveur interne qui ouvre **toujours** un gRPC à côté de l'HTTP, sur `*` et un port éphémère si on ne dit rien. C'est ce que ce dépôt refuse partout ailleurs : `tcp_l3mdev_accept` vaut 1 pour Bird, donc toute socket wildcard est joignable depuis **tout l'overlay**. La politique `drop` les couvrait — mais compter sur le pare-feu pour rattraper une écoute qu'on ne voulait pas est l'inverse de l'invariant que `looking_glass` et `openbao` défendent chacun de leur côté. Épinglés sur la boucle locale, à ports fixes. **4. Le handler des minuteries** bouclait sur la liste des nœuds : dans un conteneur il tentait de redémarrer des minuteries jamais installées. Celui-là échouait franchement. ## Un contrôle trouvé en chemin `alloy validate` accepte un **répertoire** et vérifie les références croisées entre fichiers — `30-container-relay` nomme des récepteurs définis dans `00-base`, ce qu'aucune validation fichier par fichier ne verrait. **Vérifié qu'il vérifie** plutôt que supposé : un composant inventé le fait échouer. C'est la leçon de `bao operator diagnose`, qui rend 4 sur une configuration valide comme sur du HCL cassé. Sans lui, une erreur ne se manifestait qu'au redémarrage d'Alloy — par une télémétrie qui s'arrête en silence, c'est-à-dire la panne que la supervision devait dire. ## Déploiement et vérification (10/08/2026) Les quatre nœuds puis leurs huit conteneurs, nœud par nœud. | Contrôle | Résultat | |---|---| | Récepteurs | `10.42.0.1:3500` et `:3501`, **jamais le wildcard** | | Écouteurs gRPC | `127.0.0.1:3502` et `:3503` — plus **aucun** wildcard dans `ss -ltnp` | | Pare-feu | ligne 19c, `tcp dport { 3500, 3501 }` sur `incusbr0` | | Séries dans Mimir | les **4** coffres et les **4** proxys, `instance` = `<service>-<nœud>` | | Journaux | inchangés, aucune erreur Alloy sur les 12 machines après redémarrage | Exemple mesuré : `dn42_openbao_sealed{instance="openbao-fr-bod1"} 0`, `dn42_openbao_storage_token_age_seconds{instance="ingress-fr-rbx2"} 208`. ⚠ **Les règles Mimir devront suivre** (dépôt `ATNET/k8s-flux`) : le groupe `dn42-openbao` matche des `instance` qui étaient des noms de nœuds. Rien ne casse pendant l'intervalle — le collecteur des nœuds tourne encore, les coffres y sont toujours — mais les deux jeux de séries coexistent jusqu'au démontage, qui est le bon moment pour les faire suivre. Aucun changement de `topology/` : pas de synchronisation NetBox à faire.
Jumeau de celui des journaux, et pour les mêmes deux raisons : le mot de passe
de l'ingest ne descend pas dans les conteneurs, et aucune destination du plan
privé n'est ouverte — le pont étant NATé, elle aurait valu pour TOUS les
conteneurs du nœud vers un service qui accepte des écritures.

CE QUI L'A RENDU NÉCESSAIRE. Le coffre est descendu en conteneur, et
`openbao-metrics` tournait sur le nœud en lisant des FICHIERS LOCAUX :
certificat de listener, âge du token de stockage, instantanés du raft. Sonder
les coffres depuis le nœud en DN42 aurait récupéré cinq métriques sur huit et
perdu l'âge des instantanés et celui du token — c'est-à-dire exactement les deux
pannes muettes pour lesquelles ce collecteur avait été écrit. Écarté pour ça.

L'ORDRE COMPTE : ce commit précède le démontage des coffres des nœuds. Dans
l'autre sens il aurait existé une fenêtre pendant laquelle un magasin de
certificats tourne sans témoin.

Le collecteur se scinde en suivant ce que chaque machine peut VOIR, pas un
découpage administratif : coffre, certificat et instantanés dans le conteneur
`openbao` ; token de stockage et l'agent qui l'écrit dans celui d'ingress, où
Caddy le lit. Les chemins étant identiques à l'intérieur d'un conteneur, les
scripts n'ont pas eu à apprendre où ils tournent — ils ont cessé de chercher ce
qui n'est plus chez eux. Les métriques gardent leurs noms, seule `instance`
change et elle dit maintenant la vérité.

⚠ UN CONTENEUR NE POUSSE QUE SES COLLECTEURS TEXTFILE. `set_collectors` et non
`enable_collectors` : le second ajoute aux défauts de node_exporter — cpu,
mémoire, disque — que le nœud mesure déjà pour lui, et qu'un conteneur voit de
toute façon faussement, ceux de son hôte. Ce qui monte est ce que le conteneur
est SEUL à pouvoir voir.

Deux ports parce qu'Alloy expose deux composants, l'API de poussée Loki et le
remote-write Prometheus n'ayant pas le même protocole. Une seule décision, une
plomberie double — d'où un seul fichier de configuration et une seule ligne de
matrice, la 19c, qui gagne un port.

Les `external_labels` du conteneur sont posées PAR LE CONTENEUR : un
`remote_write` ne les applique qu'à ce qu'il collecte, jamais à ce qu'on lui
relaie. Le relais aurait sinon étiqueté ces séries du nom du NŒUD, et les deux
se seraient confondues exactement comme les journaux l'auraient fait.

Enfin, un contrôle trouvé en cherchant autre chose : `alloy validate` accepte un
RÉPERTOIRE et vérifie les références croisées entre fichiers, qu'aucune
validation fichier par fichier ne verrait. Vérifié qu'il vérifie — un composant
inventé le fait échouer — parce qu'un contrôle qui accepte tout ne contrôle
rien. Sans lui, une erreur ne se manifestait qu'au redémarrage d'Alloy, par une
télémétrie qui s'arrête en silence : la panne que la supervision devait dire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGwvXLVXJ2PrRFwJTB3Xp9
thystips deleted branch feat/container-metrics-relay 2026-08-10 22:46:50 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ThysTips_dn42/infra-dn42!30
No description provided.