Docker et les volumes de données (1/2)

Vous venez de créer vos premiers conteneurs Docker et peut-être ne vous êtes pas encore demandé ce qu’il advient des données stockées à l’intérieur. Alors par défaut, les données contenues dans le conteneur disparaissent tout simplement avec lui lors de sa destruction. Ce peut être le comportement voulu dans la plupart des cas, mais il est parfois nécessaire de rendre les données persistantes. Par exemple, une base de données utilisateur ne devrait pas disparaitre lorsque l’on supprime le conteneur contenant le moteur de la base de données !

Docker offre plusieurs manières de gérer les données persistantes. Nous verrons dans un premier temps la notion de volume de données puis les conteneurs de données dans un deuxième article.

Le volume de données

Docker offre le concept de volume de données. L’idée est de stocker les données dans un répertoire spécifique du serveur hôte Docker plutôt que dans le système de fichiers par défaut du conteneur. Ce stockage externe, appelé volume de données, permet de rendre les données indépendantes de la vie du conteneur. Le volume permet également de partager ces données entre plusieurs conteneurs.

Le volume de données dans Docker propose ces fonctionnalités :

  • le volume est initialisé à la création du conteneur. Si l’image de base contient déjà des données à l’initialisation du volume, alors elles seront copiées dans le volume
  • un volume de données peut être partagé ou réutilisé par plusieurs conteneurs
  • les changements dans le volume de données sont appliqués immédiatement
  • le volume de données est persistant même si le conteneur lui-même est supprimé

A noter que Docker ne supprimera jamais automatiquement un volume même si plus aucun conteneur n’y fait référence. Ce sera à vous de faire le ménage !

Ajouter un volume

Un volume de données peut être ajouté de deux manières à un conteneur : manuellement à la création du conteneur ou dans le fichier de construction Dockerfile.

Ajout manuel

Pour ajouter un volume à la création d’un conteneur, il faut utiliser l’option -v. L’option -v peut être utilisée autant de fois que de volumes ajoutés.

Exemple d’ajout de volume :

# docker run -d -v /var/log debian:stable /bin/sleep infinity
0a6b50dfc7bbb343fbac000b4ca4256353fe511e3f6b6c486bf8665e053b282a

Dans cette commande, je lance un nouveau conteneur et je spécifie le volume /var/log. Cela signifie que les données contenues dans le répertoire /var/log du conteneur seront externalisées dans un répertoire spécifique du serveur hôte Docker et ne seront plus stockées dans le conteneur lui-même.

Si je veux ajouter plusieurs volumes, je multiplie les options -v :

# docker run -d -v /var/log -v /var/www debian:stable /bin/sleep infinity
be23eb490cd2845680a6c7e953e59f2212d4cd5ab619eb7d44271c9197a586f1

J’ai ici ajouté deux volumes : /var/log et /var/www.

Ajout dans le fichier de construction

Plutôt que d’utiliser l’option -v au lancement du conteneur, on peut aussi spécifier le volume de données dans le fichier de construction Dockerfile. On utilise pour cela l’instruction VOLUME, par exemple :

VOLUME /var/log

L’instruction VOLUME peut être utilisée plusieurs fois. On peut aussi spécifier plusieurs volumes dans une seule instruction VOLUME en les séparant par un espace. Exemple :

VOLUME /var/log /var/www

Le format JSON est aussi accepté. Exemples avec un ou plusieurs volumes :

VOLUME ["/var/log"]
VOLUME ["/var/log", "/var/www"]

Vous avez le choix de la syntaxe 🙂

A noter : les changements intervenant après l’instruction VOLUME ne seront pas reportés dans les volumes créés. Par exemple si je mets ces instructions dans mon fichier Dockerfile :

VOLUME ["/var/log"]
RUN echo "hello world" > /var/log/test

La deuxième instruction ne sera pas reportée dans le volume car positionnée APRES l’instruction VOLUME. Le conteneur créé ne verra donc pas de fichier /var/log/test contenant hello world.
Pour cette raison, pensez à placer les instruction VOLUME vers la fin du fichier Dockerfile.

Localiser les données

Bon très bien les données sont maintenant externalisées dans un volume de données. Mais … elles sont où exactement ces données sur le serveur hôte Docker ?
Pour le savoir, il faut inspecter le conteneur. Par exemple, pour mon conteneur précédent comportant deux volumes de données :

# docker inspect be23

C’est la section Mounts qui nous intéresse :

        "Mounts": [
            {
                "Name": "1b6c177462ad722bfa8d579613fd7f398f3e2be81c69cd40ad830cdde69b6757",
                "Source": "/var/lib/docker/volumes/1b6c177462ad722bfa8d579613fd7f398f3e2be81c69cd40ad830cdde69b6757/_data",
                "Destination": "/var/log",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            },
            {
                "Name": "57e8db5334392826b919f1fc63cc8b065bd652936208c02d5e0e4c9a355b2b5d",
                "Source": "/var/lib/docker/volumes/57e8db5334392826b919f1fc63cc8b065bd652936208c02d5e0e4c9a355b2b5d/_data",
                "Destination": "/var/www",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],

Les deux volumes de données apparaissent ici.

Le paramètre Source indique le chemin du volume dans le conteneur. C’est ce que l’on a indiqué dans les options -v ou via l’instruction VOLUME.

Le paramètre Destination indique lui le chemin du répertoire sur le serveur Docker.

Ainsi les données du répertoire /var/log vues par le conteneur se trouvent en réalité sur l’hôte Docker dans le répertoire /var/lib/docker/volumes/1b6c177462ad722bfa8d579613fd7f398f3e2be81c69cd40ad830cdde69b6757/_data !

Monter un répertoire comme un volume de données

Nous avons vu précédemment comment créer un volume de données pour un conteneur afin d’externaliser une partie des données sur l’hôte Docker. On peut aussi vouloir utiliser des données déjà présentes sur l’hôte Docker pour les rendre accessibles directement dans un conteneur. On utilisera pour cela la même option -v que précédemment mais en spécifiant cette fois la source (sur l’hôte Docker) et la destination (dans le conteneur). Exemple :

docker run -d -v /var/log:/log_host debian:stable /bin/sleep infinity

J’ai lancé ici un conteneur en spécifiant que je veux monter le répertoire /var/log de l’hôte Docker vers le répertoire /log_host dans le conteneur. Ainsi, si je regarde le contenu du répertoire log_host dans le conteneur :

# docker exec -it 9e4 ls -l /log_host

Je verrai alors apparaitre le contenu du répertoire /var/log de mon hôte Docker.

A noter : le répertoire de destination, ici /log_host n’a pas besoin d’exister avant. Docker le crée automatiquement. En revanche, s’il existait il sera recouvert par le montage et les données précédentes ne seront plus accessibles tant que le montage sera opérationnel. C’est le fonctionnement classique de la commande mount.

A noter : par défaut, le répertoire est monté en lecture-écriture. Attention donc aux modifications que pourraient apporter le conteneur au répertoire hôte. Le répertoire peut aussi être monté en lecture seule en spécifiant l’option ro :

docker run -d -v /var/log:/log_host:ro debian:stable /bin/sleep infinity

Vous savez désormais comment créer un volume Docker pour externaliser les données gérées par un conteneur. Vous pouvez localiser ces données sur l’hôte Docker et vous savez également comment monter un répertoire de l’hôte dans le conteneur.

Dans un prochain billet, nous parlerons du partage des volumes entre conteneur et du principe des conteneurs de données.

Sources :
https://docs.docker.com/engine/tutorials/dockervolumes/
https://docs.docker.com/engine/reference/builder/#/volume

1 réflexion sur « Docker et les volumes de données (1/2) »

Laisser un commentaire