Volúmenes en Docker

Volúmenes en Docker

Docker Volume

Antes de entrar en materia, debo comentar que este post se inicio por un problema que tuve alguna vez para montar un volumen NFS en un contenedor docker directamente, específicamente un volumen EFS (Elastic File System) de AWS. Debo reconocer que en un principio fue por desconocimiento, pero mas adelante me encontré con una serie de problemas por lo que opté finalmente por montar el volumen en la máquina host y luego montar el volumen en el contenedor.

Bueno, finalmente encontré una buena manera de solucionar este problema a través de los volúmenes de Docker.

Binding y Volúmenes

Debemos recordar que un contenedor es inmutable y efímero en si. Con esto quiero decir que un contenedor en si mismo no puede cambiar la imagen sobre la cual se esta ejecutando.  El contenedor mientras corre puede generar información modificar su filesystem cambiar todo lo que quiera mientras este en ejecución, pero una vez que este se termina y se remueve, toda la información escrita por el contenedor se pierde. Una vez que se inicia un nuevo contenedor con la misma imagen, nada de las modificaciones o de los datos generados se guardarán y por ende se perderán.

Binding

Para poder persistir los datos, se pueden montar directorios o archivos del filesystem de la máquina host, dentro del contenedor. Esto se llama binding. Al realizar un binding cualquier cambio que se haga dentro del contenedor, también podría verse reflejado por el archivo origen (host). Ejm.


docker run -d -v /opt/config/nginx.conf:/etc/nginx/conf.d/site.conf -p 80:80 nginx
#/opt/config/nginx.conf
# Archivo de configuración del SO host
#/etc/nginx/conf.d/site.conf
# Es el archivo que hace referencia a la máquina host, pero será usado en el container.
# En este caso será la configuración en Nginx.

Este mismo método se puede utilizar para montar directorios dentro del contenedor, de modo que los objetos o archivos generados por el contenedor puedan persistir en la máquina host y puedan ser usados en otro contenedor.

Un buen ejemplo de esto son las bases de datos. En general uno querría que los datos generados por el contenedor que la usa puedan ser persistidos y que no se pierdan en caso de tener que volver a crear el contenedor. Otra forma de persistir los datos son los volúmenes de docker

Volúmenes

Los volúmenes de docker son una forma especial de crear espacios para persistencia de archivos y la principal diferencia es que estos volúmenes pueden usar una serie de  drivers que permiten la integración de un volumen con algún tipo especifico de filesystem como por ejemplo Azure Filesystem, S3 de AWS, IPFS entre algunos. El driver por defecto es el local, que permite el uso del propio filesystem (ninguna novedad) como volumen.

Cada volumen es identificado con una etiqueta y no es necesario especificar un path en el SO host, ya que este volumen tiene una zona donde se crean todos los volúmenes dentro del filesystem del host. Una vez creado un volumen, puede ser montado dentro de un contenedor asociándolo a un directorio dentro del contenedor. Ejm.


docker run -d -v data:/var/lib/mysql -p 3306:3306 mysql

En el ejemplo anterior, se ha asociado un volumen llamado “data” en el path “/var/lib/mysql” dentro del contenedor. Esto quiere decir que todo los datos que se creen en ese path serán persistidos dentro de ese volumen.

¿ Pero, de dónde salió “data” ?

Bueno vamos a hacer un flashback al momento en que hemos definido tal volumen. Primero, debemos mencionar que utilizaremos el comando “volume” de docker para crear este volumen.

docker volume create data
data
docker volume ls
VOLUME NAME
local data

Con el comando anterior, hemos creado un volumen y el cual se llama “data”.  Con el comando “volume ls” podemos ver que se creo un volumen con el driver “local” que es el que viene por defecto.

Anteriormente hemos mencionado que el volumen se almacena localmente dentro del host en un repositorio local dentro del filesystem. La ruta en la que se guardan todos los volúmenes es “/var/lib/docker/volumes”. Para saber en que lugar exacto se ha creado el volumen se puede usar el comando “docker volume inspect {nombre_volumen}” el cual retornará un Json con la información.


docker volume inspect data
[ 
{ 
"CreatedAt": "2018-06-01T03:47:14Z",
 "Driver": "local",
 "Labels": {}, 
"Mountpoint": /var/lib/docker/volumes/data/_data", "Name": "data", 
"Options": {}, 
"Scope": "local"
 }

Dentro de la información de salida se puede notar la propiedad “Mountpoint” tiene la ruta donde localmente se guarda la información de ese volumen. Independiente del tipo de driver que se seleccione, siempre habrá una zona local donde se guardarán los datos dentro del filesystem local.

¿Qué pasa con el NFS?

Bueno, después de una gran explicación podrán entender como he llegado hasta el NFS… bueno, finalmente no hay un driver específico para NFS, pero descubrí que usando el driver local podemos montar un volumen NFS perfectamente, ya que este driver usa el sistema de archivos local como “mount” para trabajar con el sistema de archivos.

Precisamente uno de los parámetros que se necesita par montar un volumen a través del comando mount es “type”, el cual define que tipo de volumen queremos montar en nuestro SO. El driver local acepta varias opciones y entre ellas se encuentra el tipo de FS que se desea montar.  Para montar un volumen NFS debemos especificar el tipo y las opciones para montar. En nuestro caso, queremos montar un volumen de EFS de AWS, que no es mas que un volumen NFS con almacenamiento virtualmente infinito. Las opciones para montarlo son las mismas que un volumen NFS normal, mas algunas recomendaciones de AWS.

Las opciones para el driver local se deben pasar como un lista de tipo KEY=VALUE  o en una lista separadas por comas.

Nuestro caso sería:

tipo: NFS
Opciones:  rw,nfsvers=4.1,rsize=1048576,wsize=1048576,timeo=600
Host:  fs-9999999999.efs.us-east-2.amazonaws.com

las opciones del driver que usaremos son:

type: Tipo de FS
o:  Opciones para montar NFS
device: Path del volumen NFS

Finalmente el comando docker para crear un volumen NFS:

docker volume create --driver local \
--opt type=nfs \
--opt o=addr=fs-79854200.efs.us-east-2.amazonaws.com,rw,nfsvers=4.1,rsize=1048576,wsize=1048576,timeo=600 \
--opt device=:/ nfs

Con el comando anterior hemos creado un volumen de tipo NFS. En el momento de crearlo, este no monta el volumen, esto se hace efectivo cuando se ejecuta un contenedor. Una vez que se inicia con el comando run el contenedor realiza el mount del volumen NFS. Así:

docker run -d -v nfs:/mnt --name=nginx nginx

…Y Finalmente

Queda bastante claro que el manejo de volúmenes para persistencia de datos es algo que Docker ha abordado bastante bien. Sin embargo se debe dar una buena mirada en detalle en cada uno de estos drivers y así poder sacar provecho de estas funcionalidades, por que lo mas probable es que ya este resuelto.

En el caso particular que intentamos resolver en un principio los volúmenes se montaban sobre el host y luego se montaba como una carpeta (binding) el problema que derivaba de esto, es que si hay un cambio en el FS como or ejemplo el nombre de host, este se debía re configurar en la máquina host, lo que finalmente hacia mas complicado su automatización. Los contenedores no tienen por que saber cual finalmente el origen del FS y su configuración, esto se puede agregar directamente sobre los scripts que despliegan dejando el control de estas actividades a Docker.  En este caso usamos NFS por que AWS Elastic File System finalmente esta basado en el protocolo NFS Versión 4.

Finalmente lo mas recomendado en estos casos, es dejar que Docker se encargue de todas estas actividades ya que además todo esto facilita el trabajo de configuración y automatización al permitir el uso de scripts y archivos Dockerfile o Compose de manera transparente. Con esto se busca minimizar los puntos de fallas dejando que uno solo tenga el control, en este caso Docker.