Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Mettre à jour le moteur et sauvegarder sa configuration

Par mis à jour le

Améliorer cette page

Logo Docker

Monter de version Docker Engine ne doit pas coûter une coupure à vos services. Cette leçon vous fait passer de la version 29.8.2 à la version 29.9.2 sur une machine Debian 13 qui porte un service en marche et une donnée à contrôler, puis revenir en arrière. Les paquets du moteur y sont retenus par apt-mark hold, comme la leçon d'installation les laisse. Avant chaque geste, vous relevez l'état et vous sauvegardez. Après, vous vérifiez. Les sorties viennent d'un passage complet de la leçon, le 2026-11-04, sur une VM jetable (Docker installé depuis le dépôt officiel, cgroup v2, magasin d'images containerd).

Deux notions servent dès le début. Le démon (dockerd) est le processus qui gère les conteneurs, et une mise à jour du paquet le redémarre. live-restore est le réglage de daemon.json qui laisse les conteneurs tourner pendant que le démon est arrêté, puis les lui fait reprendre en charge à son retour.

Le schéma montre l'enchaînement de la leçon. Regardez le losange : c'est lui qui décide si vous gardez la nouvelle version ou si vous revenez à la précédente, et la boîte du retour rappelle l'obstacle le plus probable, la limite de démarrage de systemd.

Suite de sept étapes : relever l'état et les retenues et poser le témoin, sauvegarder /etc/docker, ajouter live-restore par fusion, sauvegarder à nouveau, monter en levant puis reposant la retenue, puis un test sur le PID, StartedAt, le compteur de battements et l'empreinte. S'ils sont inchangés, on garde les retenues ; sinon on revient à la version précédente avec --allow-downgrades, et si systemd refuse de relancer le démon, on lance reset-failed puis start.ouinon① Relever l'état, les retenueset poser le témoin② Sauvegarder/etc/docker et les définitions③ Ajouter live-restore par fusionjq, validate, puis recharger④ Sauvegarder à nouveau/etc/docker⑤ Monter à la version nomméeunhold, installer, hold⑥ PID, StartedAt, compteuret empreinte inchangés ?⑦ Garder la versionretenues reposéesRevenir à la version précédenteunhold, --allow-downgrades, holdDémon en start-limit-hit ?reset-failed, puis start
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Les sections suivent ces étapes, avec deux détours : ce que fait un redémarrage du démon sans live-restore, et ce que donne un daemon.json fautif.

  • Relever l'état du moteur et ses retenues apt, et poser un service témoin et une donnée témoin avant d'intervenir
  • Sauvegarder /etc/docker et les définitions des conteneurs, et savoir ce qu'une archive ne restaure pas
  • Expliquer pourquoi un redémarrage du démon relance les conteneurs, et ce que live-restore change
  • Ajouter live-restore à un daemon.json existant sans l'écraser, et le valider avant de recharger
  • Monter à une version nommée du dépôt officiel en levant puis reposant la retenue, et vérifier la reprise par des preuves
  • Revenir à la version précédente, et repartir quand systemd refuse de relancer le démon

La configuration du démon elle-même (stockage, journaux, miroirs) est décrite dans Configurer le daemon Docker : cette leçon n'en retient qu'un réglage, live-restore. Le mécanisme par lequel un conteneur survit au démon (les processus intermédiaires containerd-shim) est expliqué dans Qui lance un conteneur : la chaîne d'exécution de Docker.

Partez d'une VM Debian 13 neuve, où le dépôt officiel de Docker est configuré comme dans la leçon d'installation. Vous y reproduisez l'état que cette leçon laisse, mais en 29.8.2 : les six paquets Docker installés en version nommée, docker-ce-rootless-extras compris, puis retenus. Le bloc ajoute aussi un daemon.json qui règle la rotation des journaux : la leçon d'installation n'en crée pas, il représente ici la configuration qu'un hôte réel porte déjà. Ne jouez pas ce bloc sur votre machine de formation, dont les paquets sont retenus en 29.9.2 : apt-get y refuserait de les changer.

Nommer la version de docker-ce-rootless-extras compte : ce paquet est recommandé par docker-ce, et sans version nommée, apt installe sa version la plus récente (29.9.3 sur une machine mesurée ici), qui diffère alors de celle du moteur.

Fenêtre de terminal
V=5:29.8.2-1~debian.13~trixie
sudo apt-get install -y --allow-downgrades \
docker-ce="$V" docker-ce-cli="$V" docker-ce-rootless-extras="$V" \
containerd.io=2.4.2-3~debian.13~trixie \
docker-buildx-plugin=0.38.1-1~debian.13~trixie \
docker-compose-plugin=5.6.1-1~debian.13~trixie
sudo apt-mark hold docker-ce docker-ce-cli docker-ce-rootless-extras containerd.io docker-buildx-plugin docker-compose-plugin
echo '{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker

L'option --allow-downgrades ne sert que si la machine porte déjà une version plus récente, non retenue. Sur une VM neuve, elle est sans effet.

Comment inventorier le moteur et poser un témoin ?

Section intitulée « Comment inventorier le moteur et poser un témoin ? »

Avant de toucher au moteur, relevez ce qui est installé, ce qui est configuré et ce qui est déjà retenu, puis créez de quoi prouver que l'intervention n'a rien cassé. Un inventaire écrit avant l'intervention sert de point de comparaison : sans lui, vous ne saurez pas dire ce qui a changé.

Fenêtre de terminal
docker version --format 'client={{.Client.Version}} serveur={{.Server.Version}}'
dpkg-query -W -f='${Package} ${Version}\n' docker-ce docker-ce-cli docker-ce-rootless-extras containerd.io docker-buildx-plugin docker-compose-plugin
apt-mark showhold
docker info --format 'liverestore={{.LiveRestoreEnabled}} logging={{.LoggingDriver}}'
sudo cat /etc/docker/daemon.json
apt-cache policy docker-ce | head -4
client=29.8.2 serveur=29.8.2
containerd.io 2.4.2-3~debian.13~trixie
docker-buildx-plugin 0.38.1-1~debian.13~trixie
docker-ce 5:29.8.2-1~debian.13~trixie
docker-ce-cli 5:29.8.2-1~debian.13~trixie
docker-ce-rootless-extras 5:29.8.2-1~debian.13~trixie
docker-compose-plugin 5.6.1-1~debian.13~trixie
containerd.io
docker-buildx-plugin
docker-ce
docker-ce-cli
docker-ce-rootless-extras
docker-compose-plugin
liverestore=false logging=json-file
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }
docker-ce:
Installed: 5:29.8.2-1~debian.13~trixie
Candidate: 5:29.9.3-1~debian.13~trixie
Version table:

Cinq choses se lisent dans cette sortie. Les trois paquets du moteur (docker-ce, docker-ce-cli et docker-ce-rootless-extras) sont en 29.8.2, et les trois autres portent leurs propres versions. apt-mark showhold liste ces six mêmes paquets : ils sont retenus. liverestore=false dit que le réglage n'est pas actif. Le fichier daemon.json existe déjà et porte des options de journal, qu'il faudra conserver. Enfin, le candidat du dépôt est 29.9.3, pas la version visée.

La dernière ligne compte : si vous ne nommez pas la version, apt installe le candidat, pas celle que vous avez décidée. La montée plus bas nomme donc la version.

Le témoin se compose d'une donnée dans un volume et d'un service qui écrit un battement par seconde, soit une ligne horodatage compteur dans un fichier du volume. La donnée permet de contrôler que rien n'est perdu. Le compteur de battements prouve si le service a été relancé : un service relancé repart à 1. L'image est épinglée par son empreinte.

Fenêtre de terminal
IMG='alpine:3.24.3@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a'
docker volume create vt-donnees
docker run --rm -v vt-donnees:/data "$IMG" sh -c 'echo "commande 4471 validee" > /data/commandes.txt'
docker run -d --name vt-service --restart unless-stopped -v vt-donnees:/data "$IMG" \
sh -c 'i=0; while true; do i=$((i+1)); echo "$(date +%s) $i" >> /data/battements; sleep 1; done'
docker run --rm -v vt-donnees:/data "$IMG" sha256sum /data/commandes.txt
docker inspect -f 'StartedAt={{.State.StartedAt}} Pid={{.State.Pid}} Restart={{.RestartCount}}' vt-service

Relevez deux valeurs : l'empreinte sha256 du fichier commandes.txt (ici 6dd06996…) et la ligne StartedAt et Pid du service. Le PID est le numéro du processus du conteneur vu depuis l'hôte, et StartedAt l'heure de son démarrage. Si l'un des deux change, le conteneur a été relancé. Le compteur du dernier battement dit la même chose : s'il repart à 1, le service a été relancé.

Trois petites fonctions, à coller dans votre shell, évitent de retaper ces mesures. etat affiche le PID et StartedAt, dernier affiche le dernier battement écrit (horodatage et compteur), et empreinte recalcule l'empreinte de la donnée. L'empreinte contrôle la donnée : elle montre qu'un fichier n'a pas changé, elle ne le protège pas.

Fenêtre de terminal
etat() {
docker inspect -f 'StartedAt={{.State.StartedAt}} Pid={{.State.Pid}} Restart={{.RestartCount}} Status={{.State.Status}}' vt-service
}
dernier() {
echo "dernier battement : $(sudo tail -1 "$(docker volume inspect -f '{{.Mountpoint}}' vt-donnees)/battements")"
}
empreinte() {
sudo sha256sum "$(docker volume inspect -f '{{.Mountpoint}}' vt-donnees)/commandes.txt"
}

Ces fonctions n'existent que dans le terminal où vous les collez. Les variables que posent les blocs suivants (T0, V, D) non plus. Gardez donc le même terminal pour toute la leçon, ou recollez les fonctions dans un nouveau terminal.

Sauvegardez les fichiers de configuration du démon, la définition des services systemd, les versions de paquets installées, la définition de chaque conteneur et la liste des images par empreinte. Aucune de ces pièces n'est dans les volumes : elles disparaissent avec une réinstallation, et ce sont elles qui permettent de redire exactement ce qui tournait.

Fenêtre de terminal
D=/root/sauvegarde-docker-$(date +%Y%m%d)
sudo mkdir -p "$D"
sudo tar -C / -czf "$D/etc-docker.tgz" etc/docker
sudo sh -c "systemctl cat docker.service > $D/docker.service.txt"
sudo sh -c "ls /etc/systemd/system/docker.service.d 2>&1 > $D/drop-ins.txt"
docker version --format '{{.Server.Version}}' | sudo tee "$D/version.txt" >/dev/null
dpkg-query -W -f='${Package}=${Version}\n' docker-ce docker-ce-cli docker-ce-rootless-extras containerd.io docker-buildx-plugin docker-compose-plugin | sudo tee "$D/paquets.txt" >/dev/null
docker inspect vt-service | sudo tee "$D/vt-service.json" >/dev/null
docker image ls --digests --format '{{.Repository}}:{{.Tag}}@{{.Digest}}' | sudo tee "$D/images.txt" >/dev/null
sudo ls -la "$D"
sudo tar -tzf "$D/etc-docker.tgz"

Lisez la liste de l'archive : elle doit contenir etc/docker/daemon.json, avec les options de journal. Si ls répond No such file or directory pour docker.service.d, aucune surcharge systemd n'existe et drop-ins.txt reste vide. L'ordre 2>&1 > fichier est voulu : il envoie la liste dans le fichier et laisse l'erreur s'afficher à l'écran. Pour tout capturer dans le fichier, la forme habituelle est > fichier 2>&1.

Cette sauvegarde pose une limite : une sauvegarde ne restaure que ce qu'elle contient. Une archive prise avant une modification de la configuration ramène la configuration d'avant. Vous la referez donc après chaque modification, et la section sur la configuration fautive montre pourquoi.

Les volumes ne figurent pas dans cette sauvegarde, et sur un hôte réel leur sauvegarde est un préalable à toute montée de version : un retour arrière ne ramène que les paquets et /etc/docker, pas l'état des données sur disque. La méthode est décrite dans Sauvegarder et restaurer une application.

Pourquoi un redémarrage du démon relance-t-il les conteneurs ?

Section intitulée « Pourquoi un redémarrage du démon relance-t-il les conteneurs ? »

Sans live-restore, un démon qui s'arrête arrête ses conteneurs. Redémarrer le démon, ce que fait une mise à jour de paquet, relance donc chaque conteneur avec un nouveau processus. Les fichiers du volume restent, mais le service est interrompu et son processus change.

Mesurez-le sur le témoin, avant d'activer quoi que ce soit. Le bloc relève l'état et le dernier battement, redémarre le démon, attend 4 secondes que le service reprenne, relève l'état à nouveau, puis affiche les battements écrits autour du redémarrage, depuis deux secondes avant T0.

Fenêtre de terminal
etat; dernier
T0=$(date +%s)
sudo systemctl restart docker
sleep 4
etat
sudo awk -v t0="$T0" '$1>=t0-2' "$(docker volume inspect -f '{{.Mountpoint}}' vt-donnees)/battements" | head -16
sudo journalctl -u docker --since "@$T0" --no-pager -o cat | grep -E 'Restoring containers|running containers|Loading containers: done' | head -5
StartedAt=2026-11-04T17:30:48.788526544Z Pid=32234 Restart=0 Status=running
dernier battement : 1793813514 66
StartedAt=2026-11-04T17:32:05.399836146Z Pid=32870 Restart=0 Status=running
1793813512 64
1793813513 65
1793813514 66
1793813515 67
1793813516 68
1793813517 69
1793813518 70
1793813519 71
1793813520 72
1793813521 73
1793813522 74
1793813523 75
1793813524 76
1793813525 1
1793813526 2
1793813527 3
time="2026-11-04T17:32:04.553208870Z" level=info msg="Restoring containers: start."
time="2026-11-04T17:32:05.489246380Z" level=info msg="Loading containers: done."

Regardez trois choses. Le Pid est passé de 32234 à 32870 et StartedAt de 17:30:48 à 17:32:05 : le conteneur a bien été relancé, parce que sa politique unless-stopped demande à Docker de le relancer. Le compteur de battements, lui, monte jusqu'à 76 puis repart à 1 : c'est la preuve la plus lisible d'une relance.

Entre le battement 66 (horodatage …514) et le battement 1 (…525), il s'écoule 11 secondes. Elles s'expliquent par le délai d'arrêt de 10 secondes : le shell qui sert de PID 1 à vt-service ignore SIGTERM, comme l'explique la leçon Cycle de vie d'un conteneur, PID 1 et arrêt propre. Docker attend donc 10 secondes, pendant lesquelles le conteneur continue d'écrire (battements 67 à 76), puis envoie SIGKILL et relance. Un service qui gère SIGTERM s'arrêterait plus vite, mais il serait tout de même relancé avec un nouveau PID. Le compteur RestartCount reste à 0 : il ne dit pas si le démon a relancé le conteneur, seuls le PID, StartedAt et le compteur de battements le disent.

Le journal montre Restoring containers: start., ligne qui apparaît même sans live-restore : elle ne prouve donc rien. Seule la ligne there are running containers signale que des conteneurs sont restés vivants, et elle n'apparaît pas ici.

Ajoutez "live-restore": true au daemon.json existant en le fusionnant, sans l'écraser, validez le fichier obtenu avec dockerd --validate, puis rechargez le démon avec systemctl reload docker. Un rechargement relit la configuration sans arrêter le démon : le réglage s'active sans toucher aux conteneurs. Un redémarrage les arrêterait d'abord, puisque le réglage n'est pas encore actif.

Écrire { "live-restore": true } avec tee effacerait les options de journal déjà présentes, sans que rien ne le signale. La fusion se fait avec jq, un outil de manipulation de JSON absent d'une Debian 13 minimale : sudo apt-get install -y jq l'installe, avec deux bibliothèques. Le bloc copie le fichier, écrit le résultat de la fusion dans daemon.json.nouveau, valide ce fichier, puis seulement alors le met en place.

Fenêtre de terminal
sudo apt-get install -y jq
sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.avant-live-restore
sudo jq '. + {"live-restore": true}' /etc/docker/daemon.json.avant-live-restore | sudo tee /etc/docker/daemon.json.nouveau
sudo dockerd --validate --config-file /etc/docker/daemon.json.nouveau && \
sudo mv /etc/docker/daemon.json.nouveau /etc/docker/daemon.json
echo "validation et mise en place rc=$?"
etat
sudo systemctl reload docker; echo "reload rc=$?"
docker info --format 'liverestore={{.LiveRestoreEnabled}} logging={{.LoggingDriver}}'
etat
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"live-restore": true
}
configuration OK
validation et mise en place rc=0
StartedAt=2026-11-04T17:32:05.399836146Z Pid=32870 Restart=0 Status=running
reload rc=0
liverestore=true logging=json-file
StartedAt=2026-11-04T17:32:05.399836146Z Pid=32870 Restart=0 Status=running

Lisez d'abord le fichier fusionné : les options de journal sont conservées et live-restore s'y ajoute. Lisez ensuite configuration OK, puis le code 0 de la ligne suivante : il vaut pour toute la chaîne, validation et mise en place par mv, puisque && n'exécute mv que si la validation réussit. Lisez enfin liverestore=true avec logging=json-file, qui montre que la configuration de journal a survécu. Les deux lignes StartedAt sont identiques : le service n'a pas bougé.

dockerd --validate ne voit pas tout. Il vérifie la syntaxe du fichier et ses valeurs, mais pas un conflit avec les options de la ligne de commande de l'unité systemd. L'unité mesurée lance /usr/bin/dockerd -H fd:// …, et un fichier contenant "hosts": ["unix:///var/run/docker.sock"] entre en conflit avec ce -H. La validation répond pourtant configuration OK avec le code 0, alors que la référence de dockerd indique qu'une option posée à la fois dans le fichier et sur la ligne de commande empêche le démon de démarrer. --validate détecte donc une partie des erreurs avant le redémarrage, pas toutes.

Refaites maintenant le même redémarrage que dans la section précédente, cette fois avec live-restore actif.

Fenêtre de terminal
T0=$(date +%s)
time sudo systemctl restart docker
sleep 4
etat

Le résultat est très différent. Le redémarrage dure 0,77 seconde au lieu de 11 secondes environ, le Pid reste le même et StartedAt ne change pas. Le démon a redémarré, le conteneur a continué d'écrire.

real 0m0.773s
[...]
StartedAt=2026-11-04T17:32:05.399836146Z Pid=32870 Restart=0 Status=running

Sauvegardez maintenant la configuration à jour. Cette archive servira de point de retour dans la section sur la configuration fautive, dans un répertoire distinct de la première sauvegarde.

Fenêtre de terminal
D=/root/sauvegarde-docker-avant-maj
sudo mkdir -p "$D"
sudo tar -C / -czf "$D/etc-docker.tgz" etc/docker
sudo tar -tzvf "$D/etc-docker.tgz"

Cette fois, la liste doit montrer etc/docker/daemon.json, avec live-restore et les options de journal, ainsi que la copie daemon.json.avant-live-restore que la fusion a laissée dans le répertoire.

Comment monter à la version visée et vérifier la reprise ?

Section intitulée « Comment monter à la version visée et vérifier la reprise ? »

Les six paquets étant retenus, apt refuse d'abord la montée : il faut lever la retenue des trois paquets du moteur (docker-ce, docker-ce-cli, docker-ce-rootless-extras), les installer en nommant la version, puis reposer la retenue. Le nom de version complet se lit avec apt-cache policy docker-ce ; pour cette montée, c'est 5:29.9.2-1~debian.13~trixie.

Commencez par tenter la montée sans lever la retenue, pour voir le refus.

Fenêtre de terminal
V=5:29.9.2-1~debian.13~trixie
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -q "docker-ce=$V" "docker-ce-cli=$V" "docker-ce-rootless-extras=$V"
docker version --format 'serveur={{.Server.Version}}'
The following held packages will be changed:
docker-ce docker-ce-cli docker-ce-rootless-extras
The following packages will be upgraded:
docker-ce docker-ce-cli docker-ce-rootless-extras
3 upgraded, 0 newly installed, 0 to remove and 4 not upgraded.
E: Held packages were changed and -y was used without --allow-change-held-packages.
serveur=29.8.2

apt-get annonce la montée des trois paquets, puis refuse : Held packages were changed. Le serveur reste en 29.8.2, rien n'a été installé. Ce refus est voulu : la retenue transforme une montée en geste décidé, et non en effet de bord d'une mise à jour système.

Avant de monter, laissez passer une minute depuis votre dernier redémarrage du démon : l'unité systemd de Docker refuse un quatrième démarrage dans la même minute, et la montée redémarre le démon (voir la section sur la limite de démarrage). Faites alors la montée dans l'ordre : relevez l'état avant, levez la retenue, installez, reposez la retenue, puis vérifiez les preuves (PID, StartedAt, compteur de battements, empreinte).

Fenêtre de terminal
etat; dernier; empreinte
T0=$(date +%s)
sudo apt-mark unhold docker-ce docker-ce-cli docker-ce-rootless-extras
time sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -q "docker-ce=$V" "docker-ce-cli=$V" "docker-ce-rootless-extras=$V"
sudo apt-mark hold docker-ce docker-ce-cli docker-ce-rootless-extras
sleep 4
apt-mark showhold
dpkg-query -W -f='${Package} ${Version}\n' docker-ce docker-ce-cli docker-ce-rootless-extras
docker version --format 'client={{.Client.Version}} serveur={{.Server.Version}}'
etat; dernier; empreinte
sudo cat /etc/docker/daemon.json
StartedAt=2026-11-04T17:32:05.399836146Z Pid=32870 Restart=0 Status=running
dernier battement : 1793813665 141
6dd06996a50ab6d3a5d0b0e3ffad7f34ac43e426c8ba40c0df15f2adafdbc060 /var/lib/docker/volumes/vt-donnees/_data/commandes.txt
Canceled hold on docker-ce.
Canceled hold on docker-ce-cli.
Canceled hold on docker-ce-rootless-extras.
The following packages will be upgraded:
3 upgraded, 0 newly installed, 0 to remove and 4 not upgraded.
real 0m5.061s
[...]
docker-ce set on hold.
docker-ce-cli set on hold.
docker-ce-rootless-extras set on hold.
containerd.io
docker-buildx-plugin
docker-ce
docker-ce-cli
docker-ce-rootless-extras
docker-compose-plugin
docker-ce 5:29.9.2-1~debian.13~trixie
docker-ce-cli 5:29.9.2-1~debian.13~trixie
docker-ce-rootless-extras 5:29.9.2-1~debian.13~trixie
client=29.9.2 serveur=29.9.2
StartedAt=2026-11-04T17:32:05.399836146Z Pid=32870 Restart=0 Status=running
dernier battement : 1793813674 150
6dd06996a50ab6d3a5d0b0e3ffad7f34ac43e426c8ba40c0df15f2adafdbc060 /var/lib/docker/volumes/vt-donnees/_data/commandes.txt
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"live-restore": true
}

Lisez cette sortie dans l'ordre. Les trois retenues sont levées puis reposées, et apt-mark showhold liste de nouveau les six paquets. Les trois paquets du moteur sont en 29.9.2, pour 5,1 secondes d'installation. Le Pid et le StartedAt sont inchangés, l'empreinte aussi, et le compteur de battements continue : il passe de 141 à 150 sans repartir à 1. La montée s'est donc faite sans interrompre le service. Le daemon.json a conservé ses options, puisqu'il a été fusionné.

Dans cette montée, containerd.io reste en 2.4.2-3 : monter aussi containerd.io n'a pas été mesuré, donc la leçon ne dit rien de son effet sur les conteneurs en cours. Si vous le montez, ajoutez-le à apt-mark unhold, puis à apt-mark hold.

Le journal du démon permet de confirmer que les conteneurs ont été repris. La commande suivante le lit depuis T0, l'instant juste avant l'installation, que vous posez en début de bloc avec T0=$(date +%s).

Fenêtre de terminal
sudo journalctl -u docker --since "@$T0" --no-pager | grep -iE 'live|restor|container|error|warn'

Cherchez there are running containers, updated network configuration will not take affect : cette ligne dit que le démon a trouvé des conteneurs vivants et n'a donc pas appliqué un changement de configuration réseau. C'est normal ici, puisqu'aucun réglage réseau n'a changé. Restoring containers: start. ne suffit pas, on l'a vu, puisqu'elle apparaît aussi sans live-restore. Le journal peut aussi contenir des lignes Deleting nftables ... error, qui signalent une table de règles absente : elles figurent dans cette mesure, et le témoin n'en a pas souffert.

La documentation Docker de live-restore ne promet le maintien des conteneurs que pour les versions correctives, et pas pour une montée de version. Elle ajoute que si vous sautez des versions, le démon peut ne pas retrouver ses conteneurs.

La documentation de Docker écrit encore les versions sous la forme YY.MM.x, héritée du versionnage calendaire que Docker Engine a quitté avec la version 23.0.0 pour le versionnage sémantique. Une version corrective ne change que le troisième nombre (29.9.2 vers 29.9.3). Passer de 29.8.2 à 29.9.2 change le deuxième : c'est une version mineure, et la montée de cette leçon dépasse donc ce que la documentation de Docker promet.

Sur cette combinaison, le conteneur a pourtant survécu. Prenez-le comme un fait mesuré, pas comme une garantie : la mesure vaut pour les versions 29.8.2 et 29.9.2, avec ce témoin, sur cette machine. Une autre montée peut se comporter autrement.

La même documentation décrit deux risques, qui justifient de ne pas monter un hôte de production à la légère.

  • Si le démon ne retrouve pas ses conteneurs à son retour, il ne peut plus les gérer, et vous devez les arrêter à la main.
  • Si le démon reste arrêté longtemps, le tampon FIFO de 64 Ko qui reçoit les journaux des conteneurs se remplit, et les conteneurs qui écrivent sur la sortie standard (stdout) se bloquent. Le témoin de cette leçon écrit dans un fichier du volume, pas sur stdout : cette mesure ne montre donc pas ce risque.

Pour une montée mineure, prévoyez une fenêtre de maintenance, comme si les conteneurs devaient être interrompus (voir la section sur le redémarrage sans live-restore). Live-restore réduit la coupure quand tout se passe bien, il ne la supprime pas par principe.

La même page précise que live-restore ne reprend les conteneurs que si les options du démon (adresse du pont réseau, pilote de stockage) n'ont pas changé. C'est pourquoi cette leçon ne modifie qu'un seul réglage à la fois.

Un daemon.json invalide empêche le démon de redémarrer, alors que les conteneurs continuent de tourner grâce à live-restore. Vous perdez la main (docker ps ne répond plus) sans avoir perdu le service. Validez toujours le fichier avant de redémarrer : dockerd --validate --config-file /etc/docker/daemon.json rend un code 1 sur un fichier invalide, sauf pour un conflit avec la ligne de commande de l'unité, qu'il ne voit pas.

Provoquez la panne avec une virgule en trop, erreur classique en JSON. Le bloc écrase volontairement le fichier avec un contenu invalide, ce qu'il ne faut jamais faire sur un hôte réel : c'est le geste que la fusion et la validation de la section précédente évitent. Sauvegardez d'abord la configuration à jour, ce qui est fait plus haut. Le bloc commence par systemctl reset-failed docker, qui remet à zéro le compteur de démarrages de l'unité : la section sur la limite de démarrage, plus bas, explique pourquoi.

Fenêtre de terminal
sudo systemctl reset-failed docker
echo '{ "live-restore": true, "log-driver": "json-file", }' | sudo tee /etc/docker/daemon.json
T0=$(date +%s)
sudo systemctl restart docker; echo "restart rc=$?"
sleep 2
systemctl is-active docker
sudo journalctl -u docker --since "@$T0" --no-pager -o cat | head -12
pgrep -af 'while true' | head -2

Lisez la sortie dans l'ordre. Le redémarrage rend le code 1 et l'état du service est activating, parce que systemd retente le démarrage. Le journal donne la cause exacte : invalid JSON: invalid character '}' looking for beginning of object key string. La dernière commande trouve encore le processus du conteneur : le conteneur tourne toujours, et il continue d'écrire ses battements alors que le démon est arrêté.

Job for docker.service failed because the control process exited with error code.
See "systemctl status docker.service" and "journalctl -xeu docker.service" for details.
restart rc=1
activating
Stopping docker.service - Docker Application Container Engine...
[...]
unable to configure the Docker daemon with file /etc/docker/daemon.json: invalid JSON: invalid character '}' looking for beginning of object key string
docker.service: Main process exited, code=exited, status=1/FAILURE
docker.service: Failed with result 'exit-code'.
Failed to start docker.service - Docker Application Container Engine.
docker.service: Scheduled restart job, restart counter is at 1.
Starting docker.service - Docker Application Container Engine...
32870 sh -c i=0; while true; do i=$((i+1)); echo "$(date +%s) $i" >> /data/battements; sleep 1; done

Pour restaurer, extrayez par-dessus /etc/docker l'archive prise après l'activation de live-restore, remettez à zéro le compteur d'échecs de systemd, puis démarrez le démon.

Fenêtre de terminal
sudo tar -C / -xzf /root/sauvegarde-docker-avant-maj/etc-docker.tgz
sudo cat /etc/docker/daemon.json
sudo systemctl reset-failed docker
sudo systemctl start docker; echo "start rc=$?"
sleep 3
etat; dernier; empreinte
docker info --format 'liverestore={{.LiveRestoreEnabled}} serveur={{.ServerVersion}}'

Le démon repart (start rc=0) avec live-restore actif. Dans cette mesure, le PID reste 32870, celui d'avant la panne, l'empreinte est la même et le compteur de battements continue (220 au relevé, sans repartir à 1) : la panne de configuration n'a pas touché le service. Lisez ces mêmes lignes sur votre machine, dans cet ordre : StartedAt et Pid inchangés, compteur qui continue, empreinte identique.

L'ordre des sauvegardes compte. L'archive prise au tout début, avant la fusion, contient le daemon.json d'avant live-restore : l'extraire par-dessus un fichier fautif le remplace par ce fichier valide, mais sans live-restore : il faudrait refaire la fusion avant de redémarrer le démon, sans quoi plus rien ne garantit que les conteneurs restés vivants soient repris. La règle en découle : refaites la sauvegarde de /etc/docker après chaque modification de configuration, avant la suivante.

Installez la version précédente avec la même commande que pour monter : levez la retenue des trois paquets, nommez la version, ajoutez --allow-downgrades, reposez la retenue. Avec -y, apt-get refuse de descendre en version sans cette option. Gardez la sauvegarde des paquets (paquets.txt) sous la main : elle donne les noms de version exacts.

Fenêtre de terminal
V=5:29.8.2-1~debian.13~trixie
etat; dernier
sudo apt-mark unhold docker-ce docker-ce-cli docker-ce-rootless-extras
time sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -q --allow-downgrades "docker-ce=$V" "docker-ce-cli=$V" "docker-ce-rootless-extras=$V"
sudo apt-mark hold docker-ce docker-ce-cli docker-ce-rootless-extras
sleep 4
docker version --format 'client={{.Client.Version}} serveur={{.Server.Version}}'
dpkg-query -W -f='${Package} ${Version}\n' docker-ce-rootless-extras
StartedAt=2026-11-04T17:32:05.399836146Z Pid=32870 Restart=0 Status=running
dernier battement : 1793813810 285
Canceled hold on docker-ce.
Canceled hold on docker-ce-cli.
Canceled hold on docker-ce-rootless-extras.
The following packages will be DOWNGRADED:
0 upgraded, 0 newly installed, 3 downgraded, 0 to remove and 4 not upgraded.
real 0m4.829s
[...]
docker-ce set on hold.
docker-ce-cli set on hold.
docker-ce-rootless-extras set on hold.
client=29.8.2 serveur=29.8.2
docker-ce-rootless-extras 5:29.8.2-1~debian.13~trixie

Lisez cette sortie avec attention. apt-get annonce The following packages will be DOWNGRADED pour les trois paquets, et docker version rend 29.8.2 pour le client comme pour le serveur : les paquets sont revenus en arrière et le démon a redémarré. Si vous avez enchaîné plusieurs redémarrages en moins d'une minute, la fin de la sortie est autre : serveur= est vide et docker version répond Cannot connect to the Docker daemon. Le démon n'a pas redémarré, et la section suivante l'explique.

Ce retour ne ramène que les paquets, et la configuration de /etc/docker si vous la restaurez depuis votre archive. Il ne ramène pas l'état des données sur disque : si la version 29.9 a modifié des métadonnées ou si une application a écrit des données entre-temps, le retour ne les annule pas. C'est pourquoi la sauvegarde des volumes, sur un hôte réel, se fait avant la montée.

Que faire quand le démon refuse de repartir juste après le retour ?

Section intitulée « Que faire quand le démon refuse de repartir juste après le retour ? »

Quand systemd a déjà démarré le démon trois fois dans la dernière minute, il refuse un quatrième démarrage : l'état du service passe à failed avec le résultat start-limit-hit. Le journal le dit : Start request repeated too quickly. Il suffit pour cela de quatre démarrages en moins d'une minute : les redémarrages de la leçon, plus celui que provoque une montée ou un retour de version, s'additionnent si vous les enchaînez sans pause.

Pour le voir, provoquez-le : avec live-restore actif, le quatrième redémarrage d'affilée est refusé, et le conteneur continue de tourner.

Fenêtre de terminal
for i in 1 2 3 4; do sudo systemctl restart docker; echo "restart $i rc=$?"; done
restart 1 rc=0
restart 2 rc=0
restart 3 rc=0
Job for docker.service failed because start of the service was attempted too often.
See "systemctl status docker.service" and "journalctl -xeu docker.service" for details.
To force a start use "systemctl reset-failed docker.service"
followed by "systemctl start docker.service" again.
restart 4 rc=1

Cette limite est le réglage de l'unité docker.service mesurée ici, StartLimitBurst=3 sur une fenêtre de 1 minute, et non une règle générale de systemd : une autre unité, ou une autre version du paquet, peut en porter une autre. Le conteneur, lui, survit. Lisez les trois commandes suivantes.

Fenêtre de terminal
systemctl is-active docker
systemctl show docker -p Result
pgrep -af 'while true' | head -2
sudo tail -1 /var/lib/docker/volumes/vt-donnees/_data/battements
failed
Result=start-limit-hit
32870 sh -c i=0; while true; do i=$((i+1)); echo "$(date +%s) $i" >> /data/battements; sleep 1; done
1793813887 362

Le service est failed avec start-limit-hit, mais le processus du témoin (le même PID 32870) tourne toujours et son compteur monte : le conteneur n'a pas été touché, c'est seulement le démon qui n'est pas relancé. Pour repartir, remettez à zéro le compteur d'échecs de systemd, puis démarrez le démon.

Fenêtre de terminal
sudo systemctl reset-failed docker
sudo systemctl start docker; echo "start rc=$?"
sleep 3
docker version --format 'client={{.Client.Version}} serveur={{.Server.Version}}'
etat
sudo tail -1 /var/lib/docker/volumes/vt-donnees/_data/battements
empreinte
start rc=0
client=29.8.2 serveur=29.8.2
StartedAt=2026-11-04T17:32:05.399836146Z Pid=32870 Restart=0 Status=running
1793813891 366
6dd06996a50ab6d3a5d0b0e3ffad7f34ac43e426c8ba40c0df15f2adafdbc060 /var/lib/docker/volumes/vt-donnees/_data/commandes.txt

Le démon repart avec le client et le serveur en 29.8.2. Le PID et StartedAt sont ceux d'avant, le compteur a continué (362 puis 366) et l'empreinte est intacte : le retour s'est fait sans interrompre le service. La parade est donc systemctl reset-failed docker puis systemctl start docker, ou d'attendre une minute entre deux opérations.

Que couvre apt-mark hold, et que laisse-t-il de côté ?

Section intitulée « Que couvre apt-mark hold, et que laisse-t-il de côté ? »

apt-mark hold bloque les paquets nommés à leur version installée : apt-get upgrade ne les touche plus, et apt-get install sur ces paquets refuse tant que la retenue n'est pas levée, comme vous l'avez vu pour la montée. La leçon d'installation retient les six paquets Docker, et c'est ce qu'il faut : le blocage ne couvre que les paquets que vous avez nommés, et rien d'autre.

Vérifiez-le en simulant une mise à jour générale avec l'option -s, qui n'installe rien.

Fenêtre de terminal
sudo apt-get -s upgrade 2>&1 | grep -iE 'kept back|held|docker|^Inst'
The following packages have been kept back:
docker-ce docker-ce-cli docker-ce-rootless-extras docker-compose-plugin
Inst libapt-pkg7.0 [3.0.3] (3.0.4 Debian-Security:13/stable-security [amd64])
Inst apt [3.0.3] (3.0.4 Debian-Security:13/stable-security [amd64]) [apt-utils:amd64 ]
Inst apt-utils [3.0.3] (3.0.4 Debian-Security:13/stable-security [amd64])

Les paquets Docker pour lesquels une version plus récente existe sont tous retenus (kept back), y compris docker-compose-plugin ; seuls des paquets système seraient mis à jour. Sur le même hôte, avec seulement docker-ce, docker-ce-cli et containerd.io retenus, la même simulation annonçait au contraire Inst docker-compose-plugin [5.6.1-1~debian.13~trixie] (5.7.0-1~debian.13~trixie …) : un paquet non nommé n'est pas protégé. L'inventaire du début de la leçon donne la liste complète à retenir.

Une fois les mesures terminées, retirez le témoin. Le daemon.json fusionné (avec live-restore), la retenue des paquets et les sauvegardes de /root restent sur la machine : supprimez-les aussi si vous ne gardez pas cette VM.

Fenêtre de terminal
docker rm -f vt-service
docker volume rm vt-donnees
  • Relevez avant d'intervenir les versions de paquets (les six), docker info (dont LiveRestoreEnabled), apt-mark showhold et daemon.json : cet inventaire est le point de comparaison de toute la suite.
  • Un témoin se compose d'une donnée avec son empreinte sha256 et d'un service qui écrit un battement numéroté par seconde. L'empreinte contrôle la donnée. Pour savoir si le service a été relancé, lisez le PID, StartedAt et le compteur de battements, qui repart à 1.
  • Sans live-restore, un redémarrage du démon relance les conteneurs : nouveau PID, compteur à 1, dont 10 secondes de délai d'arrêt d'un PID 1 qui ignore SIGTERM.
  • Ajoutez live-restore par fusion (jq, copie de sauvegarde, daemon.json.nouveau, dockerd --validate, mv), puis systemctl reload docker. --validate ne voit pas un conflit avec les options de la ligne de commande de l'unité.
  • Les paquets étant retenus, la montée demande apt-mark unhold des trois paquets du moteur, l'installation en version nommée (docker-ce=5:29.9.2-1~debian.13~trixie), puis apt-mark hold. Sans version nommée, apt prend le candidat du dépôt.
  • La documentation Docker ne garantit live-restore qu'entre versions correctives. Le passage de 29.8.2 à 29.9.2 est un fait mesuré, pas une promesse, et un démon arrêté longtemps peut bloquer les conteneurs qui écrivent sur stdout : prévoyez une fenêtre de maintenance.
  • Une sauvegarde ne restaure que ce qu'elle contient : refaites celle de /etc/docker après chaque modification. Le retour arrière ne ramène que les paquets et /etc/docker, pas l'état des données : sauvegardez les volumes avant.
  • Le retour demande --allow-downgrades. Enchaîné juste après une montée, il peut tomber sur start-limit-hit : le conteneur survit, et systemctl reset-failed docker puis systemctl start docker relancent le démon.

Vérifiez que l'essentiel de cette leçon est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn