supervision : le tableau de bord des conteneurs ne voyait plus un seul conteneur (#203) #204

Merged
olivier merged 3 commits from gabriel/203-conteneurs-invisibles into develop 2026-09-08 13:57:18 +00:00
Member

Ferme #203.

Ce qui n'allait pas

Le tableau de bord « EnerVision — conteneurs » était vide de bout en bout, et rien ne le signalait : cible cadvisor verte, conteneur healthy, 2 500 séries publiées — mais toutes portant id="/", le cgroup racine. Les requêtes en {name!=""} ne rendaient donc rien, ici comme sur le compteur « conteneurs vivants » du tableau « socle ».

La cause n'est pas dans le dépôt : le démon de ml-stagiaire-02 est passé au containerd image store (Docker 29.1.3, Storage Driver: overlayfs, driver-type: io.containerd.snapshotter.v1). La couche d'écriture d'un conteneur n'est plus sous /var/lib/docker/image/ ; cAdvisor v0.52 ne la trouve pas et écarte chaque conteneur en entier au lieu de publier ce qu'il en sait.

Ce qui a été essayé avant d'en arriver là

Piste Résultat
Monter en version cAdvisor Impossible : v0.52.1 est la dernière publiée
Couper les métriques de disque (--disable_metrics=…,disk,diskIO) Sans effet, les conteneurs restent rejetés
Lire containerd (--containerd-namespace=moby) Les vingt conteneurs remontent, avec CPU, RAM et E/S

Ce que fait cette demande

  • cAdvisor lit containerd au lieu de l'API Docker. Son handler Docker est neutralisé au passage (chemin de socket inexistant), sans quoi il reprend la main sur les mêmes cgroups pour les rejeter.
  • Nouveau service noms-conteneurs (127.0.0.1:9102) : containerd ne connaît pas les noms Docker, name devient un identifiant de 64 caractères. Le service publie container_nom{name="<id>",nom="ev-grafana"} 1, que les panneaux joignent par on(name) group_left(nom). Même parti que relais-forge : bibliothèque standard, monté dans l'image Python officielle.
  • Les sept panneaux par conteneur de conteneurs.json groupent désormais par nom.
  • Le manuel documente la panne, sa cause, et le contrôle qui la révèle en une commande (§ 2, contrôle 2 ter).
  • Le banc tests/ci/test-supervision.sh tient le remède en place : lecture de containerd, présence du service, job de collecte, écoute bornée, compilation du script, et aucun panneau regroupant encore par (name).

Ce qu'il faut savoir avant de fusionner

  • La vérification sur le serveur reste à faire : cette demande n'a rien déployé. Le remède a été éprouvé sur ml-stagiaire-02 avec un cAdvisor de test hors pile, sur un port séparé (supprimé depuis) — la pile de production n'a pas été touchée. Les critères 1 à 3 du ticket se contrôlent au déploiement suivant.
  • noms-conteneurs parle au démon Docker, il en a donc tous les pouvoirs : le :ro sur le socket protège le fichier, pas les commandes qu'on y écrit. Il n'appelle qu'une route, en lecture (GET /containers/json), et n'écoute que sur la boucle locale. À dire tel quel si la question vient au jury.
  • Les panneaux réseau par conteneur resteront presque vides, et c'est attendu : nos piles tournent en réseau hôte et n'ont pas de compteurs réseau propres. Seuls les exécutants de la forge en ont. Leur description le dit maintenant au lecteur, plutôt que de laisser croire à une panne.

Vérification

bash tests/ci/test-supervision.sh      # passe ; échoue si la lecture containerd disparaît

Éprouvé en local : le script rend bien le format d'exposition attendu contre un faux démon, échappe les guillemets d'un nom, ignore un conteneur sans nom, et reste debout si le démon se tait.

Ferme #203. ### Ce qui n'allait pas Le tableau de bord « EnerVision — conteneurs » était vide de bout en bout, et **rien ne le signalait** : cible `cadvisor` verte, conteneur `healthy`, 2 500 séries publiées — mais toutes portant `id="/"`, le cgroup racine. Les requêtes en `{name!=""}` ne rendaient donc rien, ici comme sur le compteur « conteneurs vivants » du tableau « socle ». La cause n'est pas dans le dépôt : le démon de `ml-stagiaire-02` est passé au *containerd image store* (Docker 29.1.3, `Storage Driver: overlayfs`, `driver-type: io.containerd.snapshotter.v1`). La couche d'écriture d'un conteneur n'est plus sous `/var/lib/docker/image/` ; cAdvisor v0.52 ne la trouve pas et **écarte chaque conteneur en entier** au lieu de publier ce qu'il en sait. ### Ce qui a été essayé avant d'en arriver là | Piste | Résultat | |---|---| | Monter en version cAdvisor | Impossible : v0.52.1 est la dernière publiée | | Couper les métriques de disque (`--disable_metrics=…,disk,diskIO`) | Sans effet, les conteneurs restent rejetés | | Lire containerd (`--containerd-namespace=moby`) | Les vingt conteneurs remontent, avec CPU, RAM et E/S | ### Ce que fait cette demande - **cAdvisor lit containerd** au lieu de l'API Docker. Son handler Docker est neutralisé au passage (chemin de socket inexistant), sans quoi il reprend la main sur les mêmes cgroups pour les rejeter. - **Nouveau service `noms-conteneurs`** (`127.0.0.1:9102`) : containerd ne connaît pas les noms Docker, `name` devient un identifiant de 64 caractères. Le service publie `container_nom{name="<id>",nom="ev-grafana"} 1`, que les panneaux joignent par `on(name) group_left(nom)`. Même parti que `relais-forge` : bibliothèque standard, monté dans l'image Python officielle. - **Les sept panneaux par conteneur** de `conteneurs.json` groupent désormais par `nom`. - **Le manuel** documente la panne, sa cause, et le contrôle qui la révèle en une commande (§ 2, contrôle 2 ter). - **Le banc `tests/ci/test-supervision.sh`** tient le remède en place : lecture de containerd, présence du service, job de collecte, écoute bornée, compilation du script, et aucun panneau regroupant encore par `(name)`. ### Ce qu'il faut savoir avant de fusionner - **La vérification sur le serveur reste à faire** : cette demande n'a rien déployé. Le remède a été éprouvé sur `ml-stagiaire-02` avec un cAdvisor de test hors pile, sur un port séparé (supprimé depuis) — la pile de production n'a pas été touchée. Les critères 1 à 3 du ticket se contrôlent au déploiement suivant. - **`noms-conteneurs` parle au démon Docker**, il en a donc tous les pouvoirs : le `:ro` sur le socket protège le fichier, pas les commandes qu'on y écrit. Il n'appelle qu'une route, en lecture (`GET /containers/json`), et n'écoute que sur la boucle locale. À dire tel quel si la question vient au jury. - **Les panneaux réseau par conteneur resteront presque vides**, et c'est attendu : nos piles tournent en réseau hôte et n'ont pas de compteurs réseau propres. Seuls les exécutants de la forge en ont. Leur description le dit maintenant au lecteur, plutôt que de laisser croire à une panne. ### Vérification ``` bash tests/ci/test-supervision.sh # passe ; échoue si la lecture containerd disparaît ``` Éprouvé en local : le script rend bien le format d'exposition attendu contre un faux démon, échappe les guillemets d'un nom, ignore un conteneur sans nom, et reste debout si le démon se tait.
supervision : le tableau de bord des conteneurs ne voyait plus un seul conteneur (#203)
Some checks failed
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 44s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 7s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 19s
Intégration / Python — qualité, tests et dépendances (pull_request) Failing after 1m24s
af05298f62
Panne silencieuse : la cible `cadvisor` était `up`, le conteneur `healthy`,
et il publiait bien 2 500 séries — mais toutes portaient `id="/"`, le cgroup
racine. Aucune ne portait de nom de conteneur, donc les requêtes en
`{name!=""}` des tableaux de bord « conteneurs » et « socle » ne rendaient
rien : quatre stats à zéro et « No data » partout.

La cause n'est pas dans le dépôt. Le démon de ml-stagiaire-02 est passé au
containerd image store (Docker 29 : `Storage Driver: overlayfs`,
`io.containerd.snapshotter.v1`) : la couche d'écriture d'un conteneur n'est
plus sous /var/lib/docker/image/, cAdvisor v0.52 ne la trouve pas, et il
écarte chaque conteneur en entier plutôt que de publier ce qu'il en sait.
Son journal le disait, une ligne par conteneur et par minute.

Deux pistes essayées et écartées : monter en version — v0.52.1 est la
dernière publiée — et couper les métriques de disque, qui ne change rien.
Reste la lecture directe de containerd, dans le namespace `moby` où Docker
range ses conteneurs. Son handler Docker doit être neutralisé au passage,
sans quoi il reprend la main sur les mêmes cgroups pour les rejeter.

Ce que ça coûte : containerd ne connaît pas les noms Docker, `name` devient
un identifiant de 64 caractères. D'où `noms-conteneurs`, qui publie la
correspondance identifiant -> nom lisible que les panneaux joignent par
`on(name) group_left(nom)`. Même parti que le relais d'alertes : un fichier
de bibliothèque standard monté dans l'image Python officielle, écoute bornée
à la boucle locale. Il parle au démon Docker et en a donc tous les pouvoirs,
mais n'appelle qu'une route, en lecture.

Le banc tient désormais le remède en place : la lecture de containerd, la
présence du service, le job de collecte, et le fait qu'aucun panneau ne
regroupe plus par (name). Aucun contrôle n'aurait attrapé la panne à
l'exécution — c'est ce que le manuel documente maintenant, avec le contrôle
qui la révèle en une commande.

Les panneaux réseau par conteneur restent presque vides, et c'est attendu :
nos piles tournent en réseau hôte et n'ont pas de compteurs réseau propres.
Leur description le dit désormais au lecteur.
supervision : noms.py passe le lint du dépôt (#203)
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 41s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 8s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 17s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m32s
bbee59f332
`ruff check infra` couvre le répertoire : shebang sans bit d'exécution,
concaténation implicite dans une liste, `.format()` au lieu d'une f-chaîne,
`sorted()[0]` pour un minimum. Aucun changement de comportement.

Un mot de plus au passage sur le choix du nom : un conteneur peut porter
plusieurs alias, et prendre le premier venu ferait changer la série de nom
d'un scrape à l'autre — les courbes se couperaient en deux sans que rien
ne l'explique.
gabriel self-assigned this 2026-09-08 13:01:28 +00:00
lenaic approved these changes 2026-09-08 13:08:11 +00:00
lenaic removed review request for olivier 2026-09-08 13:08:15 +00:00
Merge branch 'develop' into gabriel/203-conteneurs-invisibles
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 42s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 8s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 20s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m48s
2d951b5f15
olivier merged commit 27a552fa51 into develop 2026-09-08 13:57:18 +00:00
olivier deleted branch gabriel/203-conteneurs-invisibles 2026-09-08 13:57:18 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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
g2/enervision!204
No description provided.