gjp311 - stock.adobe.com
Containers Docker : quelles sont les options pour le stockage persistant
Conçu à l’origine pour faciliter le déploiement d’applications sans état, Docker est de plus en plus utilisé pour des applications ayant besoin de stocker des données de façon persistante. Dans cet article, nous faisons le point sur les différentes façons de persister des données avec Docker.
La technologie de containers Docker s’est imposée au cours des deux dernières années comme une alternative flexible et légère à la virtualisation pour le déploiement d’applications web et d’applications Cloud. Du fait de leur nature autonome, les conteneurs Docker peuvent être déployés simplement sur n’importe quel serveur d’un cluster et ce, sans dépendance spécifique à un hôte.
Un conteneur Docker est en fait un mécanisme d’encapsulation du code et des processus d’une application qui fait croire à ces processus qu’ils ont le contrôle de l’ensemble d’un hôte. Un conteneur est de fait un moyen de virtualisation - à ceci près que contrairement à la virtualisation, l’ensemble des conteneurs d’un hôte partagent les services de base de l’OS sous-jacent. Théoriquement, le niveau d’isolation est donc inférieur à celui offert à une VM, ne serait-ce que parce que les conteneurs partagent les services du noyau, les services réseau ou les services de stockage délivrés par l’OS hôte. Mais il y a un avantage à cette légèreté : il est théoriquement possible de déployer des centaines de conteneurs sur un même hôte.
A l’origine, les conteneurs ont été conçus pour abriter des applications sans état, ne nécessitant aucune persistance de données.
Lorsqu’une image Docker est exécutée, le Docker Engine crée un système de fichiers temporaire sur lequel sont stockés l’ensemble des composants et des données générées par le conteneur. Il s’appuie pour cela sur les capacités copy-on-write de l’Union File System. Dans la pratique, la mise en œuvre de ce mécanisme de copy-on-write signifie que lorsque la même image est instanciée à de multiples reprises sur un même hôte, le Docker Engine ne crée par une copie complète de l’image mais ne stocke que les modifications apportées par chaque image en cours d’exécution. Ce mécanisme permet non seulement d’économiser de l’espace, mais aussi de gagner du temps, notamment au démarrage du conteneur - ce qui permet, dans certains cas, d’instancier un conteneur en quelques dixièmes de seconde.
Les conteneurs ont à l’origine été pensés comme un moyen de déployer à grande échelle des microservices, et donc pour l’essentiel, des applications sans état ne nécessitant pas de persistance de leur stockage. Dans la pratique, cela signifie que par défaut, à l’arrêt d’un conteneur, l’espace qu’il occupait et les données générées à l’intérieur du conteneur sont effacés. Cela convient bien à un microservice, mais ne répond pas du tout aux besoins d’applications nécessitant de persister leurs données comme des bases de données ou des applications plus complexes. Or, Docker est aussi de plus en plus utilisé pour déployer ce type d’applications.
Docker a donc prévu plusieurs mécanismes pour persister des données avec sa technologie : les volumes et les Docker Data Volume Containers. Et il est aujourd’hui possible de gérer aussi bien des volumes locaux à l’hôte docker que des volumes stockés sur des baies SAN ou NAS. Dans les quelques lignes ci-dessous, nous faisons le point sur les différentes possibilités de persistance offertes par Docker.
Docker Volume
Un volume Docker fournit un mécanisme pour assurer la persistance des données d’un conteneur ou lui permettre « d’échanger » des données avec d’autres conteneurs partageant le même volume.
Un volume Docker est initialisé lors de la création d’un conteneur et est monté dans le conteneur comme un système de fichiers. Si l’image du conteneur contient déjà des données dans le répertoire dans lequel est monté le système de fichiers, ces données sont copiées dans le nouveau volume lors de son initialisation. Les volumes de données ont l’avantage particulier de survivre à l’arrêt du conteneur et par défaut, Docker n’efface pas les volumes, même lors de la destruction d’un conteneur associé. En fait, il est même impossible d’effacer un volume tant que ce dernier est référencé par un conteneur.
Il est possible de créer un volume avec la commande Docker volume create
Il est aussi possible de créer ou d’attacher un volume à un conteneur via les commandes docker create et docker run command. Il est à noter que, par défaut, un volume est monté en mode lecture-écriture, mais qu’il est possible de limiter l’accès au seul mode lecture. La commande ci-dessous crée par exemple un conteneur nommé web depuis l’image training/webapp avec un volume baptisé /webapp stocké sur l’hôte local.
$ docker run -d -P --name web -v /webapp training/webapp python app.py
Plusieurs options sont disponibles aujourd’hui pour stocker un volume :
- La première consiste à monter un répertoire du système d’exploitation hôte comme un data volume. Cette option est simple d’utilisation, mais elle est à utiliser avec une grande précaution en production puisqu’une défaillance du système hôte se traduit par l’indisponibilité du volume. Le fait de monter un répertoire local est toutefois pratique pour les développeurs, que ce soit pendant la phase de développement ou pendant la phase de test. Il est à noter qu’il est aussi possible de monter un fichier unique comme un volume. La commande ci-dessous crée par exemple un conteneur baptisé web à partir de l’image training/webapp et monte le contenu du répertoire /src/webapp de l’hôte dans le répertoire /webapp du conteneur.
$ docker run -d -P --name web -v /src/webapp:/webapp training/webapp python app.py
- La seconde possibilité est de monter un volume de stockage partagé (iSCSI, FC ou NFS) comme data volume. Docker inclut un concept de plug-ins qui permet aux principaux fabricants de baies de stockage d’intégrer leurs systèmes de stockage avec la technologie de conteneurs. Le bénéfice évident de cette approche est que le volume n’est plus lié à un hôte, mais accessible à tous les hôtes d’un cluster (pour peu que ces hôtes aient accès à la baie de stockage et que le plug-in de stockage adapté soit déployé).
- La commande ci-dessous crée un conteneur baptisé web depuis l’image training/webapp et crée également un volume attaché baptisé mon volume en utilisant le pilote convoy (qui supporte notamment l’accès en mode NFS et l’accès aux volumes EBS d’Amazon) :
$ docker run -d -P \
--volume-driver=convoy \
-v mon-volume :/webapp \
--name web training/webapp python app.py
Data Volume Container
Docker propose également un mécanisme pour permettre de partager des données persistantes entre conteneurs. Baptisée Data Volume Container, ce mécanisme consiste à créer un conteneur nommé puis à lui attacher des volumes. Il sera ensuite possible de consommer ces volumes depuis d’autres conteneurs. Docker donne pour exemple le cas suivant, qui crée un conteneur baptisé dbstore à partir de l’image training/postgres.
$ docker create -v /dbdata --name dbstore training/postgres /bin/true
En utilisant le paramètre --volumes-from avec Docker run, il devient possible de monter le volume /dbdata du conteneur dbstore dans d’autres conteneurs. C’est ce que réalisent les deux commandes suivantes en créant deux conteneurs baptisés db1 et db2 :
$ docker run -d --volumes-from dbstore --name db1 training/postgres
$ docker run -d --volumes-from dbstore --name db2 training/postgres
Dans le cas présent, le contenu du volume /dbdata remplace celui éventuellement présent dans l’image training/postgres.Cela permet aux conteneurs db1 et db2 de démarrer avec une base de données de référence identique.
Il est à noter que la suppression des conteneurs montant les volumes n’entraîne pas la suppression du volume lui-même. Pour éviter de se retrouver avec des volumes orphelins, il faut donc explicitement utiliser la commande docker rm –v sur le dernier conteneur référençant le volume. Docker indique qu’il est possible d’inventorier les volumes orphelins avec la commande docker volume ls -f dangling=true puis d’utiliser ensuite docker volume rm <nom_du_volume > pour supprimer les volumes indésirables.
Il est à noter qu’outre les problèmes de volumes orphelins, l’utilisation des data containers pose d’autres problèmes potentiels. Par exemple, Docker n’offre pas de mécanisme de sécurité additionnel pour assurer la sécurité des volumes. Il faut donc veiller à ce que les permissions pour les utilisateurs dans les conteneurs correspondent à celles du serveur hôte. Docker n’implémente pas non plus de mécanisme additionnel de verrouillage. Lorsque plusieurs conteneurs accèdent au même volume sur un hôte, les verrous sur les fichiers doivent être gérés par les applications tournant dans les conteneurs, sous peine d’éventuels risques de corruption. Ces problèmes n’existent pas sur un stockage partagé en réseau.