Aller au contenu

Gérer les volumes Docker

Objectifs :

  • Rendre les données persistantes en utilisant les volumes Docker

  • Partager des données entre plusieurs conteneurs

  • Rendre des données disponibles à un conteneur en lecture seule

  • Utiliser les volumes éphémères

  • Supprimer rapidement les volumes obsolètes

Quel est l'intérêt des volumes ?

En règle générale, les images sont construites de manière à ce que les conteneurs basés sur elles soient compacts, portables et jetables.

Les images ne contiennent généralement que les paquets indispensables pour fournir le service prévu par l'image. Les petits fichiers de configuration qui changent rarement sont également inclus dans ces images.

Il vaut mieux pouvoir se débarrasser d'un conteneur sans pour autant craindre de perdre les données importantes. Dans la mesure du possible, il faut éviter que les données d'un conteneur - la partie data si l'on veut - résident uniquement dans le conteneur.

Dans ce cas, si vous voulez rendre persistantes - ou sauvegarder - les données générées ou utilisées par un conteneur, il faudra utiliser un volume.

Si vous souhaitez partager des données entre plusieurs conteneurs, un volume est également le meilleur choix pour ce genre de cas.

Deux approches différentes

Voyons de plus près les différents types de volumes gérés par Docker.

Dans notre atelier pratique sur l'exposition des ports nous avons utilisé l'option -v avec la commande docker run pour définir l'utilisation d'un volume avec le conteneur :

$ mkdir -v ~/pagesweb
mkdir: création du répertoire '/home/kikinovak/pagesweb'
$ cd ~/pagesweb/
$ echo '<h1>Le site web de Nico</h1>' > index.html
$ cd ..
$ docker run --name autre_nginx -d -p 8080:80 \
> -v ${PWD}/pagesweb:/usr/share/nginx/html:ro nginx
6b456addfb69fb9fe5acdec3dd4e5e7fbad0808edaac0ac28293979b036e4c0d

L'option courte -v est l'équivalent exact de l'option longue --volume :

$ docker run --name autre_nginx -d -p 8080:80 \
> --volume ${PWD}/pagesweb:/usr/share/nginx/html:ro nginx

La nouvelle manière préférée de monter des volumes dans un conteneur est l'option --mount. Docker recommande d'utiliser --mount plutôt que -v ou --volume. Quoi qu'il en soit, l'une ou l'autre méthode fonctionne parfaitement. Puisque --mount est la méthode recommandée à l'avenir, c'est celle que nous allons voir plus en détail.

Créer un volume

On utilisera la sous-commande docker volume pour gérer les volumes :

$ docker volume --help

Usage:  docker volume COMMAND

Manage volumes

Commands:
  create      Create a volume
  inspect     Display detailed information on one or more volumes
  ls          List volumes
  prune       Remove all unused local volumes
  rm          Remove one or more volumes

Run 'docker volume COMMAND --help' for more information on a command.

Créez un premier volume :

$ docker volume create testdata
testdata

Vérifiez s'il a bien été créé :

$ docker volume ls
DRIVER    VOLUME NAME
local     testdata

Supprimez ce volume :

$ docker volume rm testdata
testdata
$ docker volume ls
DRIVER    VOLUME NAME

À présent, créez un nouveau volume mesdata1 :

$ docker volume create mesdata1
mesdata1
$ docker volume ls
DRIVER    VOLUME NAME
local     mesdata1

Pour en savoir plus sur un volume, utilisez la commande inspect :

$ docker volume inspect mesdata1
[
  {
    "CreatedAt": "2022-03-29T09:02:58+02:00",
    "Driver": "local",
    "Labels": {},
    "Mountpoint": "/var/lib/docker/volumes/mesdata1/_data",
    "Name": "mesdata1",
    "Options": {},
    "Scope": "local"
  }
]

Notez que nous voyons ici l'emplacement réel du volume dans l'arborescence du système de fichiers de l'hôte. En l'occurrence, les données stockées dans le volume mesdata1 sont enregistrées dans le répertoire /var/lib/docker/volumes/mesdata1/_data du système hôte.

Attacher un volume

La prochaine étape consiste à démarrer un conteneur en attachant un volume à ce conteneur de manière à ce que le conteneur ait accès aux données du volume :

$ docker run -d --name avecvolume --mount \
  source=mesdata1,destination=/root/volume nginx
b15b836717e9d4462ffbebcdbe6fb111f86490a0bedc9b21ad25aab142091f8a

Veillez à ne pas utiliser d'espaces dans les arguments à --mount.

Inspectez le conteneur et jetez un œil à la section Mounts :

$ docker inspect avecvolume | grep -A 10 Mounts
...
"Mounts": [
  {
    "Type": "volume",
    "Name": "mesdata1",
    "Source": "/var/lib/docker/volumes/mesdata1/_data",
    "Destination": "/root/volume",
    "Driver": "local",
    "Mode": "z",
    "RW": true,
    "Propagation": ""
  }
  • La section Mounts nous affiche que /root/volume est la destination du volume à l'intérieur du conteneur, avec des droits en lecture/écrite comme indiqué par la ligne "RW": true.

  • Le chemin d'accès /var/lib/docker/volumes/mesdata1/_data correspond au chemin du volume que Docker a mis en place sur le disque du système hôte. C'est donc la source.

Utiliser les abréviations

Au lieu d'utiliser source= et destination=, nous pouvons utiliser les formes brèves correspondantes src= et dst= :

$ docker run -d --name avecvolume --mount src=mesdata1,dst=/root/volume nginx

On peut également utiliser target= au lieu de destination= ou dst=.

Supprimer un volume

Lorsque vous arrêtez un conteneur, le volume reste intact, et vous devez le supprimer séparément si vous le souhaitez. Pour ce faire, vous utiliserez la commande docker volume rm avec le nom du volume en argument.

Ajouter un fichier depuis l'hôte

Ajoutez un fichier au volume depuis le système hôte :

$ echo 'Coucou depuis le volume mesdata1 !' | \
  sudo tee /var/lib/docker/volumes/mesdata1/_data/index.html

Utilisez la commande exec pour vous connecter au conteneur et vérifiez si le fichier index.html se trouve bien à l'emplacement attendu :

$ docker exec -it avecvolume bash
root@1b198f46f5da:/# cd /root/volume/
root@1b198f46f5da:~/volume# ls -l
total 4
-rw-r--r-- 1 root root 35 Apr  1 07:28 index.html
root@1b198f46f5da:~/volume# cat index.html
Coucou depuis le volume mesdata1 !
root@1b198f46f5da:~/volume# exit
exit

Un volume pour plusieurs conteneurs

Docker permet de monter le même volume sur plusieurs conteneurs. Essayez :

$ docker run -d --name avecvolume2 --mount \
  src=mesdata1,dst=/root/volume nginx
046c17291d106720eca84ab5bbf60370406c676a884b7a5eb852d7dde8b8307b
$ docker exec -it avecvolume2 bash
root@046c17291d10:/# cat /root/volume/index.html
Coucou depuis le volume mesdata1 !
root@046c17291d10:/# exit
exit

Un volume en lecture seule

Admettons que vous ne voulez pas que le conteneur puisse modifier le contenu d'un volume donné. Autrement dit, vous souhaitez que je conteneur accède au volume en lecture seule :

$ docker run -d --name conteneurenlecture --mount \
  src=nouveauvolume,dst=/usr/share/nginx/html,ro nginx
8b5210c998c4d33a80cc1cfbc4a85510cfc2cb4b454ce8e209e487a353703fc9

Si le volume n'existe pas lors du lancement du conteneur, Docker se charge de le créer automatiquement :

$ docker volume ls
DRIVER    VOLUME NAME
local     mesdata1
local     nouveauvolume

Inspectez la section Mounts de ce conteneur :

$ docker inspect conteneurenlecture | grep -A 10 Mounts
...
"Mounts": [
  {
    "Type": "volume",
    "Name": "nouveauvolume",
    "Source": "/var/lib/docker/volumes/nouveauvolume/_data",
    "Destination": "/usr/share/nginx/html",
    "Driver": "local",
    "Mode": "z",
    "RW": false,
    "Propagation": ""
  }

Notez bien le paramètre "RW": false qui indique que le conteneur est en lecture seule. L'utilisation de cette option fait partie des bonnes pratiques en matière de sécurité lorsque l'accès en écriture n'est pas requis. Ainsi, lorsqu'un conteneur est compromis dans le contexte d'une attaque informatique, les données du ou des volumes correspondants restent intactes.

Vous pouvez très bien autoriser certains conteneurs à accéder en lecture/écriture à un volume donné, alors que d'autres conteneurs n'y accèderont qu'en lecture seule. En effet, la possibilité d'écrire sur un volume est définie au niveau du conteneur et non pas au niveau du volume.

Vérifions si notre volume est effectivement monté en lecture seule à l'intérieur du conteneur :

$ docker exec -it conteneurenlecture bash
root@8b5210c998c4:/# touch /usr/share/nginx/html/test
touch: cannot touch '/usr/share/nginx/html/test': Read-only file system
root@8b5210c998c4:/# exit
exit

Utiliser un volume éphémère

Lorsqu'on utilise des volumes qui sont censés être détruits après utilisation, on peut très bien utiliser ce qu'on appelle les volumes éphémères de type tmpfs. Voici à quoi cela peut ressembler :

$ docker run -dit --name ephemere --mount \
  type=tmpfs,dst=/root/volume nginx
943e634f246a34c97c6e4b239204232239598d8c947aa7668319b41673e13af3

Jetez un œil dans la section Mounts de ce conteneur et notez bien le type tmpfs :

$ docker inspect ephemere | grep -A 10 Mounts
...
"Mounts": [
  {
    "Type": "tmpfs",
    "Source": "",
    "Destination": "/root/volume",
    "Mode": "",
    "RW": true,
    "Propagation": ""
  }

Un volume éphémère à taille prédéfinie

Vous pouvez prédéfinir une taille statique pour un volume éphémère, ce qui vous permet de garder le contrôle sur l'espace disque utilisé :

$ docker run -dit --name ephemere2 --mount \
  type=tmpfs,tmpfs-size=256M,dst=/root/volume nginx
a7bf7dec6fb5da992800d18db81ba2229fe0a29c7d1205b0f31314d26b32a1a9

Vérifions si l'espace disque de notre volume éphémère est effectivement limité à 256 Mo :

$ docker exec -it ephemere2 df -h
Filesystem      Size  Used Avail Use% Mounted on
overlay         902G  490G  366G  58% /
tmpfs            64M     0   64M   0% /dev
tmpfs           7.8G     0  7.8G   0% /sys/fs/cgroup
shm              64M     0   64M   0% /dev/shm
/dev/md126      902G  490G  366G  58% /etc/hosts
tmpfs           256M     0  256M   0% /root/volume
tmpfs           7.8G     0  7.8G   0% /run/secrets/credentials.d
tmpfs           7.8G     0  7.8G   0% /proc/asound
tmpfs           7.8G     0  7.8G   0% /proc/acpi
tmpfs           7.8G     0  7.8G   0% /proc/scsi
tmpfs           7.8G     0  7.8G   0% /sys/firmware

Du point de vue du conteneur, nous ne pouvons pas ajouter plus de 256 Mo de données dans /root/volume.

L'utilisation de volumes éphémères est considéré comme une bonne pratique lorsqu'on a besoin d'espace de stockage temporaire pour un conteneur et qu'on souhaite faire le ménage une fois que le conteneur s'arrête.

Utiliser l'approche classique

Même si l'option --mount est la méthode recommandée pour gérer les volumes, l'option -v reste une syntaxe valide, et vous la verrez régulièrement dans la documentation :

$ docker run -dit -p 8080:80 --name nginx-avec-vol \
  -v ~/pagesweb:/usr/share/nginx/html:ro nginx
f3eedd7b4ba996626aa096a8608ca536ab613b5bdadbcaaa28572f10602da1d8

Inspectez la section Mounts de ce conteneur :

$ docker inspect nginx-avec-vol | grep -A 10 Mounts
"Mounts": [
  {
    "Type": "bind",
    "Source": "/home/kikinovak/pagesweb",
    "Destination": "/usr/share/nginx/html",
    "Mode": "ro",
    "RW": false,
    "Propagation": "rprivate"
  }

Notez le type de montage bind qui correspond à l'ancienne méthode pour accéder aux répertoires du système hôte. Ce genre de volume offre moins de fonctionnalités que l'option --mount.

Un brin de ménage

Arrêtez tous les conteneurs en état de marche :

$ docker stop nginx-avec-vol ephemere ephemere2 \
  conteneurenlecture avecvolume avecvolume2
nginx-avec-vol
ephemere
ephemere2
conteneurenlecture
avecvolume
avecvolume2

Supprimez tous ces conteneurs :

$ docker rm nginx-avec-vol ephemere ephemere2 \
  conteneurenlecture avecvolume avecvolume2
nginx-avec-vol
ephemere
ephemere2
conteneurenlecture
avecvolume
avecvolume2

Affichez les volumes disponibles sur notre système :

$ docker volume ls
DRIVER    VOLUME NAME
local     mesdata1
local     nouveauvolume

La commande docker volume prune supprime tous les volumes locaux qui ne sont pas en cours d'utilisation :

$ docker volume prune
WARNING! This will remove all local volumes not used by at least 
one container. Are you sure you want to continue? [y/N] y
Deleted Volumes:
mesdata1
nouveauvolume

Total reclaimed space: 1.147kB

Exercices

Exercice 1

  • Créez un volume nommé volumelocal.

  • Inspectez le volume et déterminez l'emplacement exact où seront stockées les données du volume sur le système hôte.

  • Créez un fichier texte fichier.txt dans le volume, avec le contenu Ce fichier existe.

  • Démarrez un conteneur détaché nommé montagevolume basé sur l'image du serveur web Apache httpd. Montez le volume volumelocal dans le répertoire /data du conteneur.

  • Connectez-vous au conteneur montagevolume avec un shell Bash.

  • Vérifiez si le volume est disponible à l'intérieur du conteneur.

  • Vérifiez si le fichier fichier.txt existe.

  • Affichez le contenu de ce fichier.

  • Depuis le conteneur, créez un fichier texte conteneur.txt avec le contenu Créé depuis le conteneur.

  • Toujours depuis le conteneur, vérifiez si le fichier a été créé correctement.

  • Détachez-vous du conteneur.

  • Vérifiez l'existence et le contenu du fichier conteneur.txt depuis le système hôte.

Exercice 2

  • Lancez un conteneur détaché nommé ephemere et basé sur l'image du serveur Web Apache httpd. Attachez un volume éphémère au répertoire /tempdata du conteneur.

  • Inspectez le conteneur en affichant les caractéristiques du volume éphémère.


La rédaction de ces cours demande du temps et des quantités significatives de café espresso. Vous appréciez cette formation ? Offrez un café au formateur en cliquant sur la tasse.