Stockage Docker : l’astuce de migration de volumes qui évite la perte de données

Cet article vous a aidé ?

La migration de stockage qui fait mal n’est pas celle qui échoue bruyamment. C’est celle qui « a fonctionné » et a discrètement fait disparaître quelques fichiers, quelques permissions, ou quelques millisecondes de durabilité jusqu’à ce que votre base de données comprenne ce qu’est l’entropie.

Les volumes Docker sont censés être ennuyeux. Puis vous les déplacez : nouveau disque, nouvel hôte, nouveau pilote, nouveau nom de stack Compose, ou « juste une reconstruction rapide ». C’est là que survient la perte de données — généralement parce qu’on a traité un volume comme un dossier, ou un dossier comme un volume.

L’astuce : migrer via un montage dans un conteneur contrôlé (pas le système de fichiers hôte)

Voici la méthode qui évite la plupart des désastres de migration de volumes : ne copiez pas « n’importe quoi sous
/var/lib/docker » et ne faites pas confiance à l’idée qu’un chemin de bind mount soit identique à un volume nommé. Au lieu de cela, attachez l’ancien volume et la nouvelle cible (un nouveau volume, un bind mount, ou un tuyau SSH) à un conteneur utilitaire éphémère et copiez à l’intérieur de cet environnement contrôlé.

Pourquoi ça marche : le montage dans le conteneur vous donne une vue stable du contenu du volume exactement comme votre application le voit. Vous ne traversez pas par inadvertance les métadonnées internes de Docker. Vous ne perdez pas de fichiers parce que vous avez copié le mauvais répertoire. Et vous pouvez standardiser le processus avec des commandes reproductibles.

Ma méthode par défaut est en deux phases :

  • Phase 1 (copie à chaud) : gardez l’application en fonctionnement, faites une synchronisation initiale pour réduire le temps d’indisponibilité.
  • Phase 2 (copie de basculement) : arrêtez l’application (ou mettez-la au repos), puis faites une synchronisation finale et vérifiez.

Si vous ne retenez qu’une chose de cet article : traitez la migration comme un déploiement. Vous avez besoin d’un plan, d’un rollback, d’une vérification et d’un basculement propre.

Faits intéressants et courte histoire expliquant les pièges actuels

  1. Les volumes Docker existaient avant Docker Compose. Les volumes ont été le premier mécanisme de persistance « officiel » ; Compose les a ensuite rendus faciles à déclarer, et encore plus faciles à mal comprendre.
  2. Les volumes nommés sont des objets gérés, pas des chemins. Ils se mappent à des chemins sur l’hôte, mais Docker se réserve le droit de choisir où et comment. Copier « le dossier » copie souvent le mauvais dossier.
  3. Les systèmes de fichiers en overlay ne sont pas faits pour stocker l’état. Le overlay2 de Docker est excellent pour les couches d’image et les écritures éphémères de conteneur. Ce n’est pas l’endroit heureux de votre base de données.
  4. Les volumes survivent aux conteneurs par conception. La suppression d’un conteneur ne supprime pas les volumes nommés sauf si vous le faites explicitement. C’est une fonctionnalité de fiabilité — et un piège opérationnel quand les anciens volumes s’accumulent.
  5. Les incompatibilités de permissions se sont aggravées avec Docker rootless. Les moteurs rootless modifient les mappages UID/GID ; une migration peut réussir et pourtant casser à l’exécution avec un « permission denied ».
  6. macOS/Windows Desktop utilise une couche de virtualisation. Sur Desktop, les volumes vivent à l’intérieur d’une VM. « Copier depuis l’hôte » n’est pas ce que vous pensez, et les caractéristiques de performance diffèrent radicalement.
  7. Les pilotes de stockage ont changé de comportements par défaut selon les versions. Ce qui était aufs est devenu overlay2 pour la plupart des distributions Linux ; la sémantique des volumes est restée stable, mais « où sont les données » continue de semer la confusion.
  8. Certaines bases de données considèrent les horodatages comme partie de la justesse. Si votre migration modifie le mtime de façon étrange (ou si vous restaurez avec de mauvaises options), certaines charges voient leurs performances diminuer ou se comportent de façon anormale.
  9. Les systèmes de fichiers réseau restent un champ de mines de compatibilité. Les options NFS, le verrouillage et le comportement de fsync peuvent transformer une migration « réussie » en corruption subtile plus tard.

Modèle mental : ce qu’est (et n’est pas) un volume Docker

Volume nommé vs bind mount vs « contenu dans le conteneur »

Trois catégories de stockage apparaissent dans les incidents réels :

  • Volume nommé : stockage persistant géré par Docker. Le moteur Docker le crée et le monte dans votre conteneur.
    Il vit typiquement sous /var/lib/docker/volumes/<name>/_data sur Linux, mais ne construisez pas d’automatisation qui suppose ce chemin.
  • Bind mount : un chemin hôte monté dans un conteneur. Idéal pour le développement et pour certains cas de production où vous contrôlez la disposition des chemins, les sauvegardes et les permissions.
  • Couche inscriptible du conteneur : le « diff » au-dessus de l’image. Elle disparaît avec le conteneur. Si vous avez des données métier là-dedans, vous n’avez pas de persistance — vous avez des sensations.

Pourquoi copier « /var/lib/docker » est une mauvaise idée

Docker stocke images, couches, cache de build, réseaux, métadonnées de conteneur et volumes sous son data-root. Le copier pendant que Docker tourne peut produire un arbre de répertoires qui a l’air cohérent mais qui est inconsistant en interne. Vous ne le découvrirez que lorsque des conteneurs refuseront de démarrer, ou pire, démarreront avec un état partiel.

Si vous devez déplacer l’ensemble du data-root de Docker, faites-le comme une procédure chirurgicale : arrêtez Docker, copiez, vérifiez, mettez à jour data-root, puis redémarrez Docker. Mais ce n’est pas une « migration de volume ». C’est « déplacer les entrailles du moteur ».

Le principe de fiabilité qui compte

Lorsque vous migrez de l’état, vous déplacez un contrat : contenu, permissions, propriété, attributs étendus, hardlinks, symlinks, et parfois le comportement des fichiers clairsemés. Votre outil doit préserver ce contrat.

Une citation que les équipes d’exploitation réapprennent sans cesse :

Idée paraphrasée — Richard Cook (sûreté des systèmes) : « Le succès cache la complexité du système ; les échecs la révèlent. »

Les blagues sont généralement une mauvaise stratégie de fiabilité, mais en voici une quand même : une migration de volume Docker, c’est comme déplacer un aquarium — vous pouvez le faire vite, ou vous pouvez le faire deux fois.

Guide de diagnostic rapide : trouver le goulot d’étranglement en quelques minutes

Quand une migration est lente ou risquée, vous n’avez pas besoin d’une semaine de benchmarks. Vous avez besoin d’un triage rapide et discipliné.
Vérifiez ceci dans l’ordre :

1) Êtes-vous lié par l’I/O côté source, destination ou réseau ?

  • Exécutez iostat/vmstat sur les deux côtés.
  • Surveillez un await élevé, une util élevée, un faible débit et le paging.
  • Si vous copiez via SSH, vérifiez aussi le CPU ; le chiffrement peut être le goulot d’étranglement.

2) Copiez-vous par accident la mauvaise chose (ou beaucoup trop) ?

  • Confirmez le(s) volume(s) et les points de montage via docker volume inspect et docker inspect.
  • Listez ce qui est réellement dans le volume en utilisant un conteneur utilitaire.
  • Mesurez la taille avec du -x depuis l’intérieur du montage.

3) Avez-vous affaire à une application qui doit être mise au repos ?

  • Les bases de données, files d’attente et tout ce qui a des write-ahead logs ont besoin d’un arrêt total (ou d’un snapshot).
  • Si vous ne pouvez pas l’arrêter, votre « migration » est un projet de réplication. Ne prétendez pas le contraire.

4) Les permissions/propriétés vont-elles vous poser problème après le basculement ?

  • Vérifiez UID/GID à l’intérieur du conteneur en cours d’exécution.
  • Vérifiez si vous utilisez Docker rootless ou des espaces de noms utilisateur.
  • Attendez-vous à des différences SELinux/AppArmor entre hôtes.

5) Vérifiez-vous le résultat avec autre chose que de l’espoir ?

  • Comptez les fichiers, comparez des sommes de contrôle pour un échantillon, et validez la santé au niveau applicatif (pas seulement le statut « Up » du conteneur).
  • Conservez l’ancien volume intact jusqu’à ce que vous ayez parcouru un cycle métier complet.

Tâches pratiques : commandes, sorties et décisions associées

Voici des tâches réelles de production. Chacune inclut : la commande, à quoi ressemble une sortie « normale », et quelle décision prendre.
Utilisez-les comme blocs de construction pour vos propres runbooks.

Tâche 1 : Identifier si vous utilisez un volume nommé ou un bind mount

cr0x@server:~$ docker inspect -f '{{range .Mounts}}{{println .Type .Source "->" .Destination}}{{end}}' app
volume /var/lib/docker/volumes/pgdata/__data -> /var/lib/postgresql/data
bind /srv/app/config -> /etc/app

Ce que cela signifie : Vous avez un volume nommé (pgdata) et un bind mount (/srv/app/config).
Décision : migrez pgdata via une copie volume-à-volume ; migrez les bind mounts via une copie filesystem normale avec règles de propriété explicites.

Tâche 2 : Inspecter les métadonnées du volume (driver, mountpoint)

cr0x@server:~$ docker volume inspect pgdata
[
  {
    "CreatedAt": "2026-01-20T10:22:14Z",
    "Driver": "local",
    "Labels": {},
    "Mountpoint": "/var/lib/docker/volumes/pgdata/_data",
    "Name": "pgdata",
    "Options": {},
    "Scope": "local"
  }
]

Ce que cela signifie : driver local, portée locale. Ce n’est pas un volume de cluster.
Décision : planifiez une copie au niveau hôte (tar/rsync) ou utilisez un conteneur intermédiaire ; n’attendez pas qu’un autre nœud puisse le voir.

Tâche 3 : Confirmer quels conteneurs utilisent le volume

cr0x@server:~$ docker ps --format '{{.Names}} {{.Mounts}}' | grep pgdata
db pgdata

Ce que cela signifie : seul db utilise ce volume.
Décision : vous pouvez programmer une fenêtre d’indisponibilité uniquement pour ce service ; pas de consommateurs cachés.

Tâche 4 : Mesurer la taille du volume depuis un conteneur utilitaire (faire confiance mais vérifier)

cr0x@server:~$ docker run --rm -v pgdata:/v alpine:3.20 sh -lc 'du -sh /v && df -h /v | tail -n +2'
12.4G    /v
/dev/sda2       220G     96G  113G  46% /v

Ce que cela signifie : environ 12.4G de données, le système de fichiers de destination a de la marge.
Décision : vous pouvez utiliser un flux tar unique ; pas besoin de découper, mais prévoyez de l’espace temporaire si vous préparez des archives.

Tâche 5 : Vérifier le type de système de fichiers et les options de montage (performance et exactitude)

cr0x@server:~$ findmnt -no SOURCE,FSTYPE,OPTIONS /var/lib/docker
/dev/sda2 ext4 rw,relatime

Ce que cela signifie : ext4, options standard.
Décision : rsync/tar devrait se comporter de manière prévisible. Si vous voyez NFS/CIFS ici, ralentissez et validez le verrouillage et le comportement de fsync.

Tâche 6 : Arrêter proprement le producteur d’écritures (sinon vous copiez une cible mouvante)

cr0x@server:~$ docker stop -t 60 db
db

Ce que cela signifie : le conteneur s’est arrêté dans le délai imparti.
Décision : procédez à la synchronisation finale. S’il ne s’arrête pas, vous devez diagnostiquer les hooks d’arrêt avant de migrer quoi que ce soit.

Tâche 7 : Copie à chaud entre deux volumes sur le même hôte (rsync)

cr0x@server:~$ docker volume create pgdata_new
pgdata_new
cr0x@server:~$ docker run --rm -i \
  -v pgdata:/from:ro \
  -v pgdata_new:/to \
  alpine:3.20 sh -lc 'apk add --no-cache rsync >/dev/null && rsync -aHAX --numeric-ids --info=stats2 /from/ /to/'
Number of files: 14832 (reg: 12110, dir: 2711, sym: 11)
Number of created files: 14832 (reg: 12110, dir: 2711, sym: 11)
Total file size: 13,274,991,224 bytes
Total transferred file size: 13,274,991,224 bytes
Literal data: 13,274,991,224 bytes
Matched data: 0 bytes
File list size: 0
File list generation time: 0.210 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 13,278,112,901
Total bytes received: 412,220
sent 13,278,112,901 bytes  received 412,220 bytes  34,201,351.55 bytes/sec
total size is 13,274,991,224  speedup is 1.00

Ce que cela signifie : rsync a préservé les attributs (-aHAX), a utilisé des IDs numériques, créé le nombre attendu de fichiers.
Décision : sûr de passer à la vérification. Si « created files » est étonnamment bas, vous avez probablement pointé vers le mauvais chemin source ou le mauvais volume.

Tâche 8 : Vérifier que le nombre de fichiers correspond (contrôle de sanity bon marché)

cr0x@server:~$ docker run --rm -v pgdata:/a -v pgdata_new:/b alpine:3.20 sh -lc 'cd /a && find . | wc -l; cd /b && find . | wc -l'
14865
14865

Ce que cela signifie : les comptes correspondent.
Décision : continuer. S’ils ne correspondent pas, arrêtez. Trouvez ce qui a été ignoré (permissions, fichiers spéciaux, problèmes de montage).

Tâche 9 : Contrôle par échantillons de checksums (détecter la corruption silencieuse)

cr0x@server:~$ docker run --rm -v pgdata:/a -v pgdata_new:/b alpine:3.20 sh -lc '
apk add --no-cache coreutils >/dev/null
cd /a
find . -type f | head -n 200 | while read f; do
  sha256sum "$f"
done | sort -k2 > /tmp/a.sha
cd /b
find . -type f | head -n 200 | while read f; do
  sha256sum "$f"
done | sort -k2 > /tmp/b.sha
diff -u /tmp/a.sha /tmp/b.sha | head
'

Ce que cela signifie : pas de sortie de diff signifie que les échantillons correspondent.
Décision : acceptez la migration avec une confiance plus élevée. Si vous voyez des divergences, suspectez des erreurs disque sous-jacentes, de la RAM défectueuse, ou un outil de copie qui n’a pas préservé le contenu.

Tâche 10 : Basculement d’un service Compose vers le nouveau volume

cr0x@server:~$ docker compose up -d
[+] Running 1/1
 ✔ Container db  Started

Ce que cela signifie : le service est démarré.
Décision : ne célébrez pas encore ; validez la santé au niveau applicatif et les logs. « Started » n’est pas « correct ».

Tâche 11 : Vérifier les logs pour des indices de permissions ou de corruption immédiatement après le basculement

cr0x@server:~$ docker logs --tail=80 db
PostgreSQL Database directory appears to contain a database; Skipping initialization
LOG:  database system was shut down at 2026-02-04 09:12:30 UTC
LOG:  database system is ready to accept connections

Ce que cela signifie : démarrage propre, reconnaît le répertoire de données existant.
Décision : procédez à un test de fumée (smoke test) au niveau applicatif. Si vous voyez « permission denied » ou « invalid checkpoint record », arrêtez et revenez à l’ancien volume.

Tâche 12 : Confirmer que le conteneur voit l’utilisation disque attendue (éviter les surprises de « volume vide »)

cr0x@server:~$ docker exec db sh -lc 'du -sh /var/lib/postgresql/data | cat'
12.4G    /var/lib/postgresql/data

Ce que cela signifie : le nouveau volume est monté et peuplé.
Décision : continuez la surveillance, puis retirez l’ancien volume seulement après une fenêtre de sécurité.

Tâche 13 : Migrer un volume entre hôtes avec un flux tar sur SSH (sans fichier de staging)

cr0x@server:~$ docker run --rm -v pgdata:/from alpine:3.20 sh -lc 'cd /from && tar -cpf - . ' | ssh ops@newhost 'docker volume create pgdata && docker run --rm -v pgdata:/to alpine:3.20 sh -lc "cd /to && tar -xpf -"'
pgdata

Ce que cela signifie : les données ont été streamées directement ; pas d’archive intermédiaire.
Décision : utilisez ceci lorsque vous avez besoin d’un transfert simple avec peu de dépendances. Si vous avez besoin de reprise/relances partielles, préférez rsync.

Tâche 14 : Vérifier le data-root Docker si vous déplacez tout le stockage du moteur

cr0x@server:~$ docker info --format '{{.DockerRootDir}}'
/var/lib/docker

Ce que cela signifie : emplacement du data-root du moteur.
Décision : si votre vrai problème est « le disque est plein », vous devrez peut-être déplacer le data-root ou faire un prune — ne confondez pas cela avec une migration d’un seul volume.

Tâche 15 : Diagnostiquer le driver de stockage et vérifier que vous ne mélangez pas les préoccupations

cr0x@server:~$ docker info --format 'Driver={{.Driver}}'
Driver=overlay2

Ce que cela signifie : overlay2 est le driver de stockage.
Décision : gardez votre état dans des volumes ; ne tentez pas de « migrer une base de données » en copiant les répertoires de couches overlay2.

Tâche 16 : Détecter l’application de SELinux qui pourrait casser l’accès après migration

cr0x@server:~$ getenforce
Enforcing

Ce que cela signifie : SELinux applique des politiques.
Décision : si vous migrez vers un bind mount, vous devrez probablement appliquer les bons labels (par ex., :Z/:z) ou relabeler. Les volumes nommés évitent généralement ce problème, mais les différences d’hôte comptent toujours.

Les schémas de migration sûrs (même hôte, nouveau disque, nouvel hôte)

Schéma A : même hôte, migration volume-à-volume (le vainqueur ennuyeux)

Si vous pouvez garder le moteur Docker sur le même hôte et que vous devez juste déplacer les données vers un autre support, créez un nouveau volume et copiez-y les données. Cela maintient les métadonnées du moteur stables.
La méthode de copie peut être rsync (meilleure pour les synchronisations incrémentales) ou tar (meilleure pour la simplicité).

Utilisez rsync si :

  • Vous voulez une copie à chaud + une copie finale de basculement.
  • Vous prévoyez des relances ou un progrès partiel.
  • Vous voulez des statistiques et un comportement de diff facile.

Utilisez tar si :

  • Vous voulez des dépendances minimales (tar est partout).
  • Vous streammez sur SSH et ne voulez pas de fichiers temporaires.
  • Vous pouvez vous permettre une passe tout-ou-rien.

Un détail que les gens zappent : exécutez votre outil de copie à l’intérieur d’un conteneur afin qu’il voie les montages exactement comme les conteneurs les voient. Cela évite la confusion des chemins hôtes et réduit la dérive « ça marche sur ma machine ».

Schéma B : migrer vers un bind mount (quand vous voulez un contrôle explicite)

C’est ce que vous faites quand vous voulez vos données sous /srv/data/postgres parce que votre système de sauvegarde, surveillance et vos auditeurs connaissent déjà ce chemin.
Cela peut être correct. Cela peut aussi être un festival de permissions.

La voie la plus sûre est :

  1. Créer le répertoire de destination.
  2. Définir la propriété et les permissions pour correspondre à l’UID/GID attendu par le conteneur.
  3. Copier les données avec rsync en préservant les IDs numériques.
  4. Le monter avec des options compatibles SELinux si applicable.

C’est là que Docker rootless complique la vie : l’UID à l’intérieur du conteneur peut ne pas se mapper comme vous le pensez sur l’hôte. Ne faites pas « chmod 777 » en production sauf si votre modèle de menace est « aucun ».

Schéma C : migration entre hôtes (tar stream ou rsync sur SSH)

Entre hôtes, vous choisissez entre simplicité et possibilité de reprise.

  • tar sur SSH est simple et rapide à mettre en place, avec peu d’éléments mobiles.
  • rsync sur SSH est votre ami quand le transfert est volumineux, le réseau instable, ou que vous voulez une synchronisation à chaud puis finale.

Pour rsync entre hôtes sans exposer le chemin du volume de l’hôte, vous pouvez toujours le faire conteneur-à-conteneur en montant le volume dans un conteneur utilitaire et en lançant rsync sortant par SSH. C’est un peu plus de frappe, beaucoup moins de confusion.

Schéma D : déplacer tout le data-root Docker (dernier recours, faites-le correctement)

Parfois l’exigence réelle est « déplacer le stockage Docker hors du disque racine ». Ce n’est pas une migration de volume ; c’est relocaliser la racine de stockage du moteur. La séquence correcte est :
arrêter Docker, copier le data-root en préservant, mettre à jour la config du démon, démarrer Docker, puis valider images/conteneurs/volumes.

Ne faites pas cela pour « corriger » un seul volume applicatif à moins que vous n’aimiez créer des zones d’impact plus larges que nécessaire.

Vérification qui détecte réellement les mauvaises migrations

Les personnes du stockage sont payées pour se méfier des signaux de succès. Un conteneur « Up » signifie que le processus init ne s’est pas encore planté. Cela ne signifie pas que vos données sont correctes.
La vérification a besoin de couches :

Couche 1 : sanity au niveau système de fichiers

  • Vérification de taille : ordre de grandeur attendu, pas des octets exacts.
  • Vérification du nombre de fichiers : détecte rapidement les erreurs de « dossier copié vide ».
  • Vérification permissions/propriété : détecte les mismatches UID/GID et les xattrs manquants.

Couche 2 : échantillonnage au niveau contenu

Les checksums complets sur des volumes multi-téraoctets peuvent être déraisonnables durant une fenêtre de maintenance. Échantillonnez intelligemment :

  • Hachez les fichiers les plus récemment modifiés.
  • Hachez les répertoires critiques connus (WAL, index, métadonnées).
  • Hachez des échantillons aléatoires à travers l’arborescence.

Couche 3 : vérité au niveau applicatif

Pour les bases de données, exécutez une requête de lecture et une requête d’écriture. Pour les stockages d’objets, récupérez et republiez un objet. Pour les files, enfilez et défilez. Validez ce qui importe réellement au business.

Discipline de rollback

Gardez l’ancien volume intact et déconnecté. Ne « nettoyez » rien tant que vous n’êtes pas au-delà du point où des erreurs silencieuses pourraient apparaître. Si le cycle métier est hebdomadaire, « demain » n’est pas suffisant.

Deuxième blague (et dernière) : les sauvegardes sont comme des parachutes — si vous en avez besoin et que vous ne l’avez pas, vous n’en aurez plus besoin après.

Trois mini-récits d’entreprise issus des tranchées du stockage

Mini-récit 1 : l’incident causé par une mauvaise hypothèse

Une entreprise de taille moyenne faisait tourner une API client sur un seul hôte Linux avec Docker Compose. La base de données était dans un volume nommé.
Un ingénieur plateforme a décidé de « simplifier les sauvegardes » en copiant /var/lib/docker/volumes chaque nuit vers un NAS en utilisant un outil de synchronisation générique.

L’hypothèse : un volume est un répertoire, et copier des répertoires équivaut à sauvegarder. Ça a marché pendant des semaines. Le job de sauvegarde rapportait un succès. Le NAS se remplissait progressivement. Tout le monde passait à autre chose.
Puis un problème de disque a forcé une restauration. Ils ont copié le répertoire de retour, redémarré le conteneur de base de données, et la BD est montée avec un sous-ensemble de tables manquant des écritures récentes.

La cause racine était douloureusement normale. La sauvegarde tournait pendant que la base écrivait. L’outil de copie a produit une vue point-in-time qui n’avait jamais existé : certains fichiers d’« avant » et certains d’« après », plus quelques segments partiellement copiés.
Il n’y avait pas de snapshot au niveau base de données, pas de mise au repos, et pas de test de restauration et de vérification. La « sauvegarde » était un tas de fichiers aux noms plausibles.

La réparation a nécessité de reconstruire à partir d’exports logiques qui étaient heureusement encore disponibles pour une partie des données. La leçon n’était pas « ne pas utiliser Docker ».
C’était : ne confondez pas « la copie de fichiers a réussi » avec « les données sont cohérentes ».

Mini-récit 2 : l’optimisation qui a échoué

Une autre organisation exécutait des workloads CI qui produisaient beaucoup d’artifacts. Ils ont migré les caches de build vers un système de fichiers réseau partagé et l’ont monté dans les conteneurs pour réduire l’usure des SSD locaux.
L’argument était séduisant : moins d’upgrades SSD locaux, gestion centralisée, nettoyage plus simple.

Les performances semblaient correctes le premier jour. Puis quelques bugs subtils sont apparus : builds échouant parfois de manière étrange, tests qui expiraient, et une hausse d’erreurs « file not found » qui disparaissaient au rerun.
Les ingénieurs pointaient du doigt des tests instables. Les SREs incriminaient le réseau. Tout le monde avait raison sur un point.

L’échec venait de la sémantique du système de fichiers. Le partage réseau avait des comportements de cache et de verrouillage qui ne correspondaient pas aux hypothèses d’un disque local.
Certains outils attendaient des renommages atomiques et un fsync fiable ; le partage retardait ou réordonnait parfois la visibilité sous charge. Ce n’était pas « en panne ». C’était « différent ».

La réparation a été de garder les artifacts véritablement partagés dans un stockage d’objets (avec des sémantiques de publication explicites), et de garder le scratch par job sur disque local.
L’« optimisation » n’a pas échoué parce que NFS est mauvais ; elle a échoué parce que la charge était sensible à la latence et supposait les garanties d’un système de fichiers local.

Mini-récit 3 : la pratique ennuyeuse qui a sauvé la mise

Une équipe de services financiers avait une règle : tout conteneur stateful doit avoir un plan de basculement écrit, un plan de rollback, et une étape de vérification qui inclut une transaction réelle.
Personne n’aimait la paperasse. Mais cela rendait les migrations reproductibles.

Ils ont dû déplacer un volume Postgres vers un nouvel hôte à cause d’un changement de contrat de maintenance. Le plan de migration utilisait un rsync à chaud pendant que la BD tournait, puis un arrêt programmé, puis un rsync final, puis des vérifications au niveau applicatif.
Le plan incluait aussi « garder l’ancien volume en lecture seule et déconnecté pendant sept jours ».

La nuit du basculement, tout semblait correct. Les logs étaient propres. Les checks de santé étaient verts.
Le lendemain matin, un job de reporting a trouvé une incohérence dans une table dérivée. Pas catastrophique, mais suspect.

Parce qu’ils avaient conservé l’ancien volume, ils ont pu le monter en lecture seule et comparer un petit ensemble de fichiers et d’horodatages.
Ils ont trouvé le problème : un changement de schéma de dernière minute avait eu lieu pendant la fenêtre de rsync à chaud, et le processus de basculement avait raté un fichier annexe créé par un sidecar en dehors du conteneur BD.
Ils ont relancé une synchronisation ciblée de ce répertoire et revérifié. Pas de drame, pas d’indisponibilité prolongée, pas de « nous avons supprimé l’ancien parce que nous étions confiants ».

L’ennuyeux a gagné. Encore.

Erreurs courantes : symptôme → cause racine → correction

1) Symptôme : le nouveau conteneur démarre « à neuf » comme si les données manquaient

Cause racine : vous avez monté un volume vide (nouveau nom) ou vous avez remplacé un volume nommé par un bind mount pointant vers un répertoire vide.

Correction : inspectez les montages avec docker inspect. Confirmez le nom du volume dans Compose. Remplissez la cible correcte, puis redémarrez.

2) Symptôme : « permission denied » après la migration

Cause racine : mismatch UID/GID, problèmes de mapping avec Docker rootless, ou étiquette SELinux incorrecte sur les bind mounts.

Correction : utilisez --numeric-ids avec rsync ; faites correspondre la propriété à l’utilisateur d’exécution du conteneur ; appliquez le bon étiquetage SELinux pour les bind mounts.

3) Symptôme : la base démarre, puis plante avec des erreurs de type corruption

Cause racine : vous avez copié pendant que la BD écrivait, produisant un état disque incohérent.

Correction : arrêtez/mettre au repos la BD pour la synchronisation finale, ou utilisez les outils natifs de sauvegarde/snapshot de la BD. Ne comptez pas sur une simple copie de fichiers d’un datastore actif.

4) Symptôme : la migration est extrêmement lente, le CPU est saturé

Cause racine : overhead du chiffrement SSH ou compression mal paramétrée, surtout sur des instances à faible nombre de cœurs.

Correction : mesurez le CPU sur les deux côtés ; envisagez de désactiver la compression ; choisissez un chiffre plus rapide ou déplacez la copie sur un réseau privé plus rapide. Ou stagiez localement et transférez via un canal meilleur.

5) Symptôme : rsync termine, mais le nombre de fichiers diffère

Cause racine : motifs exclus, erreurs de permissions, fichiers spéciaux ignorés, ou vous avez copié depuis le mauvais chemin de montage.

Correction : relancez rsync en verbose et itemize, vérifiez stderr, exécutez en root dans le conteneur utilitaire si nécessaire, et validez les montages source/target.

6) Symptôme : après le basculement, l’application est « healthy » mais les performances s’effondrent

Cause racine : le stockage de destination a des caractéristiques de latence différentes (HDD vs SSD, stockage réseau vs local, options de montage différentes).

Correction : exécutez un petit benchmark lecture/écriture avant le basculement ; vérifiez le système de fichiers et les options de montage ; pour les bases, confirmez que fsync et le comportement des barrières correspondent aux exigences.

7) Symptôme : les données semblent présentes, mais horodatages/propriétés ont changé de façon inattendue

Cause racine : les options par défaut de l’outil de copie n’ont pas préservé les métadonnées (par ex., oubli de -a), ou vous avez tar sans préserver les permissions.

Correction : utilisez rsync avec -aHAX lorsque c’est approprié ; utilisez tar avec -p et vérifiez le comportement à l’extraction ; validez par des échantillons stat.

8) Symptôme : le conteneur ne peut pas monter le volume après la migration de l’hôte

Cause racine : mismatch de driver de volume ou plugin manquant sur l’hôte de destination (commun avec les drivers tiers).

Correction : confirmez le driver et les options du volume ; installez le même plugin/driver ; si vous migrez d’un volume local vers un volume piloté par un plugin, considérez cela comme une décision d’architecture et non comme une simple copie de fichiers.

Listes de contrôle / plan étape par étape

Checklist A : Pré-vol (avant de toucher aux données)

  1. Inventoriez les montages pour le service : volumes nommés, bind mounts, tmpfs.
  2. Classez la charge : base de données/file d’attente/stockage d’objets vs « fichiers statiques ». Si c’est transactionnel, planifiez la mise au repos ou une sauvegarde native.
  3. Mesurez la taille du volume et confirmez l’espace libre à destination.
  4. Décidez la méthode de copie : rsync pour incrémental, tar pour la simplicité/streaming.
  5. Décidez de la fenêtre d’indisponibilité et du timebox du rollback.
  6. Rédigez les étapes de vérification incluant un contrôle au niveau applicatif.

Checklist B : Synchronisation à chaud (optionnelle mais recommandée)

  1. Créer le volume de destination (ou le répertoire de destination pour un bind mount).
  2. Exécuter rsync de l’ancien vers le nouveau pendant que l’application tourne.
  3. Enregistrer les comptes de fichiers et les tailles approximatives.
  4. Ne rien supprimer pour l’instant.

Checklist C : Synchronisation de basculement (la partie qui évite la perte de données)

  1. Arrêtez proprement l’application (ou mettez les écritures en pause via un mécanisme applicatif).
  2. Exécutez un rsync final (ou une copie tar) pour capturer les derniers changements.
  3. Vérifiez les comptes de fichiers et les checksums d’échantillons.
  4. Mettre à jour la définition Compose/service pour pointer vers le nouveau volume/bind mount.
  5. Démarrez le service et surveillez les logs pendant les premières minutes.
  6. Exécutez un smoke test applicatif (lecture et écriture).

Checklist D : Plan de rollback (rédigez-le avant de commencer)

  1. Si la vérification échoue, arrêtez le service.
  2. Réaffectez les pointeurs vers l’ancien volume.
  3. Démarrez le service, confirmez la santé.
  4. Conservez le volume migré en échec pour analyse ; ne « corrigez » pas en écrasant les preuves.

Checklist E : Hygiène post-basculement (ne le faites pas trop tôt)

  1. Surveillez pendant un cycle métier complet (jobs batch, rapports, sauvegardes).
  2. Ce n’est qu’ensuite que vous archivez ou supprimez l’ancien volume.
  3. Mettez à jour les runbooks pour que la prochaine migration ne soit pas une opération héroïque isolée.

FAQ

1) Quelle est la manière la plus sûre de migrer un volume nommé Docker ?

Arrêtez l’écrivain pour la synchronisation finale, puis copiez de l’ancien vers le nouveau en utilisant un conteneur utilitaire qui monte les deux volumes.
Préférez rsync pour les migrations en deux passes ; vérifiez avec des comptes de fichiers et un contrôle applicatif.

2) Puis-je simplement copier /var/lib/docker/volumes ?

Vous pouvez, mais en général vous ne devriez pas. Il est facile de copier la mauvaise chose, et copier des données en live est incohérent pour les bases.
Si vous devez le faire, arrêtez entièrement Docker et traitez cela comme un déplacement du data-root Docker, pas comme « une copie de volume ».

3) Tar est-il meilleur qu’rsync ?

Tar est plus simple et idéal pour le streaming. Rsync est préférable pour les copies incrémentales, les reprises et une approche chaud+finale.
Pour éviter la perte de données, la clé n’est pas tar vs rsync ; c’est la mise au repos des écritures et la vérification des résultats.

4) Comment migrer des volumes entre hôtes sans connaître les chemins internes de Docker ?

Utilisez un conteneur utilitaire avec le volume monté et streamez tar sur SSH vers un autre conteneur utilitaire qui extrait dans un volume de destination.
Cela vous maintient à l’écart des internes de Docker.

5) Et pour les conteneurs de bases de données — dois-je copier le répertoire de données fichier par fichier ?

Seulement si vous pouvez garantir un état disque cohérent (service arrêté, ou snapshot du système de fichiers avec garanties correctes).
Les outils natifs de sauvegarde/réplication de la BD sont souvent plus sûrs pour zéro/faible indisponibilité, mais c’est un projet plus large qu’une simple « copie ».

6) Pourquoi mon conteneur a-t-il perdu les permissions après être passé à un bind mount ?

Parce que les bind mounts exposent directement la propriété/les labels du système de fichiers hôte. Les volumes nommés ont tendance à être plus simples.
Corrigez en faisant correspondre UID/GID, en préservant les IDs numériques pendant la copie, et en gérant les labels SELinux si en mode enforcing.

7) Puis-je renommer un volume Docker ?

Pas directement. Le « renommage » pratique est : créez un nouveau volume avec le nom souhaité, copiez-y les données, mettez à jour les références, puis supprimez l’ancien volume plus tard.

8) Comment éviter totalement l’indisponibilité ?

Pour des systèmes véritablement stateful, « pas d’indisponibilité » signifie généralement réplication : déployez une nouvelle instance, répliquez/stream les changements, puis basculez le trafic.
Une migration brute de volume est typiquement une opération avec courte indisponibilité sauf si la charge est en lecture seule.

9) Quel est le plus gros signal d’alerte pendant une migration ?

Quand le nouveau service démarre « propre » et initialise un répertoire de données vierge. Cela signifie généralement qu’il n’a pas vu les données migrées du tout.
Arrêtez immédiatement et confirmez les montages.

10) Quand est-il acceptable de supprimer l’ancien volume ?

Après vérification et après avoir passé un cycle métier complet qui permettrait de faire apparaître des problèmes subtils.
Gardez-le plus longtemps si la conformité ou l’analyse judiciaire importent ; le stockage coûte moins cher que la réponse à un incident.

Prochaines étapes réalisables cette semaine

Si vous exécutez des conteneurs stateful en production, faites ces actions pratiques :

  1. Rédigez un runbook de migration utilisant l’approche conteneur utilitaire (monter ancien + nouveau, rsync/tar, vérifier, basculer, rollback). Faites-en la méthode par défaut.
  2. Ajoutez la vérification à votre définition de done : compte de fichiers + checksums échantillonnés + tests applicatifs lecture/écriture.
  3. Identifiez vos volumes (dans Compose ou via des conventions de nommage) pour savoir lesquels sont stateful et lesquels sont des caches jetables.
  4. Planifiez un test de restauration à partir de ce que vous appelez « sauvegarde ». La façon la plus rapide de découvrir qu’elle est factice est un mardi après-midi, pas pendant une panne.
  5. Décidez de votre position sur bind mounts vs volumes nommés et documentez-la. L’indécision mène à trois patterns de persistance et à aucune stratégie de sauvegarde cohérente.

L’« astuce de migration de volumes » n’est pas de la magie. C’est de la discipline : montez les données comme l’application les monte, copiez en préservant les métadonnées, arrêtez l’écrivain pour la passe finale, et vérifiez comme si vous ne vous faisiez pas confiance. Vous ne devriez pas.

← Précédent
Authentification sans mot de passe sur Windows : Plus sûre, ou juste de nouveaux problèmes ?
Suivant →
Pourquoi le ventilateur de votre portable est bruyant après une mise à jour (généralement un pilote)

Laisser un commentaire