[incident] supervision : le tableau de bord des conteneurs ne voit plus aucun conteneur #203

Closed
opened 2026-09-08 12:47:49 +00:00 by gabriel · 0 comments
Member

Symptôme observé

Le tableau de bord Grafana « EnerVision — conteneurs » est vide de bout en bout : « Conteneurs vivants » à 0, « CPU cumulé » à 0, « RAM cumulée » à 0 B, et « No data » sur les quatre panneaux par conteneur. Le compteur du tableau de bord « socle » affiche 0 pour la même raison.

Rien ne signale la panne : les huit cibles Prometheus sont vertes, ev-cadvisor est healthy, il répond, et il publie même 2 500 séries — mais toutes portent id="/", le cgroup racine. Aucune ne porte de nom de conteneur, donc les requêtes en {name!=""} ne rendent rien.

Journal de ev-cadvisor, une ligne par conteneur et par minute :

Failed to create existing container: /system.slice/docker-<id>.scope: failed to
identify the read-write layer ID for container "<id>". - open
/rootfs/var/lib/docker/image/overlayfs/layerdb/mounts/<id>/mount-id: no such file
or directory

Comment le reproduire

  1. Ouvrir https://grafana.g2.enervision/d/enervision-conteneurs/ — tout est à zéro.
  2. Sur le serveur : curl -s 127.0.0.1:9101/metrics | grep '^container_last_seen' → une seule ligne, id="/".
  3. docker logs ev-cadvisor → le rejet ci-dessus pour chacun des vingt conteneurs.

Ce qui a été tenté

  • Vérifier les cibles Prometheus : les huit sont up, sans erreur de collecte. Ce n'est donc pas un problème de collecte, et c'est ce qui rend la panne silencieuse.
  • Monter en version cAdvisor : impossible, v0.52.1 est la dernière publiée (v0.53.0 n'existe pas).
  • Couper les métriques de disque (--disable_metrics=…,disk,diskIO), au cas où le rejet ne viendrait que de la couche d'écriture : sans effet, les conteneurs restent rejetés.
  • Lire containerd au lieu de Docker (--containerd=…/containerd.sock --containerd-namespace=moby) : les vingt conteneurs remontent, avec CPU, RAM et E/S.

Cause trouvée

Le démon de ml-stagiaire-02 est en Docker 29.1.3 et range ses images avec le containerd image store : docker info donne Storage Driver: overlayfs et driver-type: io.containerd.snapshotter.v1. Dans ce mode, /var/lib/docker/image/overlayfs/layerdb/ n'existe pas — les couches sont chez containerd.

cAdvisor v0.52 cherche la couche d'écriture à cet emplacement pour chaque conteneur découvert par l'API Docker, échoue, et écarte le conteneur en entier. Il ne lui reste que le cgroup racine à publier.

Ce n'est pas une régression du dépôt : la pile de supervision n'a pas changé, c'est le démon en dessous.

Correction appliquée

Faire lire containerd à cAdvisor (namespace moby, socket déjà atteignable par /var/run, déjà monté) et neutraliser son handler Docker, qui prendrait sinon la main sur les mêmes cgroups pour les rejeter.

Contrepartie : containerd ne connaît pas les noms Docker — name devient l'identifiant de 64 caractères. Un service noms-conteneurs (script de bibliothèque standard monté dans python:3.12-alpine, même parti que relais-forge) publie container_nom{name="<id>",nom="ev-grafana"} 1 sur 127.0.0.1:9102, que les panneaux joignent par on(name) group_left(nom).

Critères d'acceptation

  1. curl -s 127.0.0.1:9101/metrics | grep -c '^container_last_seen' rend au moins autant de lignes que docker ps -q | wc -l, et non plus une seule.
  2. Le tableau de bord « conteneurs » désigne les conteneurs par leur nom Docker (ev-api, ev-grafana, …), jamais par un identifiant hexadécimal ; ses quatre stats et ses trois panneaux par conteneur portent des valeurs.
  3. Le compteur « conteneurs vivants » du tableau de bord « socle » affiche le même nombre que docker ps -q | wc -l.
  4. Le nouveau service n'écoute que sur 127.0.0.1, et son port figure au plan d'adressage de docs/EXIGENCES-collectives.md §8.
  5. tests/ci/test-supervision.sh échoue si cAdvisor reperd sa lecture de containerd, si le job de correspondance disparaît de prometheus.yml, ou si un panneau conteneur revient à une requête sans jointure de nom.

Vérification : bash tests/ci/test-supervision.sh, et le contrôle manuel des critères 1 à 3 sur le serveur.

Deux points connus, hors périmètre de ce ticket

  • Les panneaux « Réseau reçu / émis par conteneur » resteront presque vides quoi qu'il arrive : nos piles tournent en réseau hôte et n'ont donc pas de compteurs réseau propres. Seuls les runners de la forge en ont.
  • Le service de correspondance parle au démon Docker, donc il en a tous les pouvoirs : le :ro sur le socket protège le fichier, pas les commandes qu'on y écrit. Il n'appelle qu'une seule route, en lecture (GET /containers/json), et n'écoute que sur la boucle locale.

Temps perdu

1 h


Exigence servie : ENF-08 (Observabilité). Épreuve : EC04.

### Symptôme observé Le tableau de bord Grafana « EnerVision — conteneurs » est vide de bout en bout : « Conteneurs vivants » à 0, « CPU cumulé » à 0, « RAM cumulée » à 0 B, et « No data » sur les quatre panneaux par conteneur. Le compteur du tableau de bord « socle » affiche 0 pour la même raison. Rien ne signale la panne : les huit cibles Prometheus sont vertes, `ev-cadvisor` est `healthy`, il répond, et il publie même 2 500 séries — mais toutes portent `id="/"`, le cgroup racine. Aucune ne porte de nom de conteneur, donc les requêtes en `{name!=""}` ne rendent rien. Journal de `ev-cadvisor`, une ligne par conteneur et par minute : ``` Failed to create existing container: /system.slice/docker-<id>.scope: failed to identify the read-write layer ID for container "<id>". - open /rootfs/var/lib/docker/image/overlayfs/layerdb/mounts/<id>/mount-id: no such file or directory ``` ### Comment le reproduire 1. Ouvrir `https://grafana.g2.enervision/d/enervision-conteneurs/` — tout est à zéro. 2. Sur le serveur : `curl -s 127.0.0.1:9101/metrics | grep '^container_last_seen'` → une seule ligne, `id="/"`. 3. `docker logs ev-cadvisor` → le rejet ci-dessus pour chacun des vingt conteneurs. ### Ce qui a été tenté - **Vérifier les cibles Prometheus** : les huit sont `up`, sans erreur de collecte. Ce n'est donc pas un problème de collecte, et c'est ce qui rend la panne silencieuse. - **Monter en version cAdvisor** : impossible, v0.52.1 est la dernière publiée (v0.53.0 n'existe pas). - **Couper les métriques de disque** (`--disable_metrics=…,disk,diskIO`), au cas où le rejet ne viendrait que de la couche d'écriture : sans effet, les conteneurs restent rejetés. - **Lire containerd au lieu de Docker** (`--containerd=…/containerd.sock --containerd-namespace=moby`) : les vingt conteneurs remontent, avec CPU, RAM et E/S. ### Cause trouvée Le démon de `ml-stagiaire-02` est en Docker 29.1.3 et range ses images avec le *containerd image store* : `docker info` donne `Storage Driver: overlayfs` et `driver-type: io.containerd.snapshotter.v1`. Dans ce mode, `/var/lib/docker/image/overlayfs/layerdb/` n'existe pas — les couches sont chez containerd. cAdvisor v0.52 cherche la couche d'écriture à cet emplacement pour chaque conteneur découvert par l'API Docker, échoue, et **écarte le conteneur en entier**. Il ne lui reste que le cgroup racine à publier. Ce n'est pas une régression du dépôt : la pile de supervision n'a pas changé, c'est le démon en dessous. ### Correction appliquée Faire lire containerd à cAdvisor (namespace `moby`, socket déjà atteignable par `/var/run`, déjà monté) et neutraliser son handler Docker, qui prendrait sinon la main sur les mêmes cgroups pour les rejeter. Contrepartie : containerd ne connaît pas les noms Docker — `name` devient l'identifiant de 64 caractères. Un service `noms-conteneurs` (script de bibliothèque standard monté dans `python:3.12-alpine`, même parti que `relais-forge`) publie `container_nom{name="<id>",nom="ev-grafana"} 1` sur `127.0.0.1:9102`, que les panneaux joignent par `on(name) group_left(nom)`. ### Critères d'acceptation 1. `curl -s 127.0.0.1:9101/metrics | grep -c '^container_last_seen'` rend au moins autant de lignes que `docker ps -q | wc -l`, et non plus une seule. 2. Le tableau de bord « conteneurs » désigne les conteneurs par leur nom Docker (`ev-api`, `ev-grafana`, …), jamais par un identifiant hexadécimal ; ses quatre stats et ses trois panneaux par conteneur portent des valeurs. 3. Le compteur « conteneurs vivants » du tableau de bord « socle » affiche le même nombre que `docker ps -q | wc -l`. 4. Le nouveau service n'écoute que sur `127.0.0.1`, et son port figure au plan d'adressage de `docs/EXIGENCES-collectives.md` §8. 5. `tests/ci/test-supervision.sh` échoue si cAdvisor reperd sa lecture de containerd, si le job de correspondance disparaît de `prometheus.yml`, ou si un panneau conteneur revient à une requête sans jointure de nom. **Vérification** : `bash tests/ci/test-supervision.sh`, et le contrôle manuel des critères 1 à 3 sur le serveur. ### Deux points connus, hors périmètre de ce ticket - Les panneaux « Réseau reçu / émis par conteneur » resteront presque vides quoi qu'il arrive : nos piles tournent en réseau hôte et n'ont donc pas de compteurs réseau propres. Seuls les runners de la forge en ont. - Le service de correspondance parle au démon Docker, donc il en a tous les pouvoirs : le `:ro` sur le socket protège le fichier, pas les commandes qu'on y écrit. Il n'appelle qu'une seule route, en lecture (`GET /containers/json`), et n'écoute que sur la boucle locale. ### Temps perdu 1 h --- Exigence servie : **ENF-08** (Observabilité). Épreuve : **EC04**.
gabriel self-assigned this 2026-09-08 12:48:36 +00:00
gabriel added this to the EnerVision project 2026-09-08 12:48:42 +00:00
Sign in to join this conversation.
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
g2/enervision#203
No description provided.