## Introduction Les volumes nommés Docker sont la méthode recommandée pour faire persister les données générées par les conteneurs. Contrairement aux montages de liaison (bind mounts), les volumes nommés sont entièrement gérés par Docker et ne dépendent pas d'une structure de répertoires hôte spécifique. Lorsqu'un conteneur écrit dans un volume nommé, ces données survivent aux redémarrages du conteneur, à sa recréation et même à sa suppression. Pourtant, les volumes échouent encore en production pour des raisons prévisibles : un service écrit dans la couche du conteneur au lieu du point de montage, les permissions sont incorrectes à l'intérieur du conteneur, un volume est attaché au mauvais conteneur, ou des volumes orphelins consomment de l'espace disque jusqu'à ce qu'un déploiement échoue. Ce guide présente une démarche structurée de dépannage des volumes nommés Docker. Vous apprendrez à faire un inventaire en lecture seule de votre environnement, à inspecter les volumes et les conteneurs pour confirmer où les données résident réellement, à interpréter les messages d'erreur courants, à récupérer des pannes de volume les plus fréquentes, et à terminer avec une liste de vérification opérationnelle qui réduit le risque de répétition d'incident. Chaque section comprend des commandes concrètes et la sortie attendue afin que vous puissiez vérifier chaque étape avant de passer à la suivante. ## Inventaire des versions et de l'environnement Avant de modifier quoi que ce soit, collectez l'état actuel de Docker, du plugin Compose, et des conteneurs ou volumes impliqués. Commencez par les bases : ```bash docker version --format '{{.Server.Version}}' docker compose version ``` Sortie attendue : ``` 24.0.7 Docker Compose version v2.23.0 ``` Les versions exactes sont importantes car le comportement des volumes, la prise en charge des pilotes et les options de commande changent entre les versions. Si vous utilisez un pilote de volume tel que local-persist ou un plugin spécifique au cloud, vérifiez que le pilote est installé et actif : ```bash docker plugin ls ``` Ensuite, listez les conteneurs en cours d'exécution et leur statut : ```bash docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" ``` Exemple de sortie : ``` NAMES STATUS PORTS web Up 2 hours 0.0.0.0:8080->80/tcp db Up 20 minutes 5432/tcp ``` Pour un service en panne, le premier diagnostic en lecture seule est le journal du conteneur. Examinez les 100 dernières lignes et recherchez les erreurs de permission, les erreurs de fichier manquant ou les messages de pilote : ```bash docker logs --tail 100 db ``` Si les journaux ne révèlent pas le problème, inspectez les points de montage du conteneur et son état de santé : ```bash docker inspect db --format '{{json .Mounts}}' | jq ``` Sortie attendue (tronquée pour la lisibilité) : ```json [ { "Type": "volume", "Name": "postgres_data", "Source": "/var/lib/docker/volumes/postgres_data/_data", "Destination": "/var/lib/postgresql/data", "Driver": "local", "Mode": "z", "RW": true, "Propagation": "" } ] ``` Cela vous indique le nom du volume, son chemin hôte réel, la destination dans le conteneur, et s'il est monté en lecture-écriture. Dans un projet Compose, utilisez des commandes équivalentes : ```bash docker compose ps docker compose logs --tail=100 db ``` Lorsque des données sont impliquées, confirmez où les fichiers sont stockés avant de recréer un conteneur. Un volume nommé tel que `postgres_data:/var/lib/postgresql/data` est géré par Docker et réutilisé lors des reconstructions de conteneur. Un montage de liaison tel que `./data:/var/lib/postgresql/data` mappe directement un dossier hôte. Les montages de liaison sont pratiques pour le développement, mais ils peuvent échouer sur une autre machine si le chemin absolu diffère ou si l'utilisateur hôte manque de permissions. Les volumes nommés évitent ce type de problème. Pour vérifier la persistance dans un environnement local, effectuez un test de redémarrage contrôlé. Pour un conteneur nommé `db` : ```bash docker stop db docker rm db docker run -d --name db -v postgres_data:/var/lib/postgresql/data postgres:16 ``` Après le démarrage du nouveau conteneur, vérifiez qu'un fichier précédemment créé existe toujours à l'intérieur du conteneur : ```bash docker exec db ls /var/lib/postgresql/data/ ``` Si le fichier est absent, le conteneur d'origine écrivait probablement à un chemin en dehors du volume monté, comme `/var/lib/postgresql` au lieu de `/var/lib/postgresql/data`, ou le volume n'a jamais été monté. ## Chemin de configuration sûr Les modifications de configuration des volumes doivent être petites, versionnées et réversibles. Le chemin le plus sûr est de définir les volumes dans un fichier `docker-compose.yml` plutôt que de s'appuyer sur des options `docker run -v` ad hoc, car les fichiers Compose sont plus faciles à auditer et à reproduire. Commencez avec une configuration Compose minimale qui nomme explicitement le volume : ```yaml services: db: image: postgres:16 volumes: - postgres_data:/var/lib/postgresql/data volumes: postgres_data: ``` Exécutez `docker compose config` pour valider le fichier et afficher la configuration fusionnée finale : ```bash docker compose config ``` La sortie attendue inclut la déclaration du volume et le montage. Si vous voyez un avertissement concernant un volume non défini, le YAML comporte une coquille ou le volume n'est pas déclaré sous la clé de premier niveau `volumes:`. Lorsque vous devez modifier le montage, ne modifiez pas un conteneur en direct. À la place : 1. Arrêtez le service : ```bash docker compose stop db ``` 2. Sauvegardez les données du volume existant dans une archive tar sur l'hôte : ```bash docker run --rm -v postgres_data:/data -v $(pwd):/backup alpine tar czf /backup/postgres_data_backup.tar.gz -C /data . ``` Cette commande monte le volume sur `/data` et le répertoire hôte courant sur `/backup`, puis crée une archive compressée. Vérifiez que le fichier de sauvegarde existe et n'est pas de taille nulle : ```bash ls -lh postgres_data_backup.tar.gz ``` Sortie attendue : ``` -rw-r--r-- 1 user user 52M Feb 20 10:00 postgres_data_backup.tar.gz ``` 3. Mettez à jour le fichier Compose, puis recréez le service : ```bash docker compose up -d db ``` 4. Confirmez que le service est sain et peut accéder aux données : ```bash docker compose exec db ls /var/lib/postgresql/data ``` Si les données sont manquantes ou les permissions incorrectes, revenez à la version Compose précédente et restaurez la sauvegarde dans un nouveau volume : ```bash docker volume create postgres_data_restored docker run --rm -v postgres_data_restored:/data -v $(pwd):/backup alpine sh -c "cd /data && tar xzf /backup/postgres_data_backup.tar.gz" ``` Puis pointez le service vers `postgres_data_restored` et redémarrez-le. Cette procédure de restauration n'est possible que si la sauvegarde a été créée avant le changement ; faites de la sauvegarde une étape non négociable pour toute modification de volume. Un piège courant est de changer le chemin du conteneur sans mettre à jour la configuration de l'application. Par exemple, déplacer le répertoire de données Postgres de `/var/lib/postgresql/data` vers `/var/lib/postgresql/16/main` nécessite de définir la variable d'environnement `PGDATA` en conséquence : ```yaml services: db: image: postgres:16 environment: PGDATA: /var/lib/postgresql/16/main volumes: - postgres_data:/var/lib/postgresql/16/main ``` Sans ce chemin explicite, Postgres recherche les données dans son emplacement par défaut et échoue à démarrer, signalant « No data directory! » dans les journaux. ## Vérification et diagnostic Lorsqu'un conteneur signale une erreur liée au stockage, commencez par le volume lui-même. Listez tous les volumes et leurs pilotes : ```bash docker volume ls ``` Sortie attendue : ``` DRIVER VOLUME NAME local postgres_data local app_uploads ``` Inspectez un volume spécifique pour voir son point de montage, sa date de création et ses étiquettes : ```bash docker volume inspect postgres_data ``` Exemple de sortie : ```json [ { "CreatedAt": "2025-02-20T10:00:00Z", "Driver": "local", "Labels": { "com.docker.compose.project": "myapp", "com.docker.compose.volume": "postgres_data" }, "Mountpoint": "/var/lib/docker/volumes/postgres_data/_data", "Name": "postgres_data", "Options": null, "Scope": "local" } ] ``` Le point de montage révèle le répertoire hôte où les données sont stockées. Vous pouvez inspecter le contenu directement, mais soyez prudent : modifier des fichiers pendant que le conteneur fonctionne peut corrompre les données. Pour une vérification en lecture seule, utilisez un conteneur temporaire : ```bash docker run --rm -v postgres_data:/data:ro alpine ls -la /data ``` La sortie attendue montre les fichiers et leur propriété à l'intérieur du volume. Les erreurs de permission apparaissent souvent ici sous forme de `?????` pour l'utilisateur et le groupe, indiquant que l'UID du conteneur ne correspond pas au propriétaire du fichier. Pour comprendre le décalage de propriété de fichier, obtenez l'UID sous lequel le conteneur s'exécute : ```bash docker inspect --format '{{.Config.User}}' db ``` Si la sortie est vide, le conteneur s'exécute en tant que root (UID 0). Si les fichiers du volume appartiennent à l'UID 999, un utilisateur non-root du conteneur ne peut pas y écrire. La correction consiste soit à exécuter le conteneur avec un utilisateur correspondant, soit à modifier la propriété des données du volume à l'aide d'un conteneur temporaire : ```bash docker run --rm -v postgres_data:/data alpine chown -R 999:999 /data ``` Après cette commande, redémarrez le conteneur de base de données et vérifiez les journaux pour un démarrage réussi : ```bash docker restart db docker logs --tail 50 db ``` Les lignes de journal attendues incluent : ``` PostgreSQL init process complete; ready for start up. ... database system is ready to accept connections ``` Si le service échoue toujours, activez la journalisation de débogage si disponible. Pour Postgres, définissez `log_statement=all` et `log_min_messages=debug1` dans l'environnement du conteneur ou le fichier de configuration, puis reproduisez l'opération d'écriture et examinez les journaux. Les journaux de débogage montrent souvent le chemin exact du fichier ouvert et l'erreur système, comme `Permission denied` ou `No such file or directory`. ## Modes de défaillance et récupération Les volumes nommés échouent de manières limitées. Reconnaître la signature de la défaillance accélère la récupération. ### Le volume ne persiste pas les données après la recréation du conteneur Symptôme : Vous recréez un conteneur avec `docker compose up -d` et l'application démarre avec un état vide. Cause : La couche inscriptible du conteneur est utilisée au lieu du volume. Cela se produit lorsque le chemin de montage est incorrect ou que le volume n'est pas attaché au service dans le fichier Compose. Diagnostic : Pendant que l'ancien conteneur fonctionne encore, inspectez ses montages : ```bash docker inspect myapp --format '{{json .Mounts}}' | jq '.[].Destination' ``` Si le chemin attendu, comme `/var/www/html/uploads`, n'est pas listé, le volume est manquant. Recréez ensuite le conteneur et vérifiez à nouveau. S'il n'y a aucun montage, le fichier Compose n'a jamais défini de volume pour ce service. Récupération : Ajoutez le mappage de volume au fichier Compose puis démarrez le service. Les données écrites dans l'ancienne couche du conteneur sont perdues lorsque le conteneur est supprimé ; si l'ancien conteneur existe toujours, copiez les données avant de le supprimer : ```bash docker cp myapp:/var/www/html/uploads ./uploads-backup ``` Mettez ensuite à jour le fichier Compose, recréez et copiez les données dans le nouveau volume : ```bash docker compose up -d docker cp ./uploads-backup/. myapp:/var/www/html/uploads/ ``` ### Permission refusée à l'intérieur du conteneur Symptôme : Les journaux de l'application affichent `Permission denied` lors de l'écriture dans le chemin monté, ou le conteneur se termine avec une erreur. Cause : La propriété des fichiers hôtes ne correspond pas à l'ID utilisateur (UID) du conteneur. Les volumes nommés héritent de la propriété des données initiales copiées en eux, qui proviennent généralement de l'image. Si le conteneur s'exécute en tant qu'utilisateur non-root, il peut ne pas avoir l'accès en écriture. Diagnostic : Inspectez l'utilisateur du conteneur et la propriété des fichiers du volume : ```bash docker inspect --format '{{.Config.User}}' app docker run --rm -v app_uploads:/data:ro alpine ls -ln /data ``` Comparez l'UID dans la troisième colonne de `ls -ln` avec l'UID du conteneur. S'ils diffèrent et que l'utilisateur du conteneur n'est pas root, les écritures échoueront. Récupération : Modifiez la propriété des données du volume pour correspondre à l'utilisateur du conteneur. Par exemple, si le conteneur s'exécute en tant qu'utilisateur `www-data` avec UID 82, exécutez : ```bash docker run --rm -v app_uploads:/data alpine chown -R 82:82 /data ``` Alternativement, exécutez le conteneur avec un utilisateur qui correspond à la propriété existante des fichiers, ou utilisez un pilote de volume qui prend en charge la correspondance automatique des permissions (par exemple, `:z` ou `:Z` sur les systèmes SELinux). ### Volumes orphelins consommant de l'espace disque Symptôme : L'hôte manque d'espace disque et `docker system df` montre une utilisation élevée des volumes. Cause : Les volumes qui ne sont plus utilisés par aucun conteneur ne sont pas automatiquement supprimés. Avec le temps, ils s'accumulent. Diagnostic : Listez les volumes et leur utilisation : ```bash docker system df -v ``` Cherchez les volumes sans nom de conteneur associé dans la colonne « Links ». Listez également les volumes non référencés par aucun conteneur : ```bash docker volume ls -f dangling=true ``` Récupération : Avant de supprimer un volume, confirmez qu'il est vraiment orphelin. Inspectez ses métadonnées et, si possible, créez un conteneur temporaire pour lister son contenu : ```bash docker run --rm -v :/data alpine ls -la /data ``` Si les données ne sont pas nécessaires, supprimez le volume : ```bash docker volume rm ``` Pour éviter la récurrence, planifiez un nettoyage périodique avec une alerte de surveillance. Par exemple, une tâche cron qui s'exécute chaque semaine et supprime tous les volumes en suspens non utilisés au cours des 30 derniers jours (attention : c'est une commande destructive ; ayez toujours des sauvegardes) : ```bash docker volume prune --filter "label!=keep" ``` ### Sauvegarde et restauration de volume Symptôme : Vous devez migrer un volume vers un autre hôte ou restaurer après une corruption de données. Cause : Les volumes sont locaux à l'hôte par défaut ; ils ne sont pas portables sauf s'ils sont sauvegardés. Procédure : Utilisez un conteneur temporaire pour créer une archive compressée du contenu du volume. Copiez ensuite l'archive vers le nouvel hôte et extrayez-la dans un nouveau volume. Sur l'hôte source : ```bash docker run --rm -v my_volume:/data -v $(pwd):/backup alpine tar czf /backup/my_volume_backup.tar.gz -C /data . ``` Transférez `my_volume_backup.tar.gz` vers l'hôte cible avec `scp` ou un seau de stockage cloud. Sur l'hôte cible : ```bash docker volume create my_volume_new docker run --rm -v my_volume_new:/data -v $(pwd):/backup alpine sh -c "cd /data && tar xzf /backup/my_volume_backup.tar.gz" ``` Puis pointez le conteneur vers `my_volume_new`. Testez la restauration en démarrant un conteneur en lecture seule et en vérifiant l'intégrité des fichiers. ## Liste de vérification opérationnelle Adoptez cette liste de vérification avant tout changement lié aux volumes dans un environnement de production. Attribuez un seul responsable, par exemple « Priya Shah, Responsable Ingénierie », qui est chargé de vérifier chaque élément. Révisez la liste de vérification mensuellement pour l'adapter aux nouveaux modèles de défaillance. ### Liste de vérification pré-changement - [ ] Responsable : Priya Shah, Responsable Ingénierie. Date : 2025-02-20. - [ ] Exécutez `docker version` et `docker compose version` ; enregistrez la sortie. - [ ] Exécutez `docker ps --format "table {{.Names}}\t{{.Status}}"` et identifiez le conteneur cible. - [ ] Inspectez les montages du conteneur : `docker inspect --format '{{json .Mounts}}'` et confirmez le nom du volume et le point de montage. - [ ] Vérifiez l'utilisation du volume : `docker system df -v | grep ` pour voir la consommation de disque. - [ ] Créez une sauvegarde du volume avec un horodatage : `docker run --rm -v :/data -v $(pwd):/backup alpine tar czf /backup/_backup_$(date +%Y%m%d).tar.gz -C /data .` - [ ] Vérifiez la taille de la sauvegarde et listez le contenu : `tar tzf | head`. - [ ] Notez le plan de retour en arrière : la version précédente du fichier Compose et la commande pour restaurer la sauvegarde dans un nouveau volume. ### Pendant le changement - [ ] Arrêtez le service avec `docker compose stop `. - [ ] Effectuez la modification de configuration dans `docker-compose.yml` ou via les options `docker run`. - [ ] Recréez uniquement le service affecté : `docker compose up -d `. - [ ] Surveillez les journaux pendant les 1 à 2 premières minutes : `docker compose logs -f `. - [ ] Vérifiez que le service passe les contrôles de santé s'ils sont définis : `docker inspect --format '{{.State.Health.Status}}' `. ### Vérification post-changement - [ ] Effectuez un test d'écriture et de lecture à l'intérieur du conteneur, par exemple `docker exec sh -c "echo test > /data/testfile && cat /data/testfile"`. - [ ] Confirmez que le fichier existe sur l'hôte au point de montage du volume. - [ ] Recréez le conteneur et vérifiez que le fichier persiste. - [ ] Vérifiez les erreurs de permission dans le journal après le test d'écriture. - [ ] Mettez à jour le manuel d'exploitation avec le changement et le résultat de la vérification. ### Pièges courants et comment les éviter 1. **Ne pas sauvegarder avant de modifier les volumes** Pourquoi cela arrive : Les équipes supposent qu'un simple changement de configuration est à faible risque et sautent l'étape de sauvegarde. Comment l'éviter : Intégrez la commande de sauvegarde dans la liste de vérification. Automatisez-la avec un script qui refuse de continuer si le fichier de sauvegarde n'est pas créé ou est vide. 2. **Ignorer les décalages de propriété de fichier** Pourquoi cela arrive : Le conteneur s'exécute initialement en tant que root, puis une mise à jour de sécurité change l'utilisateur en non-root. Les écritures échouent soudainement. Comment l'éviter : Testez toujours l'application avec le même utilisateur qu'en production. En CI, exécutez le conteneur exactement comme défini dans l'image ou le fichier Compose, y compris la directive `user:`. 3. **Utiliser `docker volume prune` sans filtres** Pourquoi cela arrive : Les opérateurs veulent libérer rapidement de l'espace disque et exécutent la commande prune, supprimant par inadvertance des volumes qui ne sont pas actuellement utilisés mais contiennent des données importantes. Comment l'éviter : Listez d'abord les volumes en suspens avec `docker volume ls -f dangling=true`, inspectez chacun, puis supprimez uniquement les volumes explicites. Utilisez des étiquettes pour marquer les volumes critiques et les exclure avec `--filter "label!=keep"`. 4. **Supposer que les montages de liaison et les volumes nommés se comportent de la même manière** Pourquoi cela arrive : Les deux montent un chemin hôte dans un conteneur, mais les montages de liaison exposent directement le système de fichiers hôte, ce qui entraîne des problèmes de permission et de portabilité. Comment l'éviter : Préférez les volumes nommés pour les données de production. Utilisez les montages de liaison uniquement lorsque vous avez besoin d'éditer en direct le code source pendant le développement. 5. **Ne pas tester les procédures de récupération** Pourquoi cela arrive : Les sauvegardes sont créées mais jamais restaurées, alors lorsqu'une panne réelle survient, la procédure de restauration échoue en raison de dépendances manquantes ou de chemins incorrects. Comment l'éviter : Planifiez un exercice trimestriel de reprise après sinistre. Le responsable (Priya Shah) est chargé d'exécuter l'exercice et de documenter le temps de restauration. ## Conclusion Les volumes nommés Docker simplifient la persistance des données, mais ils nécessitent toujours une attention opérationnelle délibérée. Le flux de travail le plus fiable est d'observer avant de changer, de limiter chaque modification à un seul élément ciblé, de vérifier le résultat avec une commande concrète et d'avoir un plan de retour en arrière testé. Chaque étape de ce guide comprend une commande et sa sortie attendue afin que vous puissiez suivre dans votre propre environnement. Commencez par l'inventaire des versions et de l'environnement pour comprendre votre base de référence. Traitez ensuite les changements de configuration comme des opérations réversibles avec sauvegardes. Lorsqu'une panne survient, utilisez les commandes de diagnostic pour déterminer si le volume est monté, si les permissions sont correctes et si les données persistent réellement. Enfin, adoptez la liste de vérification opérationnelle et attribuez un seul responsable pour la réviser régulièrement. Ainsi, les volumes nommés Docker deviennent une partie prévisible de votre infrastructure plutôt qu'une source de surprise.