Aller au contenu principal

Utilisation de volumes

Soumis par lulu le lun 20/02/2023 - 20:58

Les volumes sont le mécanisme privilégié pour la persistance des données générées et utilisées par les conteneurs Docker. Alors que les bind mounts dépendent de la structure des répertoires et du système d'exploitation de la machine hôte, les volumes sont entièrement gérés par Docker. Les volumes présentent plusieurs avantages par rapport aux bind mounts :

  • Les volumes sont plus faciles à sauvegarder ou à migrer que les bind mounts.
  • Vous pouvez gérer les volumes à l'aide des commandes Docker CLI ou de l'API Docker.
  • Les volumes fonctionnent sur les conteneurs Linux et Windows.
  • Les volumes peuvent être partagés de manière plus sûre entre plusieurs conteneurs.
  • Les drivers de volume vous permettent de stocker des volumes sur des hôtes ou des fournisseurs de clouds distants, de chiffrer le contenu des volumes ou d'ajouter d'autres fonctionnalités.
  • Les nouveaux volumes peuvent avoir leur contenu pré-rempli par un conteneur.
  • Les volumes sur Docker Desktop sont beaucoup plus performants que les bind mounts des hôtes Mac et Windows.

En outre, les volumes sont souvent un meilleur choix que la persistance des données dans la couche inscriptible d'un conteneur, car un volume n'augmente pas la taille des conteneurs qui l'utilisent et le contenu du volume existe en dehors du cycle de vie d'un conteneur donné.

Image adaptative

Choisissez l'option -v ou --mount

En général, --mount est plus explicite et verbeux. La plus grande différence est que la syntaxe -v regroupe toutes les options dans un seul champ, alors que la syntaxe --mount les sépare. Voici une comparaison de la syntaxe pour chaque option.

Si vous devez spécifier les options du pilote de volume, vous devez utiliser --mount.

  • -v ou --volume : Consiste en trois champs, séparés par des caractères deux-points ( :). Les champs doivent être dans le bon ordre, et la signification de chaque champ n'est pas immédiatement évidente.
    • Dans le cas des volumes nommés, le premier champ est le nom du volume, et il est unique sur une machine hôte donnée. Pour les volumes anonymes, le premier champ est omis.
    • Le deuxième champ est le chemin où le fichier ou le répertoire est monté dans le conteneur.
    • Le troisième champ est facultatif. Il s'agit d'une liste d'options, telles que ro, séparées par des virgules. Ces options sont présentées ci-dessous.
  •  --mount : Consiste en plusieurs paires clé-valeur, séparées par des virgules et consistant chacune en un tuple <key>=<value>. La syntaxe --mount est plus verbeuse que -v ou --volume, mais l'ordre des clés n'est pas significatif, et la valeur de l'indicateur est plus facile à comprendre.
    • Le type de montage, qui peut être bind, volume, ou tmpfs. Cet article traite des volumes, donc le type est toujours volume.
    • La source du montage. Pour les volumes nommés, il s'agit du nom du volume. Pour les volumes anonymes, ce champ est omis. Peut être spécifié comme source ou src.
    • La destination prend comme valeur le chemin où le fichier ou le répertoire est monté dans le conteneur. Peut être spécifié comme destination, dst, ou target.
    • L'option readonly, si elle est présente, fait en sorte que le montage lié soit monté dans le conteneur en lecture seule.
    • L'option volume-opt, qui peut être spécifiée plusieurs fois, prend une paire clé-valeur constituée du nom de l'option et de sa valeur.

 

Créer et gérer des volumes

Contrairement à un bind mount, vous pouvez créer et gérer des volumes en dehors de tout conteneur.

  • Créez un volume :
$ docker volume create my-vol

 

  • Lister les volumes :
$ docker volume ls

DRIVER    VOLUME NAME
local     0dd1fc1cb2f4a6d047cebcb7de3947e947df9a474eb45d8457e754ae68ae99e5  (# volume anonyme)
local     d3d2b3b0884719e39839e99e2f31ed5fdb641c2171481ecc9b187af096340f6f
local     d21654e6ba1a69a25f517a22d2d2b8cd1b9303f484ebde0fa42009342294b5fb
local     my-volx
local     my_volume  (# volume nommé)

 

  • Inspecter un volume :
# On notera le point de montage de "my_volume" : "/var/lib/docker/volumes/my_volume/_data"
# Le répertoire "/var/lib/docker/volumes" contient tous les volumes (nommés ou anonymes) de Docker
$ docker volume inspect my_volume
[
    {
        "CreatedAt": "2021-08-20T18:05:08+02:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my_volume/_data",
        "Name": "my_volume",
        "Options": {},
        "Scope": "local"
    }
]

 

  • Supprimez un volume :
$ docker volume rm my_volume

 

Démarrer un conteneur avec un volume

Si vous démarrez un conteneur avec un volume qui n'existe pas encore, Docker crée le volume pour vous. L'exemple suivant monte le volume myvol2 dans /app/ dans le conteneur.

Les syntaxes -v et --mount ci-dessous produisent le même résultat. Vous ne pouvez pas les exécuter toutes les deux, sauf si vous supprimez le conteneur devtest et le volume myvol2 après avoir exécuté la première.

 

# Syntaxe --mount
$ docker run -d \
  --name devtest \
  --mount source=myvol2,target=/app \
  nginx:latest

# Syntaxe -v
$ docker run -d \
  --name devtest \
  -v myvol2:/app \
  nginx:latest

 

Utilisez docker inspect devtest pour vérifier que le volume a été créé et monté correctement. Recherchez la section Mounts :

"Mounts" : [
    {
        "Type" : "Volume",
        "Nom" : "monvol2",
        "Source" : "/var/lib/docker/volumes/myvol2/_data",
        "Destination" : "/app",
        "Pilote" : "local",
        "Mode" : "",
        "RW" : true,
        "Propagation" : ""
    }
],

Cela montre que le montage est un volume, que la source et la destination sont correctes et que le montage est en lecture-écriture.

Arrêtez le conteneur et supprimez le volume. Notez que la suppression du volume est une étape distincte.

$ docker container stop devtest

$ docker container rm devtest

$ docker volume rm myvol2

 

Supprimer des volumes

Un volume de données Docker persiste après la suppression d'un conteneur. Il existe deux types de volumes à prendre en compte :

  • Les volumes nommés ont une source spécifique depuis l'extérieur du conteneur, par exemple awesome:/bar.
  • Les volumes anonymes n'ont pas de source spécifique, donc lorsque le conteneur est supprimé, demandez au démon Docker Engine de les supprimer.

 

Suppression des volumes anonymes

Pour supprimer automatiquement les volumes anonymes, utilisez l'option --rm. Par exemple, cette commande crée un volume anonyme /foo. Lorsque le conteneur est supprimé, le moteur Docker supprime le volume /foo mais pas le volume awesome.

$ docker run --rm -v /foo -v awesome:/bar busybox top

 

Suppression de tous les volumes

Pour supprimer tous les volumes inutilisés et libérer de l'espace :

$ docker volume prune

 

Comparaison avec les bind mounts

Les bind mounts (montages liés) existent depuis les premiers jours de Docker. Les bind mounts ont une fonctionnalité limitée par rapport aux volumes. Lorsque vous utilisez un bind mount, un fichier ou un répertoire sur la machine hôte est monté dans un conteneur. Le fichier ou le répertoire est référencé par son chemin absolu sur la machine hôte. En revanche, lorsque vous utilisez un volume, un nouveau répertoire est créé dans le répertoire de stockage de Docker sur la machine hôte, et Docker gère le contenu de ce répertoire.

Il n'est pas nécessaire que le fichier ou le répertoire existe déjà sur l'hôte Docker. Il est créé à la demande s'il n'existe pas encore. Les bind mounts sont très performants, mais ils dépendent du fait que le système de fichiers de la machine hôte dispose d'une structure de répertoire spécifique. Si vous développez de nouvelles applications Docker, envisagez plutôt d'utiliser des volumes nommés. Vous ne pouvez pas utiliser les commandes Docker CLI pour gérer directement les bind mounts.

 

Choisissez l'option -v ou --mount

En général, --mount est plus explicite et verbeux. La plus grande différence est que la syntaxe -v regroupe toutes les options dans un seul champ, alors que la syntaxe --mount les sépare. Voici une comparaison de la syntaxe pour chaque option.

Conseil : Les nouveaux utilisateurs devraient utiliser la syntaxe --mount. Les utilisateurs expérimentés peuvent être plus familiers avec la syntaxe -v ou --volume, mais sont encouragés à utiliser --mount, car la recherche a montré qu'elle est plus facile à utiliser.

  • -v ou --volume : Consiste en trois champs, séparés par des caractères deux-points ( :). Les champs doivent être dans le bon ordre, et la signification de chaque champ n'est pas immédiatement évidente.
    • Dans le cas des bind mounts, le premier champ est le chemin d'accès au fichier ou au répertoire sur la machine hôte.
    • Le deuxième champ est le chemin d'accès où le fichier ou le répertoire est monté dans le conteneur.
    • Le troisième champ est facultatif et consiste en une liste d'options séparées par des virgules, telles que ro, z et Z.
  • --mount : Consiste en plusieurs paires clé-valeur, séparées par des virgules et consistant chacune en un tuple <key>=<value>. La syntaxe --mount est plus verbeuse que -v ou --volume, mais l'ordre des clés n'est pas significatif, et la valeur de l'indicateur est plus facile à comprendre.
    • Le type de montage, qui peut être bind, volume, ou tmpfs. Dans ce cas précis, ce sera toujours bind.
    • La source du montage. Pour les montages liés, il s'agit du chemin d'accès au fichier ou au répertoire sur l'hôte du démon Docker. Peut être spécifié comme source ou src.
    • La destination prend comme valeur le chemin où le fichier ou le répertoire est monté dans le conteneur. Peut être spécifié comme destination, dst, ou target.
    • L'option readonly, si elle est présente, fait en sorte que le montage bind soit monté dans le conteneur en lecture seule.
    • L'option bind-propagation, si elle est présente, modifie la propagation du bind. Peut être l'un des éléments suivants : rprivate, private, rshared, shared, rslave, slave.
    • L'indicateur --mount ne supporte pas les options z ou Z pour modifier les étiquettes selinux.

 

Démarrer un conteneur avec un bind mount

Considérons un cas où vous avez un répertoire source et que lorsque vous construisez le code source, les artefacts sont enregistrés dans un autre répertoire, source/target/. Vous voulez que les artefacts soient disponibles pour le conteneur dans /app/, et vous voulez que le conteneur ait accès à une nouvelle construction chaque fois que vous construisez la source sur votre hôte de développement. Utilisez la commande suivante pour monter le répertoire target/ dans votre conteneur à /app/. Exécutez la commande depuis le répertoire source. La sous-commande $(pwd) s'étend au répertoire de travail actuel sur les hôtes Linux ou macOS.

Les syntaxes --mount et -v ci-dessous produisent le même résultat. Vous ne pouvez pas les exécuter toutes les deux, sauf si vous supprimez le conteneur devtest après avoir exécuté la première.

# Syntaxe --mount
$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest


# Syntaxe -v
$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app \
  nginx:latest

 

Utilisez docker inspect devtest pour vérifier que le montage bind a été créé correctement. Recherchez la section Mounts :

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

Cela montre que le montage est un bind mount, il montre la source et la destination correctes, il montre que le montage est en lecture-écriture, et que la propagation est définie sur rprivate.

 

Intruction VOLUME dans un Dockerfile

Il est possible d'utiliser une intruction VOLUME dans un fichier Dockerfile, de la manière suivante :

VOLUME ["/home/user/my_dir"]

L'instruction VOLUME crée un point de montage avec le nom spécifié et le marque comme contenant des volumes montés en externe depuis l'hôte natif ou d'autres conteneurs. La valeur peut être un tableau JSON, VOLUME ["/var/log/"], ou une chaîne simple avec plusieurs arguments, comme VOLUME /var/log ou VOLUME /var/log /var/db.

On notera toutefois qu'aucun volume n'est créé lors du build de l'image ; la création du volume se fera uniquement lorsqu'on génèrera un conteneur à partir de cette image, au moyen de la commande docker run. C'est dans cette commande docker run que l'on pourra préciser, au moyen des instructions -v ou --mount, si l'on doit créer un volume anonyme, un volume nommé ou un bind mount :

  • si la commande ne possède aucune instruction -v ou --mount (elle est de la forme : docker run --name my_container my_image), c'est un volume anonyme qui est créé automatiquement, et visible dans le répertoire /var/lib/docker/volumes :
/var/lib/docker/volumes# ls -al
total 60
drwx-----x  3 root root   4096 août  21 12:35 .
drwx--x--x 14 root root   4096 août  21 12:35 ..
drwx-----x  3 root root   4096 août  20 21:28 5dc6dd5c31189b0764f0a31f5c8406d51fa1503d9d73aadcc7cf5118e27104fe
  • si la commande possède une instruction -v ou --mount avec un nom de volume (elle est de la forme : docker run --name my_container -v my_vol:/home/user/my_dir my_image), c'est un volume nommé qui est créé avec le nom déclaré, et visible dans le répertoire /var/lib/docker/volumes :
/var/lib/docker/volumes# ls -al 
total 60 
drwx-----x 3 root root 4096 août 21 12:35 . 
drwx--x--x 14 root root 4096 août 21 12:35 .. 
drwx-----x 3 root root 4096 août 20 21:28 my_vol
  • si la commande possède une instruction -v ou --mount avec un chemin d'accès à un répertoire dans le système de fichier de l'hôte (elle est de la forme : docker run --name my_container -v /var/www/vhosts/my_host_dir:/home/user/my_dir my_image), c'est un bind mount qui est créé : le contenu du répertoire hôte /var/www/vhosts/my_host_dir est mappé dans le conteneur, à l'emplacement prévu /home/user/my_dir. Il est à noter que dans ce cas, ce n'est pas un volume Docker qui est créé, mais simplement un bind mount entre un répertoire de l'hôte et un répertoire du conteneur ; on ne peut pas utiliser les commandes Docker CLI pour gérer ce point de montage.