fix(monitoring) : trois défauts trouvés en déroulant le relais de métriques #32

Merged
thystips merged 1 commit from fix/container-metrics-relay-followups into main 2026-08-11 01:30:53 +02:00
Owner

Ces corrections tournent déjà sur les douze machines. La PR #30 a été fusionnée alors qu'elle ne portait que son premier commit ; celui-ci, écrit pendant le déploiement, est resté dehors. main décrivait donc un état que l'infrastructure n'avait plus.

Vérifié plutôt qu'affirmé : --check sur fr-rbx1 et ses deux conteneurs rend changed=0 avec cette branche, et rendrait des différences avec main seul.

Quatre défauts, dont trois muets

Aucun ne se voyait au lint, et c'est ce qui les rend intéressants.

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 à la main ; les journaux 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 de la machine.

Sur un nœud ça ne se voyait pas — son nom d'hôte est son nom de nœud, la valeur coïncidait. Dans un conteneur, le nom d'hôte est celui du service : openbao, identique sur les quatre machines. Les quatre coffres se seraient écrasés sous une seule série, distingués par la seule étiquette node, et instance aurait cessé d'identifier quoi que ce soit — en ayant l'air juste.

Corrigé par un discovery.relabel, seul endroit où l'étiquette peut l'être. node, container et site restent dans external_labels : celles-là n'existent pas à la collecte.

3. Deux écouteurs gRPC sur le wildcard

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

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 de la chaîne input 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 : rien ne s'y connecte, et un port qui change à chaque démarrage ne se vérifie pas.

4. Le handler des minuteries

Il bouclait encore sur monitoring_textfile_collectors, la liste des nœuds : dans un conteneur il tentait de redémarrer dn42-dns-metrics et dn42-service-metrics, jamais installées. Celui-là échouait franchement, à la fin du play, loin de la tâche qui l'avait notifié.

La leçon dépasse ce handler : les tâches qui posent et celles qui réagissent doivent lire la même liste.

Mesuré après correction

Contrôle Résultat
Écoutes d'Alloy 10.42.0.1:3500 et :3501, gRPC sur 127.0.0.1:3502 et :3503aucun wildcard
Séries dans Mimir les 4 coffres et les 4 proxys, instance = <service>-<nœud>
Journaux inchangés, zéro erreur Alloy sur les 12 machines après redémarrage
Idempotence --check à changed=0
⚠ **Ces corrections tournent déjà sur les douze machines.** La PR #30 a été fusionnée alors qu'elle ne portait que son premier commit ; celui-ci, écrit pendant le déploiement, est resté dehors. `main` décrivait donc un état que l'infrastructure n'avait plus. Vérifié plutôt qu'affirmé : `--check` sur `fr-rbx1` et ses deux conteneurs rend **`changed=0`** avec cette branche, et rendrait des différences avec `main` seul. ## Quatre défauts, dont trois muets Aucun ne se voyait au lint, et c'est ce qui les rend intéressants. ### 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 à la main ; les journaux 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** de la machine. Sur un nœud ça ne se voyait pas — son nom d'hôte *est* son nom de nœud, la valeur coïncidait. Dans un conteneur, le nom d'hôte est celui du service : `openbao`, **identique sur les quatre machines**. Les quatre coffres se seraient écrasés sous une seule série, distingués par la seule étiquette `node`, et `instance` aurait cessé d'identifier quoi que ce soit — en ayant l'air juste. Corrigé par un `discovery.relabel`, seul endroit où l'étiquette peut l'être. `node`, `container` et `site` restent dans `external_labels` : celles-là n'existent pas à la collecte. ### 3. Deux écouteurs gRPC sur le wildcard ``` LISTEN *:43875 alloy ← loki.source.api, présent depuis la PR #26 LISTEN *:41783 alloy ← prometheus.receive_http, ajouté par la #30 ``` 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` de la chaîne `input` 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 : rien ne s'y connecte, et un port qui change à chaque démarrage ne se vérifie pas. ### 4. Le handler des minuteries Il bouclait encore sur `monitoring_textfile_collectors`, la liste des **nœuds** : dans un conteneur il tentait de redémarrer `dn42-dns-metrics` et `dn42-service-metrics`, jamais installées. Celui-là échouait franchement, à la fin du play, loin de la tâche qui l'avait notifié. La leçon dépasse ce handler : les tâches qui **posent** et celles qui **réagissent** doivent lire la même liste. ## Mesuré après correction | Contrôle | Résultat | |---|---| | Écoutes d'Alloy | `10.42.0.1:3500` et `:3501`, gRPC sur `127.0.0.1:3502` et `:3503` — **aucun wildcard** | | Séries dans Mimir | les 4 coffres et les 4 proxys, `instance` = `<service>-<nœud>` | | Journaux | inchangés, **zéro erreur** Alloy sur les 12 machines après redémarrage | | Idempotence | `--check` à `changed=0` |
Aucun des trois ne se voyait au lint, et deux étaient muets à l'exécution — ce
qui est la pire des deux façons de rater.

1. LE CHEMIN DU RÉCEPTEUR. `/api/v1/metrics/write` et non `/api/v1/write` :
   c'est le chemin d'Alloy, pas 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 plutôt qu'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 de la machine. Sur un nœud ça ne se voyait pas :
   son nom d'hôte EST son nom de nœud, donc la valeur coïncidait. Dans un
   conteneur le nom d'hôte est celui du service, identique sur les quatre
   machines : les quatre coffres se seraient écrasés sous une seule série, et
   `instance` aurait cessé d'identifier quoi que ce soit. 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. 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 — mesuré, `LISTEN *:43875` et `*:41783`. Le premier date de la PR #26 et
   était passé inaperçu.

   C'est exactement ce que ce dépôt refuse partout : `tcp_l3mdev_accept` vaut 1
   pour Bird, donc toute socket wildcard est joignable depuis TOUT l'overlay. La
   politique `drop` de la chaîne `input` 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 — un port qui change à chaque
   démarrage ne se vérifie pas.

Et un quatrième, celui-là franc : le handler des minuteries bouclait encore sur
la liste des NŒUDS, donc il tentait dans un conteneur de redémarrer des
minuteries jamais installées. Les tâches qui POSENT et celles qui RÉAGISSENT
doivent lire la même liste ; deux expressions pour un seul ensemble, c'est un
handler qui agit sur ce que le rôle ne gère pas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGwvXLVXJ2PrRFwJTB3Xp9
thystips deleted branch fix/container-metrics-relay-followups 2026-08-11 01:30:53 +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!32
No description provided.