Gestion de volumes sous Docker

Posted on lun. 21 janvier 2019 in Devops

Quelques commandes utiles

Obtenir des informations sur les volumes associés à un conteneur :

docker container inspect -f "{{ json .Mounts }}" <container_name> | python -m json.tool
Cette commande permet notamment de vérifier l'emplacement et le nom du volume sur le disque de l'hôte.

L'option -f (abbréviation de --format) permet de filtrer les résultats en fonction d'un ou plusieurs critères exprimés sous forme de Go template. Le "." correspond à l'object courant et dans l'exemple il s'agit de la valeur de la propriété Mounts.
json est une fonction fournie par Docker pour encoder les chaînes de caractères à ce format.
Notons enfin le pipe de présentation final : python -m json.tool
json.tool est un module Python utilisé en ligne de commande pour l'affichage indenté des objets JSON.

Mise en pratique pour un conteneur "postgres" (basé sur une image officielle postgresql) :
docker container inspect -f "{{ json .Mounts }}" postgres | python -m json.tool

[
    {
        "Destination": "/var/lib/postgresql/data",
        "Driver": "local",
        "Mode": "",
        "Name": "5452bbee2344cbcdb9768f3451a7e8c797c6f77dae57389bf870e92f3175725e",
        "Propagation": "",
        "RW": true,
        "Source": "/var/lib/docker/volumes/5452bbee2344cbcdb9768f3451a7e8c797c6f77dae57389bf870e92f3175725e/_data",
        "Type": "volume"
    }
]

Destination correspond au point de montage dans le conteneur. Name et Source correspondent respectivement au nom et à l'emplacement du volume sur le disque de l'hôte.

Identifier le conteneur auquel est rattaché un volume :

docker ps -a --filter volume=<volume_name_or_id>
C'est un peu l'inverse de la commande précédente. Partant du nom d'un volume local, retrouver à quel conteneur il est rattaché.

Exemple : docker ps -a --filter volume=5452bbee2344cbcdb9768f3451a7e8c797c6f77dae57389bf870e92f3175725e

CONTAINER ID  IMAGE         COMMAND               CREATED     STATUS                    PORTS     NAMES
81306660f9b1  postgres:9.4 "docker-entrypoint.s…" 3 days ago  Exited (255) 15 hours ago 5432/tcp  postgres

Supprimer les volumes inutilisés

Rappelons tout d'abord qu'un conteneur stoppé n'est pas automatiquement supprimé à moins de l'avoir démarré avec l'option --rm.
Cf. https://docs.docker.com/config/pruning/
Les volumes, quant à eux, ne sont jamais supprimés de manière automatique. La commande suivante permet de supprimer tous les volumes non utilisés (ceux auxquels aucun conteneur n'est rattaché) : docker volume prune

Cela évite d'avoir à supprimer les volumes un par un en les identifiant par leurs noms, en utilisant une commande du type : docker volume rm <volume_id>

Exemples de backup/restauration

Ces exemples sont très largement inspirés de la page d'aide de Docker sur les volumes :
https://docs.docker.com/storage/volumes/

Créer un volume dbdata à la racine d'un conteneur nommé "dbstore"

docker run -ti -v /dbdata --name dbstore debian:8-slim /bin/bash

L'image utilisée ici est une debian:8-slim.

Pour l'exemple, créons 2 fichiers dans le répertoire /dbdata du conteneur :

root@d3b8c5120cca:/# for i in {1,2}; do echo "Content of file$i" > file$i.log; done;

Puis stoppons le conteneur :

root@d3b8c5120cca:/# exit

Backup

docker run --rm --volumes-from dbstore -v $(pwd):/backup alpine sh -c "cd /dbdata/ && tar -zcvf /backup/backup.tgz *"

Disséquons la commande :

  1. Lancer un conteneur temporaire (à partir d'une image alpine) en montant le(s) volume(s) du conteneur nommé dbstore :
    docker run --rm --volumes-from dbstore ... alpine

  2. Lier le répertoire courant sur l'hôte au point de montage /backup du conteneur temporaire :
    -v $(pwd):/backup

  3. Compresser le contenu du répertoire /dbdata dans un fichier gzip, identifié par son chemin absolu /backup/backup.tgz :
    sh -c "cd /dbdata/ && tar -zcvf /backup/backup.tgz *"

Suppression des fichiers d'origine

Redémarrons le conteneur dbstore :

docker container start -ai dbstore

Supprimons les fichiers précédemment créés afin de simuler une erreur involontaire :

cd /dbdata && rm file*

Stoppons le conteneur :

exit

Restauration

Les deux premières étapes sont les mêmes. La troisième permet de se déplacer dans le point de montage /dbdata et de décompresser le fichier backup.tgz :

docker run --rm --volumes-from dbstore -v $(pwd):/backup alpine sh -c "cd /dbdata && tar zxvf /backup/backup.tgz"

Vérifions maintenant si la restauration a bien fonctionné :

docker container start -ai dbstore
root@d3b8c5120cca:/# ls -l /dbdata
total 0
-rw-r--r-- 1 root root 0 Feb  3 08:17 file1.log
-rw-r--r-- 1 root root 0 Feb  3 08:17 file2.log
root@d3b8c5120cca:/# cat file*.log
Content of file1
Content of file2

Conclusion

Docker offre beaucoup de souplesse dans le partage de contenus entre hôte et conteneurs. Les exemples présentés ci-dessus en sont une illustration partielle et témoignent de la facilité avec laquelle on peut utiliser des données persistentes au sein de cet environnement.

Pour en savoir plus :