tva
← Insights

Une mise à niveau majeure de Docker Engine sur un serveur de production actif

Mettre à niveau Docker Engine sur un serveur qui exécute activement plus d'une centaine de conteneurs n'est pas une tâche qui récompense l'improvisation. Lorsqu'est venu le moment de passer de Docker Engine 28 à 29 et Docker Compose v2 à v5 sur notre serveur de production Hetzner, nous l'avons traité avec la même rigueur que nous appliquerions à une migration de base de données : une checklist de préparation écrite, un chemin de rollback testé, une séquence d'exécution précise et une phase de vérification post-mise à niveau avant de déclarer la fenêtre de maintenance fermée.

Ce qui suit est un compte rendu complet de notre approche — le raisonnement derrière chaque étape, les commandes que nous avons réellement exécutées, et ce qu'il faut surveiller lorsque votre nombre de conteneurs est suffisamment élevé pour qu'une politique de redémarrage mal configurée puisse se propager en cascade vers une récupération difficile.

Pourquoi cette mise à niveau méritait un runbook formel

Docker Engine 29 a introduit des modifications au chemin d'intégration containerd et a ajusté la façon dont docker compose gère la résolution des dépendances dans les blocs depends_on avec condition: service_healthy. Docker Compose v5, quant à lui, est une réécriture interne significative — passant du binaire v2 basé sur Go à une nouvelle architecture qui résout plusieurs problèmes de longue date liés à l'ordonnancement du démarrage parallèle, mais qui déprécie également une poignée de directives du fichier Compose que v2 ignorait silencieusement.

Sur un serveur où un seul docker-compose.yml peut gouverner douze services interdépendants — bases de données, proxies inverses, conteneurs d'application, workers en arrière-plan — un changement comportemental silencieux dans la résolution des dépendances est exactement le type de problème qui ne se manifeste pas immédiatement. Il se manifeste à 03h00 quand un healthcheck ne se déclenche jamais et qu'un conteneur dépendant boucle indéfiniment.

La réponse pratique est la préparation, et non la prudence pour elle-même.

Checklist de préparation

Avant de toucher un seul paquet, nous avons travaillé sur la checklist suivante. Chaque élément a une raison d'être.

1. Inventorier tous les conteneurs en cours d'exécution et leurs politiques de redémarrage.

docker ps --format "table {{.Names}}	{{.Status}}	{{.Image}}" | sort
docker inspect $(docker ps -q) --format '{{.Name}} restart={{.HostConfig.RestartPolicy.Name}}' | sort

Cela fournit une base de référence. Les conteneurs avec restart: always tenteront de redémarrer automatiquement après le redémarrage du démon Docker — ce qui est souhaitable, mais seulement si les fichiers compose sous-jacents et les images sont dans un état connu et correct. Tout conteneur en boucle de crash au moment de la mise à niveau le sera toujours après ; mieux vaut le savoir maintenant.

2. Valider tous les fichiers compose par rapport au schéma v5 avant la mise à niveau.

docker compose config --quiet 2>&1 | grep -i warning

Exécutez ceci dans chaque répertoire contenant un docker-compose.yml. Compose v5 est plus strict concernant les clés dépréciées — notamment la clé version: en haut des fichiers compose (désormais ignorée mais génère un avertissement), et toute utilisation de la directive dépréciée links:. Les avertissements dans v5 peuvent devenir des erreurs dans les versions suivantes ; il vaut mieux les résoudre maintenant.

3. Confirmer l'espace disque disponible.

df -h /var/lib/docker
docker system df

La mise à niveau téléchargera un nouveau shim containerd et remplacera le binaire Docker Engine. L'ancien binaire et ses dépendances ne sont pas toujours nettoyés automatiquement. Sur un serveur qui tourne depuis un an, docker system df révèle souvent plusieurs gigaoctets de couches d'images récupérables. Nettoyez-les avant la mise à niveau, pas pendant.

docker system prune -f --volumes

Utilisez --volumes uniquement si vous êtes certain qu'aucun volume anonyme ne contient de données dont vous avez besoin. Sur notre serveur, toutes les données persistantes sont montées depuis des volumes nommés et explicitement déclarés — c'était donc sûr.

4. Exporter la version actuelle de Docker Engine et de compose dans un fichier de référence.

docker version > /root/docker-pre-upgrade.txt
docker compose version >> /root/docker-pre-upgrade.txt
docker ps -a >> /root/docker-pre-upgrade.txt

Cela prend dix secondes et fournit une référence de rollback sans ambiguïté si quelque chose tourne mal et que vous devez identifier quels conteneurs tournaient au moment de la mise à niveau.

5. Notifier les systèmes en aval.

Notre serveur exécute une pile Supabase auto-hébergée et plusieurs instances d'automatisation de workflow n8n. Tout webhook qui se déclenche pendant un cycle de redémarrage de conteneur échouera silencieusement côté émetteur. Nous avons défini une fenêtre de maintenance dans notre outil de surveillance de disponibilité et désactivé les webhooks entrants dans n8n pour la durée.

Le plan de rollback

Un plan de rollback n'est utile que si vous avez décidé à l'avance quelle condition le déclenche. Le nôtre était simple : si un conteneur qui tournait avant la mise à niveau ne tourne pas dix minutes après sa complétion, et ne peut pas être récupéré avec un docker compose up -d, nous revenons au binaire Docker Engine précédent.

Revenir à Docker Engine sur un système Debian signifie épingler la version précédente du paquet. Nous avons noté la chaîne de version exacte avant de commencer :

apt-cache policy docker-ce docker-ce-cli containerd.io docker-compose-plugin

Sortie sur notre serveur avant la mise à niveau :

docker-ce:
  Installed: 5:28.1.1-1~debian.12~bookworm
  Candidate: 5:29.0.1-1~debian.12~bookworm

docker-compose-plugin:
  Installed: 2.35.1-1~debian.12~bookworm
  Candidate: 2.36.0-1~debian.12~bookworm

La commande de rollback, si nécessaire :

apt-get install -y   docker-ce=5:28.1.1-1~debian.12~bookworm   docker-ce-cli=5:28.1.1-1~debian.12~bookworm   containerd.io   docker-compose-plugin=2.35.1-1~debian.12~bookworm

Nous avons conservé cette commande dans un fichier texte sur le serveur à /root/docker-rollback.sh avec les permissions d'exécution, prête à être lancée sans avoir à taper sous pression.

Le processus de mise à niveau

La mise à niveau proprement dite est simple une fois la préparation terminée. La séquence est importante : mettre à jour l'index des paquets en premier, vérifier ce qui sera installé, puis installer.

Étape 1 : Mettre à jour l'index apt uniquement pour le dépôt Docker.

apt-get update -o Dir::Etc::sourcelist="sources.list.d/docker.list"   -o Dir::Etc::sourceparts="-"   -o APT::Get::List-Cleanup="0"

Cela restreint la mise à jour au dépôt Docker plutôt que de rafraîchir toutes les sources. Sur un serveur de production, une mise à jour de paquet non intentionnelle depuis une source non liée pendant une fenêtre de maintenance Docker est une variable dont vous n'avez pas besoin.

Étape 2 : Confirmer les versions candidates.

apt-cache policy docker-ce docker-ce-cli containerd.io docker-compose-plugin

Vérifiez que le candidat correspond à ce que vous attendez avant de procéder. Si le candidat affiche une version plus récente que celle testée en staging, faites une pause et évaluez.

Étape 3 : Installer.

apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

Cela arrêtera le démon Docker, remplacera les binaires et redémarrera le démon. Les conteneurs avec restart: always seront relancés par le démon au redémarrage. La fenêtre pendant laquelle les conteneurs ne tournent pas est généralement inférieure à trente secondes sur un serveur de taille raisonnable — bien que cela varie selon le temps que prend containerd à s'initialiser.

Surveillez le redémarrage dans un second terminal :

watch -n 2 'docker ps --format "table {{.Names}}	{{.Status}}" | sort'

Étape 4 : Vérifier les versions installées.

docker version
docker compose version

Sur notre serveur, la sortie a confirmé :

Docker Engine: 29.0.1
Docker Compose: v2.36.0

Notez que Docker Compose v5 est distribué sous la version 2.36.x du paquet docker-compose-plugin — la désignation « v5 » fait référence à la version de réécriture interne, pas au semver du paquet. Cela cause une certaine confusion dans la documentation ; la version du paquet est ce que l'index apt suit.

Vérification post-mise à niveau

Dix minutes après la mise à niveau, nous avons effectué un passage de vérification structuré. Ce n'est pas optionnel — le redémarrage du démon relancera les conteneurs, mais il ne garantit pas que les conteneurs qui dépendent de healthchecks les ont réellement passés.

Vérifier que tous les conteneurs sont dans l'état attendu.

docker ps -a --format "table {{.Names}}	{{.Status}}	{{.RunningFor}}" | sort

Tout conteneur affichant Restarting ou Exited nécessite une investigation immédiate. Sur notre serveur, un conteneur — un service d'analytique Supabase — affichait Restarting (1). Il était déjà en boucle de crash à basse fréquence avant la mise à niveau ; la mise à niveau ne l'a pas causé, mais il est apparu plus visiblement dans le scan post-mise à niveau. C'est exactement le type de problème préexistant que l'inventaire pré-mise à niveau existe pour détecter.

Vérifier que les healthchecks passent.

docker inspect $(docker ps -q)   --format '{{.Name}} health={{.State.Health.Status}}'   2>/dev/null | grep -v "health=<no value>" | sort

Les conteneurs sans healthchecks explicites afficheront <no value> — filtrez-les. Ceux qui comptent sont ceux dont dépendent vos autres conteneurs via condition: service_healthy.

Exécuter un passage de validation de config compose sur toutes les stacks.

for dir in /opt/*/; do
  if [ -f "${dir}docker-compose.yml" ]; then
    echo "--- $dir ---"
    docker compose -f "${dir}docker-compose.yml" config --quiet 2>&1
  fi
done

Cela détecte toutes les directives de fichier compose que Compose v5 traite maintenant comme des avertissements ou des erreurs. Sur notre serveur, trois stacks ont émis l'avertissement de dépréciation de la clé version. Ce sont des avertissements cosmétiques mais qui méritent d'être résolus lors du prochain cycle de maintenance.

Vérifier les logs du démon Docker pour les erreurs.

journalctl -u docker --since "1 hour ago" | grep -i -E "error|warn|fatal"

Une mise à niveau propre ne produit aucune erreur dans le log du démon. Les avertissements concernant la configuration dépréciée méritent d'être notés mais pas traités immédiatement.

Confirmer la connectivité réseau entre les conteneurs.

docker network ls
docker network inspect bridge --format '{{range .Containers}}{{.Name}} {{end}}'

Docker Engine 29 n'a pas modifié le comportement du pilote réseau par défaut, mais il vaut la peine de confirmer que les réseaux bridge personnalisés ont toujours les appartenances de conteneurs attendues après le redémarrage du démon.

Ce que nous avons observé

La mise à niveau s'est terminée en moins de deux minutes. Tous les conteneurs qui tournaient avant la mise à niveau tournaient après. Le changement de Docker Compose v5 que nous avons remarqué le plus immédiatement était un format de sortie --dry-run plus clair — la nouvelle version produit une sortie structurée et colorée qui facilite la révision de ce qu'un docker compose up fera réellement avant de l'exécuter.

L'ordonnancement des healthchecks depends_on s'est comporté de manière identique à v2 sur nos stacks. C'était attendu — nous avions validé les fichiers compose au préalable — mais c'était rassurant de le confirmer.

Un changement substantiel : Compose v5 n'ignore plus silencieusement le champ container_name en combinaison avec scale. Nous avions une stack qui utilisait les deux — un vestige d'avant notre passage aux réplicas appropriées. Compose v5 a émis une erreur claire sur docker compose up pour cette stack, là où v2 avait silencieusement ignoré le conflit. La correction était une modification d'une ligne pour supprimer le champ container_name redondant.

Insights connexes

Articles connexes