Introduction
Les services avec état tels que PostgreSQL, MySQL, Redis et les magasins de fichiers nécessitent un stockage durable qui survit aux conteneurs. Docker propose deux options principales :
- Volumes : gérés par Docker. Portables, valeurs par défaut sûres et comportement cohérent sur Linux, macOS et Windows.
- Bind mounts : chemins d'hôte directs. Idéaux pour le développement local et l'itération rapide ; plus risqués en production sans contrôles stricts (drapeau read-only, chemins d'hôte fixes, provisionnement automatisé).
Ce guide montre comment choisir entre eux, déclarer le stockage dans Docker Compose, définir les bonnes permissions de fichiers, effectuer des sauvegardes et restaurations fiables, tester localement et éviter les pièges courants en production. Il s'adresse aux opérateurs qui ont besoin d'étapes claires et d'exemples copiables.
Concepts clés en 3 minutes
- Volume nommé : stockage géré par Docker identifié par un nom. Se trouve sous le répertoire de données de Docker (par exemple
/var/lib/docker/volumes/sur Linux). - Bind mount : associe un chemin d'hôte précis au conteneur. Vous contrôlez entièrement le chemin et son contenu.
- tmpfs : montage en mémoire qui disparaît au redémarrage ; utile pour les secrets ou les caches éphémères.
- Frontière de persistance : ensemble des répertoires dont les données doivent être conservées lors des mises à niveau et redéploiements (par exemple
/var/lib/postgresql/data).
Quand utiliser chacun :
- Privilégiez les volumes pour les bases de données et les services qui doivent s'exécuter de manière cohérente sur différents OS et machines.
- Utilisez les bind mounts en développement local lorsque vous souhaitez modifier des fichiers sur l'hôte et voir les changements instantanément dans le conteneur.
Aperçu du flux de travail
- Identifier les répertoires de données
- Consultez la documentation de l'image ou le Dockerfile pour trouver le chemin de données (ex.
/var/lib/postgresql/datapour PostgreSQL,/datapour Redis,/var/lib/mysqlpour MySQL).
- Choisir le type de stockage
- Par défaut en production : volumes nommés.
- En développement local : bind mounts pour le code source ou les assets statiques ; volumes pour les données de base de données.
- Déclarer dans Compose
- Utilisez
volumes:au niveau supérieur pour définir les volumes nommés. - Utilisez
volumes:au niveau du service pour les monter aux bons chemins.
- Définir l'utilisateur du conteneur et les permissions
- Alignez les UID/GID entre hôte et conteneur ou changez le propriétaire des répertoires de données au build ou à l'exécution. Pour l'utilisateur par défaut de PostgreSQL (UID 999), exécutez
sudo chown -R 999:999 ./datasur l'hôte avant le montage.
- Démarrer les services et écrire un enregistrement de test
- Vérifiez que les données survivent aux redémarrages de conteneur et aux mises à jour d'image.
- Sauvegarder et restaurer
- Entraînez-vous aux sauvegardes au niveau volume (archives tar) et aux exports logiques de base de données avant d'en avoir besoin. Conservez les scripts en contrôle de version.
- Surveiller et maintenir
- Suivez l'espace libre avec
df -h /var/lib/docker, supprimez les volumes inutilisés en toute sécurité viadocker volume ls -qf dangling=true | xargs -r docker volume rm, et planifiez la rotation des sauvegardes.
Exemples Compose
Exemple : PostgreSQL avec un volume nommé
version: "3.9"
services:
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- db_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb" ]
interval: 10s
timeout: 5s
retries: 5
volumes:
db_data: {}
Exécution :
docker compose up -d
Confirmer la persistance :
docker exec -it $(docker compose ps -q db) psql -U app -d appdb -c "CREATE TABLE t(x int); INSERT INTO t VALUES (1); SELECT * FROM t;"
docker compose restart db
# Relancer le SELECT pour confirmer que la ligne est toujours présente
Exemple : Nginx servant des fichiers statiques locaux via bind mount (dev uniquement)
version: "3.9"
services:
web:
image: nginx:1.27-alpine
volumes:
- ./public:/usr/share/nginx/html:ro
ports:
- "8080:80"
Cela donne un retour instantané lors de l'édition des fichiers sur l'hôte. En production, utilisez des volumes ou une image artefact pour éviter la dérive de chemin et les surprises de permissions.
Exemple Dockerfile pour permissions sûres
Exécutez les conteneurs en utilisateur non-root et assurez-vous que les répertoires de données appartiennent à cet utilisateur.
FROM postgres:16
# Exemple de pattern : créer un utilisateur non-root avec UID/GID stables
# (De nombreuses images officielles de BD tournent déjà avec un utilisateur dédié.)
# Ce qui suit est un modèle générique si vous avez besoin d'une image personnalisée.
RUN groupadd -g 1001 app && useradd -u 1001 -g 1001 -m app
# Préparer un répertoire de données d'application et définir le propriétaire
RUN mkdir -p /data && chown -R app:app /data
USER app
# L'image de base de données peut toujours gérer /var/lib/postgresql/data ; ajustez si nécessaire
Notes :
- Maintenez UID/GID stables across les environnements pour éviter les erreurs "Permission denied" sur les bind mounts.
- Si vous devez exécuter en root pour l'initialisation, passez à un utilisateur non-root avec
USERpour l'opération normale.
Permissions et propriétaires de fichiers
Problèmes courants et correctifs :
- Symptôme : Permission denied sur un bind mount.
- Vérifiez l'UID/GID du processus conteneur :
docker exec -it <cid> id. - Sur l'hôte, définissez la propriété pour qu'elle corresponde :
sudo chown -R 1001:1001 ./data. - Envisagez les ACL POSIX lorsque plusieurs utilisateurs ont besoin d'accès :
setfacl -m u:1001:rwx ./data. - SELinux (RHEL, Fedora, CentOS) : ajoutez
:Z(privé) ou:z(partagé) aux bind mounts, ex../data:/var/lib/app:Z. - Partage de fichiers Windows et macOS : assurez-vous que le chemin est autorisé dans les paramètres Docker Desktop (Resources > File Sharing).
Sauvegardes et restaurations
Les sauvegardes doivent être scriptées et testées. Utilisez des outils conscients de la base de données quand c'est possible.
Sauvegarder un volume nommé avec tar
Créer une archive de sauvegarde d'un volume Docker :
# Sauvegarder le volume db_data dans backups/db_data_$(date +%F).tgz
mkdir -p backups
V=backups/db_data_$(date +%F).tgz
docker run --rm \
-v db_data:/data:ro \
-v "$(pwd)"):/backup \
alpine:3.19 sh -c "tar -czf /backup/$(basename \"$V\\( -C /data ."
ls -lh "$V"
Restaurer vers un volume neuf :
# Arrêter le service et créer un volume propre
docker compose down
docker volume rm db_data || true
docker volume create db_data
# Restaurer depuis l'archive
docker run --rm \
-v db_data:/data \
-v "$(pwd)"):/backup \
alpine:3.19 sh -c "tar -xzf /backup/db_data_YYYY-MM-DD.tgz -C /data"
# Redémarrer les services
docker compose up -d
Sauvegarder un bind mount avec tar
# En supposant que ./data est bind-monté dans le conteneur
mkdir -p backups
tar -czf backups/data_$(date +%F).tgz -C ./data .
Sauvegardes logiques de base de données (instantanés cohérents)
Exemple PostgreSQL :
# Exporter schéma et données
CID=$(docker compose ps -q db)
docker exec -T "$CID" pg_dump -U app -d appdb -F c -f /tmp/appdb.dump
# Copier vers l'hôte
docker cp "$CID":/tmp/appdb.dump ./backups/appdb_$(date +%F).dump
Restauration :
CID=$(docker compose ps -q db)
# Créer une base vide si nécessaire
docker exec -T "$CID" dropdb -U app --if-exists appdb
docker exec -T "$CID" createdb -U app appdb
# Restaurer depuis le dump
cat ./backups/appdb_YYYY-MM-DD.dump | docker exec -i "$CID" pg_restore -U app -d appdb --clean
Exemple MySQL :
CID=$(docker compose ps -q db)
docker exec -T "$CID" mysqldump -uapp -psecret appdb > ./backups/mysql_appdb_$(date +%F).sql
# Restauration
cat ./backups/mysql_appdb_YYYY-MM-DD.sql | docker exec -i "$CID" mysql -uapp -psecret appdb
Conseils :
- Privilégiez les exports logiques pour les bases de données afin d'assurer la cohérence sans arrêter le service.
- Pour les magasins de fichiers, utilisez
rsync -av --checksum --exclude='*.tmp' ./data/ /backup/data/.
Plan pilote local
Objectif : prouver la persistance de bout en bout, la sauvegarde et la restauration avec un risque minimal.
Étendue :
- Un conteneur de base de données (PostgreSQL), un volume nommé, une table avec une ligne de test, une archive de sauvegarde, une restauration dans un volume propre.
Étapes :
- Démarrer les services
docker compose up -d
- Écrire des données de test
CID=$(docker compose ps -q db)
docker exec -it "$CID" psql -U app -d appdb -c "CREATE TABLE pilot(k text); INSERT INTO pilot VALUES ('ok');"
- Vérifier la persistance au redémarrage
docker compose restart db
docker exec -it "$CID" psql -U app -d appdb -c "SELECT * FROM pilot;"
- Créer une sauvegarde de volume
mkdir -p backups
V=backups/db_data_$(date +%F).tgz
docker run --rm -v db_data:/data:ro -v "$(pwd)"):/backup alpine:3.19 \
sh -c "tar -czf /backup/$(basename \"$V\\( -C /data ."
- Simuler un sinistre et restaurer
docker compose down
docker volume rm db_data || true
docker volume create db_data
docker run --rm -v db_data:/data -v "$(pwd)"):/backup alpine:3.19 \
sh -c "tar -xzf /backup/$(basename \"$V\\( -C /data"
docker compose up -d
CID=$(docker compose ps -q db)
docker exec -it "$CID" psql -U app -d appdb -c "SELECT * FROM pilot;"
- Documenter les résultats et conserver les commandes comme scripts dans votre dépôt.
Critères de réussite :
- Données présentes après redémarrage et après restauration.
- Aucune erreur de permission dans les journaux du conteneur.
Conseils de production et pièges
- Ne montez pas en bind des chemins d'hôte arbitraires en production. Privilégiez les volumes nommés pour la portabilité et des permissions prévisibles.
- Fixez les utilisateurs des conteneurs. Utilisez un UID/GID stable (ex. 999 pour PostgreSQL, 999 pour Redis) et évitez d'exécuter le processus principal en root.
- Utilisez des montages read-only quand c'est possible. Par exemple, montez les fichiers de configuration en
:roet n'accordez:rwqu'aux répertoires de données. - Considérations cross-OS : les bind mounts se comportent différemment sur Linux, macOS et Windows. Les volumes évitent la plupart des surprises.
- SELinux : rappelez-vous
:Zou:zpour les bind mounts sur les hôtes SELinux. - Performance sur macOS/Windows : les bind mounts peuvent être plus lents. Envisagez les volumes ou des outils de synchronisation dédiés comme Mutagen.
- Sauvegardes : pratiquez à la fois les sauvegardes au niveau volume (tar) et les exports logiques de base de données. Conservez au moins une copie hors hôte.
- Capacité : surveillez l'utilisation du disque. Nettoyez les volumes inutilisés avec précaution :
docker volume ls,docker volume rm, etdocker volume prune(revoyez avant de purger).
Référence rapide de dépannage
- Permission denied au démarrage :
- Vérifiez l'UID du conteneur :
docker exec -it <cid> id. - Corrigez la propriété sur l'hôte :
sudo chown -R <uid>:<gid> <chemin-hôte>. - Vérifiez les options de montage, les drapeaux SELinux et read-only vs read-write.
- Les données n'ont pas persisté :
- Confirmez que le chemin de montage correspond au répertoire de données de l'application.
docker inspect <cid>et examinez la section Mounts.- Dans Compose, vérifiez que le service utilise le volume nommé prévu, pas un volume anonyme.
- Confusion de noms de volumes :
- Listez les volumes :
docker volume ls. - Inspectez :
docker volume inspect <nom>pour voir le point de montage et les consommateurs.
- Problèmes de chemins Windows :
- Utilisez des chemins absolus pour les bind mounts (ex.
C:/Users/nom/projet/data) et assurez-vous que le lecteur est partagé dans Docker Desktop.
Commandes Docker pratiques
- Créer et inspecter des volumes :
docker volume create mydata
docker volume inspect mydata
- Exécuter avec un volume nommé :
docker run -d --name app -v mydata:/var/lib/app myimage:tag
- Exécuter avec un bind mount (dev) :
docker run -d --name web -v "$(pwd)/public:/usr/share/nginx/html:ro" -p 8080:80 nginx:alpine
- Copier un fichier ponctuel depuis un conteneur (pas une stratégie de sauvegarde) :
docker cp <cid>:/path/in/container ./local/path
Conclusion
Pour les services avec état, privilégiez les volumes nommés en production, utilisez les bind mounts de manière sélective en développement, et exécutez toujours avec des utilisateurs explicites et des permissions correctes. Déclarez clairement les volumes dans Compose, vérifiez la persistance localement, et scriptez les sauvegardes et restaurations avant le déploiement. Commencez par un pilote étroit que vous pouvez inspecter de bout en bout, puis étendez à d'autres services une fois le flux de travail éprouvé.