Vous avez des sauvegardes. Vous avez même une coche verte dans un tableau de bord. Puis un nœud meurt, l’astreinte lance une restauration,
et soudain la seule chose que vous restaurez, c’est votre respect pour la loi de Murphy.
Docker facilite le déploiement d’applications. Il facilite aussi le fait d’oublier où les données vivent réellement : volumes, montages bind,
secrets, fichiers d’environnement, registres et quelques répertoires « temporaires » qu’un collègue a codés en dur à 2 h du matin.
Un exercice de restauration est un produit, pas un rituel
Une « sauvegarde » est une promesse. Un exercice de restauration est l’endroit où vous remboursez la promesse et prouvez que vous pouvez la tenir sous pression.
Le livrable n’est pas un tarball dans un stockage objet. C’est un processus de récupération répétable avec des bornes temporelles connues.
L’exercice de restauration a un seul rôle : convertir des hypothèses en mesures. Quel est votre RPO (combien de données vous pouvez perdre)
et votre RTO (combien de temps vous pouvez être indisponible) ? Quelles parties sont lentes ? Quelles parties sont fragiles ? Quelles parties nécessitent la mémoire
et la caféine d’une personne spécifique ?
Le résultat le plus précieux d’un exercice est souvent ennuyeux : une liste de fichiers manquants, de permissions incorrectes, de secrets introuvables,
et de surprises du type « on croyait que c’était dans la sauvegarde ». L’ennuyeux, c’est bon. L’ennuyeux, c’est comment vous survivez aux incidents.
Une citation à garder sur votre bureau : L’espoir n’est pas une stratégie.
(attribuée au général Gordon R. Sullivan)
Ce que vous restaurez réellement dans Docker
Docker ne « contient » pas l’état. Il rend simplement l’état plus facile à égarer. Pour les exercices de restauration, traitez votre système comme des couches :
état de l’hôte, état des conteneurs, état des données et état de déploiement. Ensuite décidez ce que vous promettez de restaurer.
1) État des données
- Volumes nommés (gérés par Docker) : généralement sous
/var/lib/docker/volumes. - Montages bind : n’importe où dans le système de fichiers de l’hôte ; souvent pas dans la même politique de sauvegarde que les volumes.
- Stockage externe : NFS, iSCSI, Ceph, EBS, LUN SAN, datasets ZFS, LVM, etc.
- Bases de données : Postgres/MySQL/Redis/Elastic/etc. La méthode de sauvegarde compte plus que l’endroit où elle est stockée.
2) État de déploiement
- Fichiers Compose, fichiers d’environnement et overrides.
- Secrets et leur mécanisme de distribution (secrets Swarm, fichiers, SOPS, modèles Vault, etc.).
- Tags d’images : « latest » n’est pas un plan de restauration.
- Accès au registre : si vous ne pouvez pas pull, vous ne pouvez pas démarrer.
3) État de l’hôte
- Configuration de Docker Engine, driver de stockage, flags du démon.
- Noyau + détails du système de fichiers : attentes overlay2, xfs ftype, SELinux/AppArmor.
- Réseau : règles de firewall, DNS, routes, MTU.
4) État d’exécution (généralement pas utile à « restaurer »)
Les couches de conteneur et les fichiers d’exécution éphémères peuvent être recréés. Si vous sauvegardez tout le répertoire Docker root
(/var/lib/docker) en espérant ressusciter les conteneurs octet par octet, vous vous engagez à rencontrer des cassures subtiles.
La cible correcte est presque toujours les volumes de données plus la configuration de déploiement, et reconstruire proprement les conteneurs.
Blague n°1 : Si votre plan de récupération commence par « je crois que les données sont sur ce nœud-là », félicitations — vous avez inventé un point unique de surprise.
Faits & contexte historique (pour arrêter de répéter les mêmes erreurs)
- Fait 1 : L’époque AUFS de Docker a normalisé l’idée que les conteneurs sont jetables ; beaucoup d’équipes ont par erreur rendu les données jetables aussi.
- Fait 2 : Le passage d’AUFS à overlay2 n’était pas qu’une question de performances : les sémantiques de restauration et les exigences du système de fichiers ont changé (notamment l’attente XFS
ftype=1). - Fait 3 : Le mouvement vers une « infrastructure immuable » a réduit les restaurations d’hôtes mais augmenté le besoin de restaurer l’état externalisé (volumes, stockages objets, bases gérées).
- Fait 4 : Compose est devenu la description d’application par défaut pour beaucoup d’organisations, même lorsque la rigueur opérationnelle (rotation des secrets, versions figées, healthchecks) n’a pas suivi.
- Fait 5 : Beaucoup d’incidents imputés à « Docker » sont en réalité des problèmes de cohérence de stockage : copies de systèmes de fichiers prises sous une base de données active.
- Fait 6 : Le ransomware a fait évoluer la stratégie de sauvegarde de « peut-on restaurer ?» à « peut-on restaurer sans faire confiance à l’attaquant qui aurait chiffré nos clés de sauvegarde ? »
- Fait 7 : Les registres d’images conteneurisées sont devenus une infrastructure critique ; perdre un registre privé ou ses identifiants peut bloquer les restaurations même si les données sont saines.
- Fait 8 : Les snapshots de systèmes de fichiers (LVM/ZFS) ont facilité les sauvegardes rapides — mais ils ont aussi encouragé l’excès de confiance quand les applications n’étaient pas sûres pour les snapshots.
- Fait 9 : L’essor des conteneurs sans privilèges rootless a changé les chemins de sauvegarde et les modèles de permissions ; restaurer des données en root peut discrètement casser des runtimes rootless par la suite.
Choisir la portée de l’exercice : hôte, application ou couche données
Un exercice de restauration peut être trois choses différentes. Si vous ne déclarez pas laquelle vous faites, vous « réussirez » à la plus facile
et échouerez à celle qui importe.
Portée A : Exercice de restauration des données (le plus courant, le plus utile)
Vous restaurez les données des volumes/montages bind et redéployez les conteneurs depuis des images et configurations connues. C’est la bonne valeur par défaut
pour la plupart des déploiements Docker Compose en production.
Portée B : Exercice de restauration de l’application (déploiement + données)
Vous restaurez la pile d’applications exacte : fichiers Compose, env/secrets, reverse proxy, certificats, plus les données. Cela valide l’hypothèse « tout ce qu’il faut pour faire tourner ».
Cela expose aussi la maladie du « on gardait cette conf sur l’ordinateur portable de quelqu’un ».
Portée C : Recréation de l’hôte (rare, mais à faire au moins annuellement)
Vous supposez que le nœud est perdu. Vous fournissez un hôte neuf et restaurez dessus. C’est là que vous découvrez la dépendance à des noyaux anciens, des paquets manquants,
des règles iptables personnalisées, d’étranges hacks MTU et des incompatibilités de drivers de stockage.
Mode opératoire de diagnostic rapide (trouver le goulot vite)
Pendant une restauration, vous êtes typiquement bloqué par l’un des quatre éléments : identité/identifiants, intégrité des données,
vitesse de transfert des données, ou correction de l’application. Ne devinez pas. Triez dans cet ordre.
Premier : Pouvez-vous même accéder à ce dont vous avez besoin ?
- Avez-vous les identifiants du dépôt de sauvegarde et les clés de chiffrement ?
- L’hôte de restauration peut-il atteindre le stockage objet / serveur de sauvegarde / NAS ?
- Pouvez-vous puller les images conteneurs (ou avez-vous un cache air‑gapped) ?
Second : La sauvegarde est-elle complète et cohérente en interne ?
- Avez-vous tous les chemins attendus de volumes/montages bind pour l’application ?
- Les sommes de contrôle correspondent-elles ? Pouvez-vous lister et extraire les fichiers ?
- Pour les bases : avez-vous un backup logique ou seulement une copie crash‑consistante du système de fichiers ?
Troisième : Où passe le temps ?
- Débit réseau (egress du stockage objet, contraintes VPN, throttling) ?
- Décompression et crypto (outils de restauration mono‑thread) ?
- IOPS et tempêtes de petits fichiers (millions de fichiers minuscules) ?
Quatrième : Pourquoi l’application ne démarre-t-elle pas ?
- Permissions/possession/labels SELinux sur les données restaurées.
- Dérive de configuration : variables d’environnement, secrets, tags d’images modifiés.
- Incompatibilité de schéma : restaurer des données anciennes dans une nouvelle version d’application.
Si vous ne retenez qu’une chose : mesurez la vitesse de transfert et vérifiez les clés tôt. Tout le reste est secondaire.
Construire un environnement de restauration réaliste
Un exercice de restauration sur le même hôte qui a produit les sauvegardes est un mensonge réconfortant. Il partage les mêmes images en cache,
les mêmes identifiants déjà connectés et les mêmes règles de firewall ajustées manuellement. Votre objectif est d’échouer honnêtement.
Ce que « réaliste » veut dire
- Hôte neuf : nouvelle VM ou bare metal, même famille d’OS, mêmes versions majeures.
- Même contraintes réseau : même route vers le stockage de sauvegarde, même NAT/VPN, même DNS.
- Pas d’état caché : ne réutilisez pas l’ancien
/var/lib/docker; ne montez pas directement des volumes de production. - Limité dans le temps : vous testez le RTO ; arrêtez d’admirer les logs et démarrez un chronomètre.
Définir les critères de réussite au départ
- RPO validé : vous pouvez pointer sur la sauvegarde la plus récente et en montrer l’horodatage et le contenu.
- RTO mesuré : de « hôte provisionné » à « service répond correctement ».
- Correction vérifiée : pas seulement « les conteneurs tournent » mais « les données sont correctes ».
Tâches pratiques : commandes, sorties, décisions
Voici des tâches d’exercice de restauration que j’attends dans un runbook. Chacune inclut une commande, ce que signifie la sortie et
la décision que vous en tirez. Exécutez‑les sur l’hôte cible de restauration sauf indication contraire.
Task 1: Inventory running containers and their mounts (source environment)
cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
NAMES IMAGE STATUS
api registry.local/api:1.42.0 Up 3 days
postgres postgres:15 Up 3 days
nginx nginx:1.25 Up 3 days
Signification : Ceci est la liste minimale « ce qui existe ». Ce n’est pas suffisant, mais c’est un début.
Décision : Identifiez quels conteneurs sont stateful (ici : postgres) et lesquels sont stateless.
cr0x@server:~$ docker inspect postgres --format '{{range .Mounts}}{{.Type}} {{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
volume pgdata -> /var/lib/postgresql/data
bind /srv/postgres/conf -> /etc/postgresql
Signification : Vous avez à la fois un volume nommé et un montage bind. Deux politiques de sauvegarde, deux modes de défaillance.
Décision : Votre plan de restauration doit capturer à la fois pgdata et /srv/postgres/conf.
Task 2: List Docker volumes and map them to projects
cr0x@server:~$ docker volume ls
DRIVER VOLUME NAME
local myapp_pgdata
local myapp_redisdata
local shared_uploads
Signification : Les noms de volumes encodent souvent les noms de projet Compose. C’est utile lors des restaurations.
Décision : Décidez quels volumes sont critiques et lesquels peuvent être reconstruits (par ex. caches).
Task 3: Identify where volumes live on disk (restore host)
cr0x@server:~$ docker info --format '{{.DockerRootDir}}'
/var/lib/docker
Signification : Répertoire Docker root par défaut. Les volumes seront sous ce chemin sauf configuration différente.
Décision : Confirmez que cela correspond à vos attentes de sauvegarde ; les divergences provoquent des « restauration réussie, données manquantes. »
Task 4: Verify filesystem and free space before restoring
cr0x@server:~$ df -hT /var/lib/docker /srv
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda2 ext4 200G 32G 158G 17% /
/dev/sdb1 xfs 800G 120G 680G 15% /srv
Signification : Vous avez de la marge de capacité. Notez aussi les types de système de fichiers ; certains comportements diffèrent pour overlay et permissions.
Décision : Si l’espace disponible est faible, vous n’« essayez pas quand même ». Redimensionnez d’abord ou choisissez une cible de restauration plus grande.
Task 5: Confirm Docker storage driver and kernel compatibility
cr0x@server:~$ docker info --format 'Driver={{.Driver}}; BackingFS={{.BackingFilesystem}}'
Driver=overlay2; BackingFS=extfs
Signification : overlay2 sur ext4 (Docker rapporte « extfs »). Si votre hôte d’origine utilisait un driver différent, n’assumez pas la portabilité de /var/lib/docker.
Décision : Préférez restaurer uniquement les volumes et la config ; reconstruisez les conteneurs depuis les images.
Task 6: Verify the backup artifact exists and is recent
cr0x@server:~$ ls -lh /backups/myapp/
total 4.1G
-rw------- 1 root root 1.9G Jan 2 01:05 myapp-volumes-2026-01-02.tar.zst
-rw------- 1 root root 2.2G Jan 2 01:06 myapp-bindmounts-2026-01-02.tar.zst
-rw------- 1 root root 12K Jan 2 01:06 myapp-compose-2026-01-02.tgz
Signification : Artifacts séparés pour volumes, bind mounts et config de déploiement : sain. Cela rend les restaurations partielles possibles.
Décision : Si le fichier le plus récent est plus ancien que votre RPO, arrêtez et escaladez. Restaurer des données périmées sans prévenir est la voie royale vers un incident.
Task 7: Validate archive integrity before extraction
cr0x@server:~$ zstd -t /backups/myapp/myapp-volumes-2026-01-02.tar.zst
/backups/myapp/myapp-volumes-2026-01-02.tar.zst: OK
Signification : Le flux compressé n’est pas corrompu.
Décision : Si cela échoue, n’extrayez pas « une partie ». Trouvez un autre jeu de sauvegarde ou refaites la chaîne de sauvegarde.
Task 8: Dry-run list of files inside the backup (spot missing paths)
cr0x@server:~$ tar -I zstd -tf /backups/myapp/myapp-bindmounts-2026-01-02.tar.zst | head
srv/postgres/conf/postgresql.conf
srv/postgres/conf/pg_hba.conf
srv/myapp/env/.env.production
srv/nginx/conf.d/app.conf
Signification : Vous pouvez voir les fichiers de configuration attendus pour les montages bind.
Décision : Si des répertoires clés sont manquants, arrêtez et corrigez la définition de sauvegarde. Les exercices de restauration ne sont pas des tours de magie.
Task 9: Restore bind mounts to a staging prefix first (avoid clobbering)
cr0x@server:~$ mkdir -p /restore-staging
cr0x@server:~$ tar -I zstd -xpf /backups/myapp/myapp-bindmounts-2026-01-02.tar.zst -C /restore-staging
cr0x@server:~$ ls -la /restore-staging/srv/postgres/conf
total 24
drwxr-xr-x 2 root root 4096 Jan 2 01:06 .
drwxr-xr-x 3 root root 4096 Jan 2 01:06 ..
-rw-r--r-- 1 root root 980 Jan 2 01:06 pg_hba.conf
-rw-r--r-- 1 root root 3150 Jan 2 01:06 postgresql.conf
Signification : Fichiers restaurés avec permissions préservées (-p). La propriété sera importante ensuite.
Décision : Comparez le staging à la mise en place cible. Ce n’est qu’ensuite que vous déplacerez en place.
Task 10: Restore named volume data using a helper container
Pour les volumes nommés, ne copiez pas manuellement dans les entrailles de Docker. Utilisez un conteneur temporaire qui monte le volume.
cr0x@server:~$ docker volume create myapp_pgdata
myapp_pgdata
cr0x@server:~$ docker run --rm -v myapp_pgdata:/data -v /backups/myapp:/backup alpine:3.20 sh -c "cd /data && tar -I zstd -xpf /backup/myapp-volumes-2026-01-02.tar.zst --strip-components=2 ./volumes/myapp_pgdata"
tar: removing leading './' from member names
Signification : Vous extrayez seulement le sous‑arbre pour ce volume dans le chemin monté du volume.
Décision : Si la disposition de l’archive ne correspond pas à vos attentes, arrêtez et revérifiez le script de sauvegarde ; n’improvisez pas des restaurations partielles.
Task 11: Sanity-check restored volume contents and ownership
cr0x@server:~$ docker run --rm -v myapp_pgdata:/data alpine:3.20 sh -c "ls -la /data | head"
total 128
drwx------ 19 999 999 4096 Jan 2 01:04 .
drwxr-xr-x 1 root root 4096 Jan 2 02:10 ..
-rw------- 1 999 999 3 Jan 2 01:04 PG_VERSION
drwx------ 5 999 999 4096 Jan 2 01:04 base
Signification : La propriété est 999:999, typique de l’image officielle Postgres. Bien.
Décision : Si la propriété est incorrecte (par ex. root), corrigez‑la maintenant (chown) ou Postgres peut refuser de démarrer.
Task 12: Restore deployment config and pin image versions
cr0x@server:~$ mkdir -p /opt/myapp
cr0x@server:~$ tar -xpf /backups/myapp/myapp-compose-2026-01-02.tgz -C /opt/myapp
cr0x@server:~$ ls -la /opt/myapp
total 40
drwxr-xr-x 3 root root 4096 Jan 2 02:13 .
drwxr-xr-x 3 root root 4096 Jan 2 02:13 ..
-rw-r--r-- 1 root root 2241 Jan 2 01:06 docker-compose.yml
-rw------- 1 root root 412 Jan 2 01:06 .env.production
Signification : La configuration est présente, y compris le fichier env. Traitez‑la comme sensible.
Décision : Assurez‑vous que les images sont figées sur des tags ou des digests de confiance. Si le fichier Compose utilise latest, corrigez‑le dans le cadre de l’exercice.
Task 13: Validate images can be pulled (or are already available)
cr0x@server:~$ docker compose -f /opt/myapp/docker-compose.yml pull
[+] Pulling 3/3
✔ postgres Pulled
✔ api Pulled
✔ nginx Pulled
Signification : Votre chemin de registre, vos identifiants et le réseau fonctionnent.
Décision : Si les pulls échouent, votre plan de restauration doit inclure un miroir de registre, des tarballs d’images hors‑ligne ou des étapes de récupération d’identifiants.
Task 14: Bring the stack up and watch for fast failures
cr0x@server:~$ docker compose -f /opt/myapp/docker-compose.yml up -d
[+] Running 3/3
✔ Container myapp-postgres-1 Started
✔ Container myapp-api-1 Started
✔ Container myapp-nginx-1 Started
Signification : Les conteneurs ont démarré, mais ce n’est pas une preuve de correction.
Décision : Vérifiez immédiatement les logs et les endpoints de santé. « Démarré » peut encore vouloir dire « cassé silencieusement ».
Task 15: Verify health and read the first errors, not the last ones
cr0x@server:~$ docker compose -f /opt/myapp/docker-compose.yml ps
NAME IMAGE COMMAND SERVICE STATUS PORTS
myapp-api-1 registry.local/api:1.42.0 "gunicorn -c ..." api Up 20s (healthy) 0.0.0.0:8080->8080/tcp
myapp-nginx-1 nginx:1.25 "/docker-entrypoint…" nginx Up 20s 0.0.0.0:80->80/tcp
myapp-postgres-1 postgres:15 "docker-entrypoint…" postgres Up 20s (healthy) 5432/tcp
Signification : Les healthchecks passent. C’est un signal fort, pas une garantie.
Décision : Si la santé échoue, vérifiez les logs pertinents les plus anciens (DB d’abord, puis l’app, puis le proxy).
cr0x@server:~$ docker logs --tail=50 myapp-postgres-1
2026-01-02 02:14:12.101 UTC [1] LOG: database system is ready to accept connections
Signification : Postgres est prêt à accepter des connexions. Si vous voyez « invalid checkpoint record » ou « permission denied », votre restauration n’est pas correcte.
Décision : Pour les erreurs DB, décidez si vous avez besoin d’une restauration logique plutôt qu’une copie du système de fichiers.
Task 16: Prove correctness with an application-level query
cr0x@server:~$ curl -fsS http://127.0.0.1:8080/health
{"status":"ok","db":"ok","version":"1.42.0"}
Signification : Votre application déclare qu’elle est saine. Validez maintenant les données, pas seulement la vivacité.
Décision : Exécutez une requête connue ou un contrôle métier (par ex. « un client spécifique existe »).
cr0x@server:~$ docker exec -i myapp-postgres-1 psql -U postgres -tAc "select now(), count(*) from users;"
2026-01-02 02:14:35.812396+00|1842
Signification : Vous avez des données, et elles semblent plausibles.
Décision : Comparez avec une plage attendue ou un rapport de checksum. Si le compte est zéro, vous avez restauré le mauvais élément ou pointé vers un volume vide.
Task 17: Measure restore throughput to predict RTO
cr0x@server:~$ /usr/bin/time -f 'elapsed=%E cpu=%P' tar -I zstd -xpf /backups/myapp/myapp-volumes-2026-01-02.tar.zst -C /restore-staging-voltest
elapsed=0:02:41 cpu=380%
Signification : Le CPU est un goulot (380% indique ~4 cœurs occupés). Bien à savoir.
Décision : Si la restauration est liée au CPU, vous pouvez réduire le RTO en ajoutant des cœurs, en ajustant le niveau de compression ou en changeant d’outil — avant le vrai incident.
Task 18: Verify file permissions and SELinux/AppArmor context (where applicable)
cr0x@server:~$ getenforce
Enforcing
Signification : SELinux est activé. Les montages bind peuvent nécessiter des labels, sinon les conteneurs auront « permission denied » malgré des perms UNIX corrects.
Décision : Si vous utilisez SELinux, assurez‑vous que votre Compose utilise :z ou :Z là où il faut, et relabellez les chemins restaurés.
cr0x@server:~$ ls -lZ /srv/postgres/conf | head -n 3
-rw-r--r--. root root unconfined_u:object_r:default_t:s0 pg_hba.conf
-rw-r--r--. root root unconfined_u:object_r:default_t:s0 postgresql.conf
Signification : Le type SELinux par défaut sur les fichiers de conf peut ne pas correspondre à ce que votre conteneur attend.
Décision : Si les conteneurs ne peuvent pas lire les montages bind sous SELinux, vous devez relabeler ou ajuster les options de montage ; ne désactivez pas SELinux comme « solution ».
Task 19: Confirm the restore didn’t silently swap volumes
cr0x@server:~$ docker inspect myapp-postgres-1 --format '{{range .Mounts}}{{.Name}} {{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
myapp_pgdata /var/lib/docker/volumes/myapp_pgdata/_data -> /var/lib/postgresql/data
Signification : Le conteneur utilise le volume attendu.
Décision : Si vous voyez un nom de volume inattendu, vous restaurez dans un volume mais vous exécutez contre un autre. C’est une erreur classique d’exercice.
Task 20: Capture evidence and timings (so the drill improves)
cr0x@server:~$ journalctl -u docker --since "today" | tail -n 5
Jan 02 02:13:55 server dockerd[1023]: API listen on /run/docker.sock
Jan 02 02:14:03 server dockerd[1023]: Loading containers: done.
Signification : Vous avez des horodatages pour le démarrage du démon Docker et le chargement des conteneurs.
Décision : Enregistrez-les dans le rapport d’exercice avec les heures de début/fin. Si vous ne mesurez pas, vous vous disputerez pendant l’incident au lieu d’agir.
Blague n°2 : Un exercice de restauration, c’est comme passer la soie dentaire — tout le monde prétend le faire, et les preuves saignent généralement.
Trois mini-histoires d’entreprise (comment ça échoue dans la réalité)
Mini‑histoire 1 : L’incident causé par une mauvaise hypothèse
Une entreprise SaaS de taille moyenne utilisait Docker Compose sur quelques VM costauds. Leurs sauvegardes étaient « simples » : un tar nocturne de
/srv plus un snapshot hebdomadaire du disque de la VM. L’hypothèse était que tout d’important vivait dans /srv.
L’incident commença par une défaillance de stockage banale. La VM ne démarrait plus proprement après un incident d’hôte. L’équipe provisionna
une nouvelle VM et restaura /srv depuis la sauvegarde nocturne. Compose démarra. Nginx servait les pages. L’API renvoyait des 500.
Les logs Postgres montraient un nouveau cluster de base initialisé vide. Personne ne l’avait restauré — parce que personne ne l’avait sauvegardé. La DB utilisait
un volume Docker nommé, situé dans la racine Docker sous /var/lib/docker/volumes, en dehors du périmètre de sauvegarde. Le snapshot hebdomadaire de la VM le contenait,
mais il était trop ancien pour le RPO implicite de l’entreprise, et il était géré par une autre équipe.
Le postmortem n’était pas spectaculaire. C’était pire : c’était évident. Ils avaient confondu « le répertoire de données de notre appli » avec « l’endroit où Docker stocke l’état ».
La correction n’était pas sophistiquée non plus : inventorier les montages, sauvegarder explicitement les volumes nommés et exécuter un exercice trimestriel sur un hôte propre.
Aussi : arrêtez d’appeler « sauvegardes simples » ce qui n’inclut pas votre base de données.
Mini‑histoire 2 : L’optimisation qui s’est retournée contre eux
Une autre organisation se mit sérieusement à optimiser la vitesse. Leur temps de restauration était trop lent pour la patience de la direction, alors ils optimisèrent.
Ils passèrent des dumps logiques de BD aux snapshots crash‑consistants du volume de base de données. C’était plus rapide et produisait des transferts incrémentaux plus petits. Tout le monde était content.
Six mois plus tard, ils eurent besoin de restaurer. Un mauvais déploiement corrompit l’état applicatif et ils roulèrent en arrière. La restauration « fonctionnait »
mécaniquement : le snapshot s’extrayait, les conteneurs démarraient, les healthchecks devenaient verts. Puis le trafic monta, et la BD commença à lancer des erreurs :
corruption subtile d’index, bizarreries du planificateur de requêtes, puis boucle de crash.
La cause racine était plate mais mortelle : le snapshot avait été pris alors que la base subissait des écritures, sans coordonner un checkpoint ni utiliser un mécanisme de backup natif DB.
La sauvegarde du volume était cohérente au niveau système de fichiers, pas nécessairement au niveau base de données. Elle a restauré vite et échoué tard — exactement le genre d’échec qui fait perdre le plus de temps.
La correction fut un compromis : conserver des snapshots rapides pour les récupérations « oups » à court terme, mais aussi prendre périodiquement des backups natifs DB (ou exécuter la procédure de base backup supportée)
pouvant être validés. Ils ajoutèrent aussi un job de vérification qui démarre une DB restaurée en bac à sable et exécute des checks d’intégrité. Les optimisations sont permises. Les optimisations non vérifiées sont juste des risques déguisés en performance.
Mini‑histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une entreprise proche des finances faisait tourner plusieurs services clients dans Docker. Leur lead SRE n’était pas romantique. Tous les trimestres, ils exécutaient
un exercice de restauration utilisant un VPC isolé, une image VM propre et une copie du dépôt de sauvegarde. L’exercice avait une checklist et un chronomètre.
L’exercice incluait toujours les mêmes étapes fastidieuses : vérifier que les clés de chiffrement sont accessibles à l’astreinte, valider les manifests de sauvegarde, restaurer les volumes
en staging d’abord, puis les remplacer en place, puis exécuter une série de contrôles de sanity applicatifs. Enfin, documenter les temps et mettre à jour le runbook.
Personne n’aimait ça. Personne ne le mettait sur une diapositive.
Puis un incident réel arriva : une erreur opérateur effaça un volume de production et la réplication se fit rapidement. L’astreinte suivit le runbook sans improviser.
Ils savaient déjà que l’étape la plus lente était la décompression et avaient ajusté la taille de l’hôte de restauration pour cela. Ils savaient déjà exactement quels secrets
devaient être présents et où ils étaient. Ils avaient déjà traité le problème d’étiquetage SELinux — dans l’exercice, pas pendant l’incident.
La restauration se termina dans la fenêtre attendue. Pas parce que l’équipe fut héroïque, mais parce qu’elle avait choisi l’ennui volontairement. En exploitation, l’ennuyeux est une fonctionnalité.
Listes de contrôle / plan étape par étape
Plan d’exercice de restauration (répétable, pas « voyons ce qui se passe »)
-
Déclarez la portée et les critères de réussite.
- Quels services ? Quels jeux de données ? Quels RPO/RTO validez‑vous ?
- Que signifie « correct » (requêtes, checksums, actions UI, compteurs de messages) ?
-
Gelez l’inventaire.
- Exportez les fichiers Compose et les références env/secrets.
- Listez les volumes et montages bind par conteneur.
- Enregistrez les références d’images (tags ou digests).
-
Provisionnez une cible de restauration fraîche.
- Même famille d’OS, CPU/mémoire similaires, mêmes choix de système de fichiers.
- Même chemin réseau vers les sauvegardes et registres (ou explicitement différent si vous testez une région DR).
-
Récupérez les artifacts de sauvegarde et validez l’intégrité.
- Checksum, déchiffrez, listez le contenu, vérifiez les horodatages.
- Confirmez que vous avez les clés et mots de passe dans le modèle d’accès attendu pendant un incident.
-
Restaurez d’abord en staging.
- Montages bind dans
/restore-staging. - Volumes via conteneurs helpers dans des volumes nouvellement créés.
- Montages bind dans
-
Appliquez permissions, labels et propriétés.
- Les volumes DB doivent correspondre aux attentes UID/GID du conteneur.
- SELinux/AppArmor : assurez les labels et options de montage corrects.
-
Démarrez la stack avec des images connues bonnes.
- Pull des images ; si le pull échoue, utilisez des images en cache/hors‑ligne.
- Démarrez DB d’abord, puis l’app, puis les proxies edge.
-
Vérifiez la correction.
- Endpoint de santé + au moins une requête de données par service critique.
- Pour files/caches : vérifiez que vous n’avez pas besoin de les restaurer (souvent non).
-
Mesurez les temps et rédigez le rapport d’exercice.
- Début/fin de restauration, débit de transfert, goulots, échecs, corrections.
- Mettez à jour le runbook et automatisez les étapes fragiles.
Ce qu’il faut automatiser après votre premier exercice honnête
- Export d’inventaire : montages, volumes, images, configs Compose.
- Génération de manifestes de sauvegarde : chemins et volumes attendus, tailles, horodatages.
- Vérifications d’intégrité : checksums, tests d’archive, restauration périodique en sandbox.
- Normalisation des permissions : mapping UID/GID connu par service.
- Rétention d’images : conservez les images requises pour votre fenêtre RPO (ou exportez des tars).
Erreurs courantes : symptôme → cause racine → correction
1) « Les conteneurs sont démarrés, mais l’application est vide »
Symptôme : Les healthchecks passent, mais les données utilisateurs sont manquantes ou réinitialisées aux valeurs par défaut.
Cause racine : Restauration dans le mauvais nom de volume, ou Compose a créé un nouveau volume vide à cause d’un mismatch de nom de projet.
Correction : Inspectez les montages (docker inspect), assurez‑vous que les noms de volumes correspondent, et nommez explicitement les volumes dans Compose plutôt que de compter sur le scope implicite du projet.
2) « Permission denied » sur des montages bind restaurés
Symptôme : Les conteneurs plantent avec des erreurs d’accès aux fichiers ; les fichiers semblent corrects sur l’hôte.
Cause racine : Labels SELinux incorrects, ou un conteneur rootless attend une propriété différente de celle produite par la restauration.
Correction : Utilisez les options de montage :z/:Z
3) Postgres/MySQL démarre puis se comporte étrangement sous charge
Symptôme : La BD démarre, puis vous voyez des erreurs de type corruption ou des crashes plus tard.
Cause racine : Sauvegarde crash‑consistante prise sans coordination avec la BD ; état WAL/checkpoint incohérent.
Correction : Préférez les méthodes de backup natives de la BD pour des restaurations durables ; si vous utilisez des snapshots, coordonnez‑les avec le mode de backup supporté par la BD et validez dans un sandbox.
4) La restauration est « lente sans raison »
Symptôme : Heures de restauration, CPU saturé, disques sous‑utilisés.
Cause racine : Décompression/chiffrement mono‑thread ou niveau de compression trop élevé ; millions de petits fichiers amplifiant les opérations métadonnées.
Correction : Benchmarquez la décompression, envisagez une compression moins lourde ou des outils parallèles, et restructurez les sauvegardes (par ex. archives par volume) pour réduire la thrash métadonnées.
5) Vous ne pouvez pas puller les images pendant la restauration
Symptôme : Auth registre échoue, DNS échoue, ou les images ont disparu.
Cause racine : Identifiants stockés uniquement sur l’ancien hôte ; rétention du registre ayant garbage‑collecté des tags dont vous dépendez ; dépendance aux limites de rate des registres publics.
Correction : Stockez les identifiants de registre dans un gestionnaire de secrets récupérable, figez par digest ou tags immutables, et conservez un cache/offline des images critiques.
6) Compose « marche en prod » mais échoue sur l’hôte de restauration
Symptôme : Même fichier Compose, comportement différent : ports, DNS, réseaux, problèmes MTU.
Cause racine : Configuration hôte cachée et divergente : sysctls, iptables, modules noyau, daemon.json personnalisé, ou réseau cloud spécifique.
Correction : Codifiez le provisionnement hôte (IaC), exportez et versionnez les réglages du démon, et incluez un exercice de restauration sur hôte neuf annuellement.
7) La sauvegarde est présente, mais pas les clés
Symptôme : Vous voyez le fichier de sauvegarde mais ne pouvez pas le déchiffrer ou y accéder pendant la réponse à l’incident.
Cause racine : Clés/mots de passe verrouillés derrière une personne, un laptop mort, ou un chemin SSO cassé.
Correction : Entraînez la récupération de clés pendant les exercices, stockez l’accès break‑glass correctement, et vérifiez la procédure avec un rôle d’astreinte à moindre privilège.
8) Vous avez restauré la config mais pas les dépendances ennuyeuses
Symptôme : L’app démarre mais ne peut pas envoyer d’e-mails, atteindre le prestataire de paiement, ou les callbacks échouent.
Cause racine : Certificats TLS manquants, règles de firewall, enregistrements DNS, secrets webhook, ou listes d’autorisation sortantes.
Correction : Traitez les dépendances externes comme partie de l’« état de déploiement » et testez‑les dans l’exercice (ou simulez explicitement et documentez).
FAQ
1) Dois‑je sauvegarder /var/lib/docker ?
Généralement non. Sauvegardez les volumes et tous les répertoires applicatifs montés en bind, plus la configuration Compose et les références aux secrets.
Sauvegarder tout le répertoire Docker root est fragile entre versions, drivers de stockage et différences d’hôte.
2) Quelle est la façon la plus sûre de sauvegarder une base de données dans Docker ?
Utilisez le mécanisme de sauvegarde supporté par la base (dumps logiques, base backups, archivage WAL, etc.) et validez en restaurant dans un sandbox.
Les backups au niveau système de fichiers peuvent fonctionner s’ils sont correctement coordonnés, mais « ça avait l’air OK une fois » n’est pas une méthode.
3) À quelle fréquence devrais‑je exécuter des exercices de restauration ?
Trimestriels pour les systèmes critiques est un bon seuil. Mensuels si le système change constamment ou si RTO/RPO sont serrés.
Exécutez aussi un exercice après des changements majeurs : migration de stockage, mise à jour de Docker, mise à jour de la DB, ou changement d’outil de sauvegarde.
4) Puis‑je faire un exercice de restauration sans dupliquer les données de production (problèmes de vie privée) ?
Oui : utilisez des jeux de données masqués, des fixtures synthétiques, ou restaurez dans un environnement isolé chiffré avec contrôles d’accès stricts.
Mais vous devez quand même restaurer une structure réaliste : permissions, tailles, nombre de fichiers, schéma et comportement d’exécution.
5) Quelle est la chose n°1 qui fait exploser le temps de restauration ?
Les petits fichiers et les arbres à métadonnées lourdes, surtout combinés avec chiffrement et compression. Vous pouvez avoir beaucoup de bande passante
et être bloqué par le CPU ou les IOPS.
6) Dois‑je compresser les sauvegardes ?
Généralement oui, mais choisissez une compression adaptée à vos contraintes de restauration. Si vous êtes lié au CPU pendant la restauration, une forte compression
nuit au RTO. Mesurez‑le avec une extraction chronométrée pendant les exercices et ajustez.
7) Comment savoir si j’ai restauré la bonne chose ?
Ne faites pas confiance au statut des conteneurs. Utilisez des contrôles applicatifs : exécutez des requêtes DB, vérifiez des comptes, validez un client connu,
ou exécutez une transaction métier en lecture seule. Automatisez ces vérifications dans l’exercice.
8) Dois‑je restaurer Redis ou d’autres caches ?
Typiquement non — les caches sont reconstruisables et les restaurer peut réintroduire un état incorrect. Mais vous devez confirmer que l’application tolère un cache vide
et que la configuration du cache (mots de passe, TLS, politiques maxmemory) est sauvegardée.
9) Qu’en est‑il des secrets dans les variables d’environnement ?
Si votre production dépend d’un fichier env, ce fichier fait partie de l’état de déploiement et doit être récupérable. Mieux : migrez les secrets vers un gestionnaire de secrets
ou l’équivalent Docker secrets, et incluez la récupération break‑glass dans l’exercice.
10) Puis‑je faire cela avec Docker Compose et rester « enterprise‑grade » ?
Oui, si vous traitez Compose comme un artefact avec versioning, images figées, restaurations testées et gestion d’état disciplinée.
« Enterprise‑grade » est un comportement, pas un choix d’outil.
Conclusion : prochaines étapes réalisables cette semaine
Si vous ne faites qu’une chose, planifiez un exercice de restauration sur un hôte neuf et chronométrez‑le. Pas en production, pas sur votre portable, pas « un de ces jours ».
Mettez‑le au calendrier et invitez qui gère les sauvegardes, le stockage et l’application. Vous voulez tous les modes de défaillance dans la même salle.
Ensuite faites ces étapes, dans l’ordre :
- Inventoriez les montages pour chaque conteneur stateful et notez les chemins et noms de volumes faisant autorité.
- Séparez les artifacts en données (volumes), montages bind et configuration de déploiement pour restaurer de façon chirurgicale.
- Validez l’intégrité du jeu de sauvegarde le plus récent et prouvez que vous avez les clés pour le déchiffrer avec les permissions d’astreinte.
- Restaurez dans un sandbox et exécutez des vérifications applicatives, pas seulement « le conteneur tourne ».
- Mesurez le RTO, identifiez l’étape la plus lente, et corrigez cette chose avant d’optimiser autre chose.
Les sauvegardes que vous n’avez jamais restaurées ne sont pas des sauvegardes. Ce sont de l’optimisme compressé. Exécutez l’exercice, notez ce qui a cassé, et rendez‑le ennuyeux.