docker - arrêtez d'utiliser des conteneurs data-only

Publié le 18/04/2016
thumbnail for this post

Le voilà le titre racoleur. Et en le lisant vous êtes en train de vous dire que je vais aller à l’encontre des bonnes pratiques. Vous vous trompez !

Non mais sérieusement, je sais qu’on vous a répété maintes fois “utilisez un conteneur data-only”, et je comprends bien l’intérêt. Cela-dit, je vais faire durer un peu le suspens et commencer par réexpliquer ce qu’est un “volume” et ce que fais un conteneur “data-only”. Je vous donnerai enfin la meilleure option pour vous en passer, et pour éviter de vous torturer la tête: non je ne vais pas non plus faire un montage de volume local tout bête. Mais je ne vais pas non plus rendre plus complexe l’opération.

En clair, on va se passer de conteneur data-only, que ce soit avec docker ou docker-compose tout en gardant la souplesse de l’opération.

EDIT: Merci à Adirelle, dans les commentaires, d’avoir précisé que les volumes nommés sont une “nouveauté” de Docker 1.9, ce qui explique l’utilisation massive de conteneurs data-only avant cette version

EDIT: Merci aussi à cedvanet pour sa question: Effectivement je n’ai pas été clair sur le fait que les conteneurs “host:conteneurs” ne sont pas le sujet de cet article, ils sont bel et bien très utiles, par exemple pour développer en partageant les sources entre votre IDE et votre conteneur, ou pour placer de la configuration. De plus, effectivement, il arrive que des “data-only containers” soient utiles, le titre du billet est peut-être trop brutal. Ce que je tente de vous démontrer n’est pas qu’un data-only container est “inutile” ou “à ne jamais utiliser”, mais qu’il faut les utiliser que certains cas.

C’est quoi un volume ?

Docker propose des volumes depuis le début. Le principe est parfois mal compris et très souvent mal utilisé. Il existe deux types de volumes très utilisés:

  • les volumes “host:conteneur”
  • les volumes “conteneur:conteneur”

Ces noms sont donnés à titre indicatif et n’existent pas dans la documentation de docker, c’est juste pour vous donner une définition simple.

Le premier, “host-conteneur” est un bête montage de répertoire local que vous choisissez vers le conteneur. Pour faire simple:

$ docker run -it --rm -v ~/Documents:/opt/docs alpine sh
> ls -la /opt/docs

Vous voyez dans le répertoire “/opt/docs” du conteneur les fichiers et répertoires “~/Documents”. C’est simple et c’est pratique. Par exemple quand on travaille en PHP, Python ou je ne sais quel autre langage, il suffit de monter les sources du projet dans le conteneur qui aura loisir de démarrer votre projet.

L’autre type de volume est plus sournois. Une image va définir un volume qui pourra alors être monté dans d’autres conteneurs. Outre le fait que la majeure partie des gens n’ont pas conscience de ce qui découle de déclarer un volume dans une image (et, brisons le suspens, c’est la finalité de l’article), c’est certainement l’une des notions les plus intéressantes de Docker.

Prenons une image simple:

FROM alpine
# on déclare un volume
VOLUME /opt/data
# pas la peine le de le laisser tourner
# les volumes sont accessibles même
# quand le conteneur ne tourne plus
CMD ["true"]

Maintenant démarrons un conteneur avec cette image:

$ docker build -t $USER/dataexample .
$ docker run -it -d --name data $USER/dataexample

Et lions les volumes de ce conteneur dans un nouveau conteneur:

$ docker run --rm -it --volumes-from data alpine sh
> touch /opt/data/foo
> exit

Maintenant, créons un nouveau conteneur qui monte, une fois de plus, les volumes du conteneur “data”

$ docker run --rm -it --volumes-from data alpine sh
> ls /opt/data
> foo

Vous l’avez compris. Le fait de déclarer un volume dans une image permet de partager un répertoire depuis le conteneur vers d’autres conteneurs.

Maintenant, pour bien poser l’idée, le conteneur “data” est un conteneur “data-only”. Il ne fait rien d’autre que de garder les données d’un répertoire qu’il a déclaré comme étant un volume.

Sans passer par la case “image”, on peut tout à faire créer un conteneur “data-only” directement:

docker run -it --volume /opt/data --name data2 busybox

Le fait de ne pas donner de point de montage local va créer un volume.

Ok, mais il stocke où alors ?

Et justement… Voilà ce qui me chagrine avec l’utilisation de conteneurs “data-only”.

Le volume est bel et bien stocké, par défaut, sur votre système. Sauf que peu d’utilisateurs en ont conscience.

Si je ne déclare pas de volume, les fichiers sont stockés dans le conteneur lui-même (en fait, dans le répertoire /var/lib/docker/SHA_IMAGE…). Mais quand on déclare un volume, docker va créer un répertoire prévu à cet effet. Que l’on soit bien clair, Docker déclare un volume, et vous pouvez le voir par vous-même:

$ docker -d run -it --name data -v /opt/data busybox
$ docker inspect  --format "{{ .Mounts }}" data
[{ab099654cc0be... /var/lib/docker/volumes/ab099654cc0be.../_data /opt/data local  true }]

$ docker volume inspect ab099654cc0be...
[
    {
        "Name": "ab099654cc0be...",
        "Driver": "local",
        "Mountpoint": "/var/lib/docker/volumes/ab099654cc0be.../_data"
    }
]

Et voilà où je veux en venir. Supprimez le conteneur “data”, et relancez la commande d’inspection de volume:

docker stop data
docker rm data

Vous venez de supprimer le conteneur, mais son volume est toujours là…

docker volume inspect ab099654cc0bef6c8b0f18e3fb8efe2b564b39832cd1db6b277f4c92bf84272c
[
    {
        "Name": "ab099654cc0be...",
        "Driver": "local",
        "Mountpoint": "/var/lib/docker/volumes/ab099654cc0be.../_data"
    }
]

On vient de créer un orphelin.

Autre souci, je ne peux plus monter ce volume:

$ docker run --rm -it --volumes-from data busybox
docker: Error response from daemon: No such container: data

Donc, voilà la première raison qui me pousse à ne pas utiliser des conteneurs “data-only”, mais plutôt d’utiliser un volume.

Ok, utilisons un volume

Maintenant que vous avez compris le principe, sachez que docker peut vous permettre de créer un volume sans passer par un conteneur. C’est là que je veux en venir !

Prenons un exemple que j’utilise beaucoup, un conteneur mongo dont je ne veux pas perdre les données. J’utilisais, avant, un répertoire perso pour y stocker les données.

Plus tard, j’ai utilisé un conteneur data-only, ce qui fonctionne bien, je ne dis pas le contraire. L’image mongo déclare un volume “/data/db”, donc je n’avais qu’à faire:

docker run --name mongodata -v /data/db -it -d busybox
docker run --rm -it --volumes-from mongodata mongo

Oui, mais c’est bête !

Voilà la meilleure façon de faire:

docker volume create --name mongodata
docker run --rm -it -v mongodata:/data/db mongo

Cette fois, je définis où je monte le volume, et je n’ai qu’un seul conteneur qui tourne: le conteneur mongo. Le volume est local (par défaut) et se trouve ici:

$ docker volume inspect mongodata
[
    {
        "Name": "mongodata",
        "Driver": "local",
        "Mountpoint": "/var/lib/docker/volumes/mongodata/_data"
    }
]

C’est bien plus efficace et ça limite largement le nombre de conteneurs à démarrer. On notera aussi la facilité à retrouver les volumes par leurs noms.

et avec docker-compose ?

Docker-compose est vraiment un outil pratique et j’attendais avec impatience l’arrivée d’une version “2” qui me permettais de jouer avec un peu plus d’options. Et justement, depuis cette version, nous pouvons créer des volumes.

Exemple:

version: '2'
services:
	database:
		image: mongo
		volumes:
		- mongodata:/data/db
volumes:
	mongodata:
		driver: local

Et voilà, un conteneur, un volume, pas la peine de créer un conteneur simplement pour garder des données au chaud.

Les drivers

Par défaut nous utilisons des volumes locaux. Mais il existe d’autres drivers qui permettent de monter le volume autrement que sur votre disque.

La liste est déjà conséquente et se trouve ici: https://docs.docker.com/engine/extend/plugins/

J’ai utilisé, pour le fun https://github.com/gondor/docker-volume-netshare qui permet d’utiliser NFS (entre autre). Ça marche bien !

Suites aux commentaires

J’ai reçu pas mal de commentaires sur Twitter, Google plus et ici qui sont trés intéressants. Je vais donc repréciser un point:

Effectivement, il peut arriver que, à l’inverse, un “volume nommé” tel que je le présente ne soit pas la panacée. Un “data-only container” peut avoir tout son intérêt, par exemple pour partager un volume avec beaucoup de conteneurs avec en plus un accès “host”.

Clairement, je ne suis pas là pour juger les utilisateurs de conteneurs data-only, ni pour définir une règle. Ce que je tenais à démontrer ici, dans ce billet de blog, c’est simplement que pour pas mal de cas, la plupart même, un conteneur “data-only” n’est pas une évidence.

En clair:

  • vous développez et devez avoir accès au répertoire de travail localement: faites un volume host:container
  • vous voulez partager un volume avec beaucoup de conteneurs et avoir accès localement aux données: faites un conteneur “data-only”
  • vous voulez persister des données mais y avoir accès localement n’est que ponctuel ou anecdotique: créez un volume nommé

Donc

Donc voilà, le titre, je le répète, “arrêtez d’utiliser des conteneurs data-only”, c’est en fait rarement utile et surtout c’est moins efficace. Docker gère très bien les volumes en tant qu’entité.

comments powered by Disqus