Chapitre 07
Déploiement Docker
Objectifs du chapitre
- Décrire le service edge de façon reproductible avec Docker Compose.
- Comprendre les tags d'image et pourquoi on ne déploie jamais
:latestsur un edge. - Faire une mise à jour propre (pull d'une version précise,
up -d) et surtout savoir revenir en arrière (rollback) en une minute. - Régler les politiques de redémarrage et articuler compose ↔ systemd.
1. Docker Compose : la stack en un fichier
Plutôt qu'une série de docker run à rallonge, on décrit toute la stack edge dans un
fichier compose.yml — versionné, relisable, reproductible. Le nôtre, dans
/opt/edge/ :
# /opt/edge/compose.yml
services:
gateway:
image: registry.local/edge:1.8.2 # ← version PRÉCISE, jamais :latest
container_name: edge-gateway
restart: unless-stopped
depends_on:
- mqtt
ports:
- "8080:8080"
volumes:
- gateway-data:/data # données persistantes
- ./gateway.yml:/cfg/gateway.yml:ro # config en lecture seule
environment:
- OPCUA_ENDPOINT=opc.tcp://192.168.0.1:4840
healthcheck:
test: ["CMD", "/healthcheck.sh"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: json-file
options: { max-size: "20m", max-file: "5" }
mqtt:
image: eclipse-mosquitto:2.0.18
container_name: edge-mqtt
restart: unless-stopped
volumes:
- mqtt-data:/mosquitto/data
volumes:
gateway-data:
mqtt-data:
Les commandes de base de la stack, exécutées depuis /opt/edge/ :
maint@edge-cell12:/opt/edge$ docker compose ps # état de la stack
maint@edge-cell12:/opt/edge$ docker compose up -d # (re)crée ce qui doit l'être, en arrière-plan
maint@edge-cell12:/opt/edge$ docker compose logs -f # logs agrégés de la stack
maint@edge-cell12:/opt/edge$ docker compose down # arrête et retire les conteneurs (PAS les volumes)
docker compose down retire les conteneurs et le réseau, mais conserve les
volumes nommés — tes données survivent. En revanche docker compose down -v
supprime aussi les volumes : sur un edge, c'est effacer les données persistantes du
service (historique local, état, base). Ne tape jamais -v par réflexe ; c'est
irréversible sans sauvegarde (chapitre 8).
2. Les tags : pourquoi :latest est un piège
Une image est identifiée par un tag (edge:1.8.2). Le tag spécial
:latest ne veut pas dire « la plus récente en permanence » : c'est
juste un nom par défaut, qui pointe vers une image figée au moment du pull. Sur un edge, il
pose deux problèmes graves :
- Tu ne sais plus ce qui tourne. Deux edges « en
:latest» peuvent exécuter des images différentes selon la date de leur dernier pull. Impossible de reproduire un incident ou de garantir un parc homogène. - Le rollback devient impossible. Sans numéro de version, on ne peut pas désigner « la version d'avant » pour y revenir.
Règle simple : on déploie toujours un tag de version explicite et immuable
(1.8.2), on garde une trace de la version précédente, et on ne se sert de :latest
que pour du test jetable. Un edge en production doit pouvoir répondre à « quelle version
tourne ? » d'un seul docker ps.
3. Le registry : d'où viennent les images
Les images sont stockées dans un registry — public (Docker Hub) ou, en OT, souvent un
registry privé de l'usine (registry.local), accessible depuis l'edge. On récupère une image
avec pull :
maint@edge-cell12:~$ docker pull registry.local/edge:1.9.0
1.9.0: Pulling from edge
a1b2c3: Pull complete
d4e5f6: Pull complete
Digest: sha256:7f3a...c1
Status: Downloaded newer image for registry.local/edge:1.9.0
Sur un réseau OT cloisonné, l'edge n'a souvent pas d'accès Internet : le
registry public est injoignable. Les images passent par le registry interne, ou sont chargées à la main
(docker load depuis un .tar transféré par le laptop : docker save
registry.local/edge:1.9.0 -o edge-1.9.0.tar côté source, docker load -i edge-1.9.0.tar
côté edge). Prévois comment l'image arrive avant de planifier une mise à jour — c'est souvent le
vrai point dur en production.
4. Mettre à jour proprement
La mise à jour est un simple changement de tag suivi d'un up -d. Compose ne recrée que ce
qui a changé (le service gateway), et laisse le broker tranquille.
# 1. Récupérer la nouvelle image AVANT de toucher au service en marche
maint@edge-cell12:/opt/edge$ docker pull registry.local/edge:1.9.0
# 2. Mettre à jour le tag dans compose.yml : image: registry.local/edge:1.9.0
maint@edge-cell12:/opt/edge$ sudoedit compose.yml
# 3. Appliquer : seul « gateway » est recréé (nouvelle image), « mqtt » reste en place
maint@edge-cell12:/opt/edge$ docker compose up -d
[+] Running 2/2
✔ Container edge-mqtt Running
✔ Container edge-gateway Started
# 4. VÉRIFIER immédiatement que ça remonte bien
maint@edge-cell12:/opt/edge$ docker compose ps
maint@edge-cell12:/opt/edge$ docker logs --tail 20 -f edge-gateway
L'étape 1 (pull avant) est stratégique : le téléchargement peut être long ou échouer ;
on veut l'avoir sécurisé pendant que l'ancienne version tourne encore. Le service n'est
interrompu qu'à l'étape 3, brièvement, image déjà en local. Ne jamais éditer le tag puis up -d
en espérant que le pull se fasse tout seul au pire moment.
5. Rollback : revenir en arrière en une minute
La nouvelle version se comporte mal (le healthcheck vire unhealthy, les logs crachent des
erreurs, la supervision ne reçoit plus). On revient à la version précédente — d'où
l'importance d'avoir gardé son image et noté son tag.
# 1. Constat : la 1.9.0 est mauvaise
maint@edge-cell12:/opt/edge$ docker logs --tail 10 edge-gateway
2026-07-04T10:12:03Z FATAL schema mismatch: expected v3, got v4
# 2. Remettre le tag précédent dans compose.yml : image: registry.local/edge:1.8.2
maint@edge-cell12:/opt/edge$ sudoedit compose.yml
# 3. Ré-appliquer : l'image 1.8.2 est toujours en local, redéploiement immédiat
maint@edge-cell12:/opt/edge$ docker compose up -d
✔ Container edge-gateway Started
maint@edge-cell12:/opt/edge$ docker ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}'
edge-gateway registry.local/edge:1.8.2 Up 4 seconds (health: starting)
up -d — pas
un nouveau téléchargement. C'est pourquoi on ne purge jamais l'image de la version précédente
(chapitre 6).Le rollback n'est rapide que si trois conditions sont réunies : (1) l'image
précédente est encore en local (ne pas l'avoir pruneée), (2) tu connais son tag exact,
(3) les données du volume restent compatibles. Le point (3) est le piège : si la
1.9.0 a migré le schéma des données dans le volume, revenir à la 1.8.2 peut ne plus lire ces données.
D'où l'ordre de bataille : sauvegarde du volume (chapitre 8) avant toute mise à jour,
pour pouvoir restaurer aussi les données, pas seulement l'image.
6. Politiques de redémarrage
La clé restart: du compose dit à Docker quoi faire quand un conteneur s'arrête :
| Politique | Comportement |
|---|---|
no | ne jamais redémarrer (défaut) |
on-failure | redémarre seulement si sortie en erreur (code ≠ 0) |
always | redémarre toujours, même après un stop manuel + reboot |
unless-stopped | comme always, mais respecte un arrêt manuel |
Pour un service edge, unless-stopped est le bon défaut : il redémarre
le conteneur après un crash et après un reboot de l'edge, tout en te laissant l'arrêter
volontairement pour une maintenance sans qu'il ressuscite tout seul.
7. Compose ou systemd ? Les deux
On a vu deux mécanismes de résilience : la politique restart: de Docker
(chapitre 7) et l'unité systemd edge-gateway.service (chapitre 3). Ils ne
s'excluent pas :
- Docker
enabledau boot (systemctl enable docker) +restart: unless-stoppeddans le compose : suffit à ce que la stack revienne seule après un reboot. C'est l'approche la plus simple. - Une unité systemd qui pilote
docker compose up(chapitre 3) : ajoute un interrupteur clair (systemctl start/stop edge-gateway), des logs dans journald, et des dépendances explicites. C'est l'approche la plus « administrable ».
Ne fais pas gérer le même conteneur par les deux à la fois de façon concurrente : si une
unité systemd fait compose up pendant que Docker applique aussi restart: always,
tu peux avoir des courses au démarrage (double lancement, ports en conflit). Choisis un chef :
soit systemd pilote la stack (et le compose garde restart pour le seul cas du crash entre
deux passages de systemd), soit Docker gère tout seul et tu n'écris pas d'unité. Le plus courant sur un
edge : Docker enabled + unless-stopped, sans unité dédiée.
Récapitulatif
- Compose décrit la stack en un fichier versionné :
up -d,ps,logs -f,down(jamaisdown -vpar réflexe — il efface les volumes). - Tags explicites toujours (
1.8.2), jamais:latesten prod : sinon on ne sait plus ce qui tourne et le rollback est impossible. - Mise à jour :
pullla nouvelle image d'abord, changer le tag,up -d, vérifier (ps + logs + healthcheck). - Rollback = remettre l'ancien tag +
up -d(image gardée en local). Attention à la compatibilité des données du volume — sauvegarder avant de mettre à jour (chapitre 8). - Résilience :
restart: unless-stopped+ Dockerenabledau boot ; éventuellement une unité systemd — mais un seul chef à la fois.
Exercices
Exercice 1 — Mettre à jour sans casser la prod
Tu dois passer edge-gateway de 1.8.2 à 1.9.0. Donne la séquence, dans le bon ordre, en
minimisant l'interruption et en gardant la possibilité de rollback.
Voir la solution
$ docker pull registry.local/edge:1.9.0 # 1. pull d'abord (ancienne toujours en marche)
# 2. éditer compose.yml : image: ...edge:1.9.0 (NE PAS supprimer l'image 1.8.2 locale)
$ docker compose up -d # 3. bascule brève
$ docker compose ps && docker logs -f edge-gateway # 4. vérifier healthcheck + remontée
Rollback possible parce qu'on n'a pas purgé l'image 1.8.2 : si la 1.9.0 déraille, on remet le
tag 1.8.2 et up -d. Idéalement, on a aussi sauvegardé le volume avant l'étape 3
(chapitre 8) au cas où la 1.9.0 aurait modifié les données.
Exercice 2 — Le rollback qui ne rollback pas
Tu reviens de 1.9.0 à 1.8.2, le conteneur démarre, mais il plante toujours avec des erreurs de lecture de données. Que s'est-il passé, et qu'aurais-tu dû faire ?
Voir la solution
La 1.9.0 a probablement migré le format des données dans le volume
(gateway-data). Revenir à l'image 1.8.2 ne suffit pas : le code 1.8.2 ne sait pas
lire des données au nouveau format. Le rollback d'image ne restaure que le code, pas les
données. Il aurait fallu sauvegarder le volume avant la mise à jour et le
restaurer en même temps que l'image (chapitre 8). C'est la raison pour laquelle « sauvegarde
avant déploiement » est une règle, pas une option.
Exercice 3 — Quelle politique de restart ?
Tu veux que le service edge : (a) revienne seul après un crash, (b) revienne après un reboot de
l'edge, mais (c) reste arrêté si toi l'arrêtes pour une maintenance. Quelle valeur de
restart: ?
Voir la solution
restart: unless-stopped. Il couvre (a) et (b) comme always, mais respecte
un arrêt manuel (c) : après docker compose stop, le conteneur ne redémarre pas tout
seul, y compris après reboot, jusqu'à ce que tu le relances explicitement. always, lui,
le ressusciterait même après ton arrêt volontaire — gênant en maintenance.