Al usar Docker, a menudo surge la necesidad de respaldar datos almacenados en un volumen o de copiarlo a otro volumen para pruebas.

Si bien podría pensar en el comando docker cp, este comando solo admite la copia entre el sistema de archivos del contenedor y el host, sin permitir el acceso directo a los volúmenes gestionados por Docker.

En este artículo, presentaremos la forma correcta de copiar volúmenes de Docker. No solo enumeraremos los comandos, sino que nos enfocaremos en por qué debemos usar este método, centrándonos en el principio detrás de él. Comprender este principio le permitirá aplicar los comandos de forma natural cada vez que lo necesite, sin tener que memorizarlos.

El principio clave es simple.

La forma más segura y 'dockeriana' de manipular volúmenes es utilizar un 'contenedor temporal' que tenga acceso al volumen como puente (bridge).


1. Copiar un volumen existente a un nuevo volumen (Volume-to-Volume)



Este es el escenario más común. Puede que desee clonar el volumen db_data en un volumen de prueba db_data_test.

🤔 ¿POR QUÉ: por qué usar un contenedor temporal?

Los volúmenes de Docker se almacenan en alguna parte del sistema de archivos del host (por ejemplo, /var/lib/docker/volumes/...), pero el daemon de Docker gestiona esta ruta. No se recomienda el acceso directo a esta ruta para usar el comando cp, ya que podría causar problemas de permisos o de consistencia de datos.

En su lugar, ejecutamos un contenedor temporal que monta los dos volúmenes simultáneamente.

  1. El contenedor monta el volumen de origen (source_volume) en la ruta /from.

  2. El contenedor monta el volumen de destino (new_volume) en la ruta /to.

  3. El contenedor ejecuta un simple comando de Linux (cp o tar) que copia todos los datos de /from a /to en cuanto se inicia.

  4. Una vez que el comando finaliza, el contenedor se elimina automáticamente (--rm).

Este contenedor se utiliza únicamente como una 'herramienta' para la copia de datos.

🚀 ¿CÓMO: ejemplo de comando?

A continuación se muestra un ejemplo que copia los datos del volumen source_data al volumen target_data.

  1. Crear un volumen de prueba (opcional)
docker volume create source_data
docker volume create target_data
# (asumiendo que hay datos en source_data)
  1. Copia usando un contenedor temporal
docker run --rm \
       -v source_data:/from \
       -v target_data:/to \
       alpine \
       sh -c "cp -a /from/. /to/"

💡 Explicación del comando

  • docker run --rm: La bandera --rm significa que se eliminará inmediatamente el contenedor cuando complete su tarea y se detenga. Esto es esencial para trabajos temporales.

  • -v source_data:/from: Monta el volumen source_data en el directorio /from dentro del contenedor.

  • -v target_data:/to: Monta el volumen target_data en el directorio /to dentro del contenedor.

  • alpine: Se utiliza la imagen de Linux alpine que incluye utilidades básicas como cp o sh y tiene un tamaño muy pequeño.

  • sh -c "...": Es el comando que se ejecutará cuando se inicie el contenedor alpine.

  • cp -a /from/. /to/:

    • -a (archivar): Esta bandera es crucial. En lugar de una copia simple (cp -r), se preservan todas las propiedades como la propiedad, permisos y sellos de tiempo. Esto es muy importante al manejar datos sensibles como archivos de bases de datos.

    • /from/.: Se refiere a toda la contenido (incluidos los archivos ocultos) dentro de /from, no al directorio /from en sí.


2. Copiar datos de un volumen al sistema de archivos del host (respaldo)

Se utiliza cuando desea respaldar los datos de un volumen a un directorio específico en su máquina local o servidor, por ejemplo, como un archivo .tar.

🤔 ¿POR QUÉ: cómo funciona?

El principio es el mismo que en el primer caso. La única diferencia es que ahora el destino es un 'directorio del host' en lugar de un 'volumen de Docker'.

Docker admite la función de montaje por enlace (Bind Mount), que permite montar un directorio específico del host en el contenedor.

  1. El contenedor monta el volumen de origen (source_volume) en la ruta /data.

  2. El contenedor monta un directorio específico del host ($(pwd)/backup) en la ruta /backup.

  3. El contenedor copia (o comprime) el contenido de /data al directorio /backup.

  4. Aunque esta operación se realiza dentro del contenedor, /backup está vinculado a un directorio real del host, por lo que los resultados permanecen en el host.

🚀 ¿CÓMO: ejemplo de comando?

Copia los datos del volumen source_data al directorio backup en la ubicación actual ($(pwd)).

# Crear un directorio en el host para la copia de seguridad
mkdir -p $(pwd)/backup

docker run --rm \
       -v source_data:/data:ro \
       -v $(pwd)/backup:/backup \
       alpine \
       cp -a /data/. /backup/

💡 Explicación del comando

  • -v source_data:/data:ro: Se ha añadido la bandera :ro (Solo lectura). Dado que el respaldo solo necesita leer los datos, es recomendable montarlo como solo lectura para evitar modificar accidentalmente el volumen de origen.

  • -v $(pwd)/backup:/backup: A diferencia de source_data (nombre del volumen), el directorio que comienza con / o incluye $(pwd) (ruta actual) representa un directorio del host.


3. Consejos clave para copiar (Tips)



Consejo 1: Copia eficiente usando tar (respaldo comprimido)

Usar tar en lugar del comando cp ofrece varios beneficios. Es particularmente útil cuando se desea empaquetar datos en un archivo de archivo (.tar.gz) para respaldarlos.

De Volumen a Volumen (usando tar)

Puede ser más rápido que cp, especialmente eficiente cuando hay muchos archivos.

docker run --rm \
       -v source_data:/from \
       -v target_data:/to \
       alpine \
       sh -c "cd /from && tar -cf - . | (cd /to && tar -xf -)"
  • cd /from && tar -cf - .: Navega a /from y empaqueta el contenido del directorio actual (.) con tar, enviándolo a salida estándar (-) en lugar de a un archivo.

  • | (cd /to && tar -xf -): Toma la salida estándar con el pipe (|) y descomprime en el directorio /to con tar (x).

De Volumen a Host (respaldo comprimido)

Comprime el contenido del volumen source_data en un archivo backup.tar.gz para almacenarlo en el host.

docker run --rm \
       -v source_data:/data:ro \
       -v $(pwd):/backup \
       alpine \
       tar -czf /backup/backup.tar.gz -C /data .
  • tar -czf /backup/backup.tar.gz ...: c (crear), z (gzip), f (archivo) para crear el archivo /backup/backup.tar.gz.

  • -C /data .: Esta parte es crucial. Cambie primero al directorio /data y luego comprimimos todo lo que hay dentro (.

    ) Así evitamos incluir el directorio /data en el archivo tar.

Consejo 2: Restaurar archivos del host al volumen

La restauración, que es lo opuesto al respaldo, utiliza el mismo principio. Solo necesita invertir la dirección del cp.

# Restaurar los datos del directorio $(pwd)/backup al volumen new_data
docker run --rm \
       -v new_data:/data \
       -v $(pwd)/backup:/backup:ro \
       alpine \
       cp -a /backup/. /data/

Resumen

La clave para manejar volúmenes en Docker es "usar contenedores como herramientas". En lugar de tocar directamente los archivos de volumen en el host, se ejecuta un contenedor temporal con los volúmenes y directorios requeridos, donde se pueden ejecutar de manera segura comandos estándar de Linux como cp o tar.

Si recuerda este principio, podrá copiar, respaldar y restaurar datos de volúmenes con libertad en cualquier situación.