Para almacenar los datos, un contenedor tiene diversas opciones.
Si no se le ha especificado nada en su imagen, hará uso de su propia capa de escritura, pero esta se elimina junto con el contenedor, haciendo que no perdure esa información.
Luego también tenemos los binds, que nos permiten mostrar una ruta de nuestro host y hacerla accesible para nuestro contenedor, pero este método no es el que mejor rendimiento da, y no permite hacer copias de seguridad de forma cómoda, ya que estas dependerían del anfitrión.
Pero aquí es donde entran los volúmenes.
Importante Docker 101
Este post forma parte de una serie de publicaciones formativas respecto a Docker. Si ya te manejas con ello no es necesario, pero igualmente te recomiendo revisarlo, ya que ahí encontrarás todas las publicaciones que tengan que ver con Docker 101 ordenadas:
Échales un un ojo aquí: Docker 101
Requisitos
Para seguir cómodamente este post, recomiendo lo siguiente:
- Tener un entorno con Docker, lo cual puedes obtener siguiendo este post (inglés): https://blog.runesoft.net/install-docker-and-portainer-ce/
- Haber repasado el post anterior, para entender mejor lo que es: https://blog.runesoft.net/docker-101-comandos-de-utilidad/
¿Qué es un Volumen Docker?
Es una unidad persistente de almacenamiento, creada y gestionada por Docker.
Son necesarios ya que los contenedores sin uso de volúmenes, hacen uso de su capa de escritura, la cual es volátil y va ligada al ciclo de vida del contenedor.

Estos volúmenes se crean con el comando docker volume create, o cuando levantamos un contenedor con volúmenes nuevos entre sus parámetros.

Crear un volumen
Vamos a crear uno de ejemplo, para almacenar la información de una base de datos como PostgreSQL:
sudo docker volume create postgres-data
Si ahora lanzamos un nuevo contenedor, con la imagen de PosgreSQL, podremos asignarle este nuevo volumen con el parámetro -v seguido del nombre en nuestro comando docker run:
sudo docker container run \
--name postgres-test \
-v postgres-data:/var/lib/postgresql \
-e POSTGRES_PASSWORD=testPassword123 \
-d postgres
Si lo ejecutamos en la terminal tendremos este output:

También es posible compartir el mismo volúmen entre diferentes contenedores, permitiendo que compartan información.

Volúmenes BIND
Son volúmenes que vinculan o mapean una ruta del sistema de ficheros de nuestro anfitrión a una ruta interna del contenedor. Son muy cómodos y prácticos, por ejemplo, para entornos de pruebas y contenedores de desarrollo, ya que nos permite mostrar la ruta de, por ejemplo, nuestra aplicación web, a un contenedor con NodeJS, para así desplegarla.

Para crear un volumen de este tipo haríamos uso del siguiente comando:
sudo docker volume create
--driver local \
--opt type=none \
--opt device=/home/test/postgresql \
--opt o=bind \
postgre-sql-data-bind
Y posteriormente podríamos asignárselo a nuestro contenedor. Ahora todo lo que introduzcamos en la ruta indicada en el la opción device, que en nuestro caso es /home/test/postgresql será visible y accesible desde el contenedor, pudiendo ser modificada tanto por este, como por el anfitrión.
Otros tipos de volúmenes
Hay más tipos de volúmenes, como los NFS, que son algo más complejos e involucran otros sitemas. Los veremos más adelante en posts sobre copias de seguridad de contenedores, o los stacks, que nos permitirán tener múltiples contenedores como una única aplicación orquestada.

Gestionar nuestros volúmenes
A continuación, vamos a ver una serie de situaciones y los comandos involucrados para cada una de ellas.
Ver los volúmenes (docker volume ls)
Como en muchos otros comandos, tenemos la opción de listar, que se haría:
sudo docker volumes ls
Esto nos mostraría todos los volúmenes que tenemos creados actualmente en nuestro sistema de Docker.
Saber los contenedores asignados
Otra de las situaciones habituales es querer saber qué contenedores están haciendo uso de un volumen concreto. Para esto, tendríamos que ejecutar el siguiente comando, que listará la información de nuestros contenedores y filtrará por sus volúmenes. Sólo tendríamos que indicarle el nombre:
docker ps -a --filter "volume=postgres-data" --format "table {{.Names}}\t{{.ID}}\t{{.Image}}"
En nuestro caso obtendríamos algo así por pantalla:

Este comando es muy práctico, te recomiendo tenerlo a mano.
Eliminar un volumen (docker volume rm)
Este también es muy sencillo y común a otros, haciendo uso de remove o rm:
sudo docker volume rm postgres-data
Si lo ejecutamos sobre un volumen que esté actualmente en uso, nos saldrá la siguiente advertencia:

Indicando que no se puede eliminar dado que está asignado a un contenedor. Por tanto, para poder eliminarlo, tendríamos que primero parar nuestro contenedor. Ahora podemos saber cuál es usando el comando del punto anterior (Saber los contenedores asignados):
sudo docker container stop postgres-test
Pero a pesar de pararlo, el contenedor sigue asignado, por lo que deberíamos eliminarlo:
sudo docker container rm postgres-test
Y ahora sí que podríamos eliminar el volumen
sudo docker volume rm postgres-data
Y como en otros comandos, el output es el nombre del objeto manipulado, en este caso, el de nuestro volumen eliminado:

Entorno
Si has ejecutado los comandos de este paso, te recomiendo ejecutar el siguiente comando para regenerar el entorno de pruebas de este post:
sudo docker volume create postgres-data
sudo docker container run \
--name postgres-test \
-v postgres-data:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=testPassword123 \
-d postgres
Inspeccionar un volumen (docker volume inspect)
Al igual que con los contenedores, a veces nos interesa ver la información detallada de nuestro volumen, para conocer todas sus características.
El comando es muy sencillo, tan solo ejecutaremos:
sudo docker volume inspect postgres-data
Si queremos registrar esta información o verla más en detalle, podemos exportarla a un fichero volumen-inspect.yml:
sudo docker volume inspect postgres-data >> postgres-data-inspect.yaml
Si lo abrimos, veremos lo siguiente:

Información muy básica sobre:
- Fecha de creación.
- Tipo de driver que utiliza nuestro volumen.
- Etiquetas, nos sirven para agrupar objetos en Docker, lo veremos más adelante.
- Punto de montaje, la ruta donde se encuentra el volumen en sí.
- Nombre del volumen.
- Y otras opciones.
Eliminar todos los volúmenes sin uso (docker volume prune)
Hay que tener cuidado con este comando, ya que podría eliminar volúmenes que queremos conservar pero que no se están usando actualmente.
Advertencia
Es muy cómodo en entornos de prueba y homelabs, pero no recomiendo su uso a la ligera, siempre es mejor elegir los volúmenes concretos.
Para usarlo, deberemos parar y eliminar nuevamente nuestro contenedor:
sudo docker container rm -f postgres-test
Y ahora podremos hacer el prune:
sudo docker volume prune
Como he mencionado antes es una operación delicada, y el propio Docker nos avisa:

Si le decimos que si pulsando Y, procederá a eliminarlo. La opción por defecto es N, cancelando la operación. Le daremos a que sí queremos, y nos listará los IDs de los volúmenes eliminados:

Conclusión
Ahora sabemos crear, asignar, listar, inspeccionar y eliminar nuestros volúmenes, facilitando mucho su gestión. Son un punto muy importante, ya que actúan a modo de disco duro para nuestros contenedores, y son lo único que perdurará si un contenedor se reinicia, o incluso se pueden emplear para compartir información entre varios contenedores.
Referencias
Lista de documentación, vídeos, cursos y referencias utilizadas:
- Persistiendo los datos de un contenedor: Persisting container data | Docker Docs
- Compartiendo archivos con contenedores: Sharing local files with containers | Docker Docs
- Formación obtenida del curso: https://www.udemy.com/course/docker-mastery
- Formación obtenida del curso: https://www.udemy.com/course/dive-into-cloud-native-containers-kubernetes-and-the-kcna