🐧 Edge Linux Debian · diagnostic & exploitation

Chapitre 06
Docker sur l'edge : état & diagnostic

Objectifs du chapitre

1. Le vocabulaire minimal

ObjetCe que c'estAnalogie
Imageun modèle figé en lecture seule (app + dépendances), versionné par un tagle « programme installé », une version précise
Conteneurune instance en cours d'exécution d'une imagele programme « lancé »
Volumeun stockage persistant, hors du conteneurle disque de données, qui survit au conteneur
Réseaule réseau virtuel qui relie les conteneurs entre euxle switch interne de la stack

La distinction image ≠ conteneur est le socle : une image ne « tourne » pas, elle sert de moule ; un conteneur est une image en train de s'exécuter, avec son état propre. Et surtout : tout ce qu'un conteneur écrit hors d'un volume est perdu à sa recréation. C'est pour ça que les données vivent dans des volumes — retiens-le pour le chapitre 8 (sauvegarde).

2. Voir ce qui tourne : docker ps

maint@edge-cell12:~$ docker ps
CONTAINER ID   IMAGE                       STATUS                    PORTS                    NAMES
b1f9a2c3d4e5   registry.local/edge:1.8.2   Up 87 days (healthy)      0.0.0.0:8080->8080/tcp   edge-gateway
7a2b1c9d8e6f   eclipse-mosquitto:2.0.18    Up 87 days                0.0.0.0:1883->1883/tcp   edge-mqtt

Chaque colonne dit quelque chose d'utile :

docker ps ne montre que les conteneurs en cours d'exécution. Un conteneur qui a planté n'apparaît pas — et on croit à tort qu'« il n'existe pas ». Pour voir aussi les morts, il faut docker ps -a : c'est là qu'on découvre un Exited (137) révélateur. Réflexe : quand un conteneur « a disparu », la première commande est docker ps -a, pas docker ps.

maint@edge-cell12:~$ docker ps -a
CONTAINER ID   IMAGE                       STATUS                       NAMES
b1f9a2c3d4e5   registry.local/edge:1.8.2   Up 87 days (healthy)         edge-gateway
7a2b1c9d8e6f   eclipse-mosquitto:2.0.18    Exited (137) 4 minutes ago   edge-mqtt

3. Que dit le conteneur : docker logs

La sortie de l'application à l'intérieur du conteneur (que journald ne montre pas) se lit avec docker logs :

maint@edge-cell12:~$ docker logs --tail 20 -f edge-gateway
2026-07-04T08:24:01Z INFO  connected to opc.tcp://192.168.0.1:4840
2026-07-04T08:24:01Z INFO  subscribed 128 nodes, publish interval 1000ms
2026-07-04T08:24:02Z INFO  → mqtt localhost:1883 (128 tags)
^C

Les options utiles : --tail 20 (les 20 dernières lignes), -f (suivre en direct, comme journalctl -f), --since 10m (fenêtre temporelle), --timestamps (forcer l'horodatage).

Rappel de la répartition : journalctl -u edge-gateway répond « le service a-t-il démarré/planté ? » (vu par systemd) ; docker logs edge-gateway répond « que dit l'application dedans ? ». Sur un service edge qui remonte des données, la panne applicative (session OPC-UA refusée, broker injoignable) se lit dans docker logs.

4. Le healthcheck : sain ou malade ?

Un conteneur peut être Up (le processus tourne) tout en étant incapable de rendre son service (bloqué, déconnecté). Le healthcheck est une sonde définie dans l'image ou le compose qui teste périodiquement la santé applicative. On voit son verdict dans STATUS (healthy/unhealthy) et son détail avec inspect :

maint@edge-cell12:~$ docker inspect --format '{{json .State.Health}}' edge-gateway | python3 -m json.tool
{
    "Status": "healthy",
    "FailingStreak": 0,
    "Log": [
        { "ExitCode": 0, "Output": "OPC-UA up; MQTT up\n" }
    ]
}

Un conteneur Up … (unhealthy) est un signal précieux : le processus n'est pas mort, donc docker ps paraît normal, mais la sonde applicative échoue. C'est typiquement l'edge qui « tourne » mais ne remonte plus rien : la session OPC-UA est tombée, ou le broker MQTT ne répond plus. Sans healthcheck, cette panne serait invisible jusqu'à ce que quelqu'un remarque l'absence de données côté supervision.

5. Consommation : docker stats

maint@edge-cell12:~$ docker stats --no-stream
CONTAINER      CPU %   MEM USAGE / LIMIT   MEM %   NET I/O         BLOCK I/O
edge-gateway   3.2%    142MiB / 512MiB     27.7%   88MB / 44MB     12MB / 0B
edge-mqtt      0.8%    18MiB / 128MiB      14.1%   44MB / 88MB     2MB / 0B

--no-stream donne un instantané (sans lui, l'affichage se rafraîchit en continu). On surveille surtout la colonne MEM USAGE / LIMIT : un conteneur qui frôle sa limite mémoire est candidat à se faire tuer par le noyau (l'OOM kill du chapitre 4), avec un code de sortie 137.

6. Panne classique nº 1 : le restart loop

docker ps montre le conteneur en Restarting en boucle. La démarche :

maint@edge-cell12:~$ docker ps
CONTAINER ID   IMAGE                       STATUS                          NAMES
b1f9a2c3d4e5   registry.local/edge:1.8.2   Restarting (1) 8 seconds ago    edge-gateway

maint@edge-cell12:~$ docker logs --tail 5 edge-gateway
2026-07-04T09:01:14Z FATAL config file /cfg/gateway.yml: no such file or directory
maint@edge-cell12:~$ docker inspect --format '{{.State.ExitCode}} {{.RestartCount}}' edge-gateway
1 214

Lecture : le conteneur sort en erreur (ExitCode 1), la politique de redémarrage le relance, il replante — 214 fois. La cause est dans docker logs : un fichier de config manquant (un volume mal monté ? un chemin renommé ?). Le restart loop n'est pas la maladie, c'est le symptôme : on lit toujours la dernière erreur avant le redémarrage.

Les codes de sortie les plus parlants : 137 = tué par SIGKILL, presque toujours un OOM kill (manque de mémoire) — croise avec journalctl -k | grep -i oom et docker stats. 139 = segfault (SIGSEGV), un bug applicatif. 143 = arrêt propre par SIGTERM (normal lors d'un stop). 1 = erreur applicative générique — la vraie raison est dans les logs.

7. Réseaux & volumes en un coup d'œil

maint@edge-cell12:~$ docker volume ls
DRIVER    VOLUME NAME
local     edge_gateway-data      # ← les données à sauvegarder (ch. 8)
local     edge_mqtt-data

maint@edge-cell12:~$ docker network ls
NETWORK ID     NAME          DRIVER    SCOPE
3f1a...        edge_default  bridge    local
# Les conteneurs de la stack se parlent par leur nom sur ce réseau :
# edge-gateway atteint le broker via « edge-mqtt:1883 », pas via une IP.

Sur le réseau d'une stack compose, les conteneurs se résolvent par leur nom de service (DNS interne Docker). Si le gateway ne joint plus le broker, avant de suspecter le réseau OT, vérifie que les deux sont bien sur le même réseau Docker et que le nom de service n'a pas changé : une erreur « could not resolve edge-mqtt » est interne à Docker, pas un problème de câble.

8. Borner les logs Docker (place disque)

Par défaut, le pilote de logs json-file de Docker écrit sans limite : un conteneur bavard peut remplir /var/lib/docker et donc le disque. On borne, globalement dans /etc/docker/daemon.json :

{
  "log-driver": "json-file",
  "log-opts": { "max-size": "20m", "max-file": "5" }
}

Chaque conteneur garde alors au plus 5 fichiers de 20 Mo. On applique par sudo systemctl restart dockerce qui redémarre tous les conteneurs, donc à faire dans une fenêtre de maintenance. On peut aussi mettre ces options par service dans le compose (chapitre 7), ce qui évite le restart global.

Le geste à ne jamais faire à l'aveugle sur un edge : docker system prune -a. Il supprime toutes les images non utilisées par un conteneur actif — y compris l'image de la version précédente que tu gardais exprès pour un rollback (chapitre 7), et potentiellement une image dont dépend un conteneur arrêté que tu comptais relancer. Sur un edge sans accès facile au registry, tu peux te retrouver incapable de redéployer. Purge de façon ciblée (docker image rm <tag précis>), jamais en masse.

Récapitulatif

Exercices

Exercice 1 — « Le conteneur a disparu »

Un collègue dit que edge-mqtt « n'existe plus » car docker ps ne le montre pas. Comment vérifies-tu s'il a planté, et pourquoi ?

Voir la solution
$ docker ps -a | grep edge-mqtt
7a2b1c9d8e6f  eclipse-mosquitto:2.0.18  Exited (137) 4 minutes ago  edge-mqtt
$ docker logs --tail 20 edge-mqtt      # que disait-il avant de mourir ?
$ journalctl -k | grep -i oom          # le noyau l'a-t-il tué (OOM) ?

docker ps -a révèle le conteneur mort (que docker ps cache). Le Exited (137) oriente vers un OOM kill ; on confirme avec les logs du noyau et docker stats sur les conteneurs restants. Le conteneur n'a pas « disparu », il s'est arrêté — on peut le relancer une fois la cause traitée.

Exercice 2 — Up mais rien ne remonte

La supervision ne reçoit plus de données, pourtant docker ps montre edge-gateway en Up. Où regardes-tu ?

Voir la solution

Le mot-clé est le healthcheck : regarde si le STATUS dit (unhealthy), et lis la sonde :

$ docker ps                      # Up ... (unhealthy) ?
$ docker inspect --format '{{json .State.Health}}' edge-gateway
$ docker logs --tail 30 edge-gateway   # session OPC-UA tombée ? broker injoignable ?

« Up » ne veut pas dire « fonctionne ». Le healthcheck et les logs applicatifs disent la vérité : souvent la session OPC-UA vers le CPU est tombée (redémarrage automate, certificat, réseau) ou le broker MQTT ne répond plus. La cause peut même être en dehors de l'edge (l'automate) — d'où l'intérêt du cours réseau en complément.

Exercice 3 — Pourquoi pas prune -a ?

Le disque se remplit d'images Docker. Un collègue lance docker system prune -a. Quel risque, et quelle alternative ?

Voir la solution

prune -a supprime toutes les images qu'aucun conteneur actif n'utilise — dont l'image de la version précédente gardée exprès pour un rollback (chapitre 7). Sur un edge coupé du registry, on peut alors ne plus pouvoir redéployer l'ancienne version. Alternative ciblée : docker images pour lister, puis docker image rm registry.local/edge:1.7.0 pour retirer une image précise dont on est sûr de ne plus avoir besoin. On peut aussi docker image prune (sans -a) qui ne retire que les couches « dangling » (orphelines), bien plus sûr.