13-Persistencia de Datos en MySQL con Docker Volumes

 

Persistencia de Datos en MySQL con Docker Volumes

Introducción

En este tutorial configuraremos un volumen persistente para la base de datos MySQL, asegurando que los datos no se pierdan al detener o eliminar los contenedores.


Paso 1: Importancia de los Volúmenes Persistentes

¿Por qué necesitamos un volumen?

Sin VolumenCon Volumen
Los datos se pierden al detener el contenedorLos datos persisten entre reinicios
Cada docker compose down elimina la base de datosLa base de datos mantiene su estado
No apto para desarrollo ni producciónIdeal para desarrollo y producción

Paso 2: Verificar Configuración Actual

2.1 Ver estado de los contenedores

bash
# Verificar contenedores activos
docker compose ps

# Ver volúmenes existentes
docker volume ls

2.2 Estructura actual de docker-compose.yml

bash
# Ver contenido actual
cat docker-compose.yml

Paso 3: Agregar Volumen al Servicio de Base de Datos

3.1 Abrir el archivo de configuración

bash
nano docker-compose.yml

3.2 Modificar el servicio db

Agregar la sección volumes al servicio db:

yaml
services:
  db:
    image: mysql:8.0
    container_name: laravel_db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE:-laravel}
      MYSQL_USER: ${DB_USERNAME:-laravel}
      MYSQL_PASSWORD: ${DB_PASSWORD:-secret}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootsecret}
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - laravel

Explicación:

  • mysql_data: Nombre del volumen (debe coincidir con la definición)

  • /var/lib/mysql: Ruta donde MySQL almacena los datos dentro del contenedor


Paso 4: Definir el Volumen en la Sección Global

4.1 Agregar la sección volumes al final del archivo

yaml
volumes:
  mysql_data:

Archivo completo con la sección:

yaml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        USERNAME: ${USER:-laravel}
        USERID: ${UID:-1000}
    image: laravel-app
    container_name: laravel_app
    restart: unless-stopped
    working_dir: /var/www/html
    volumes:
      - ./:/var/www/html
    networks:
      - laravel
    depends_on:
      - db
      - redis

  nginx:
    image: nginx:alpine
    container_name: laravel_nginx
    restart: unless-stopped
    ports:
      - "8000:80"
    volumes:
      - ./:/var/www/html
      - ./docker/nginx:/etc/nginx/conf.d
    networks:
      - laravel
    depends_on:
      - app

  db:
    image: mysql:8.0
    container_name: laravel_db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE:-laravel}
      MYSQL_USER: ${DB_USERNAME:-laravel}
      MYSQL_PASSWORD: ${DB_PASSWORD:-secret}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootsecret}
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - laravel

networks:
  laravel:
    driver: bridge

volumes:
  mysql_data:

Paso 5: Verificar la Configuración

5.1 Verificar sintaxis

bash
# Validar el archivo docker-compose.yml
docker compose config

# Si hay errores, revisar indentación (2 espacios, no tabs)

5.2 Errores comunes al definir volúmenes

yaml
# ❌ INCORRECTO (indentación incorrecta)
volumes:
- mysql_data:/var/lib/mysql

# ❌ INCORRECTO (nombre no coincide)
volumes:
  - mysql-database:/var/lib/mysql  # Nombre diferente

# ✅ CORRECTO
volumes:
  - mysql_data:/var/lib/mysql

Paso 6: Comandos para Gestionar Volúmenes

6.1 Levantar contenedores con volumen

bash
# Levantar los servicios (se crea el volumen automáticamente)
docker compose up -d

# Verificar que el volumen se haya creado
docker volume ls

Salida esperada:

text
DRIVER    VOLUME NAME
local     laravel_mysql_data

6.2 Ver información del volumen

bash
# Inspeccionar el volumen
docker volume inspect laravel_mysql_data

Salida:

json
[
    {
        "CreatedAt": "2024-xx-xxTxx:xx:xxZ",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "laravel",
            "com.docker.compose.version": "2.xx.x"
        },
        "Mountpoint": "/var/lib/docker/volumes/laravel_mysql_data/_data",
        "Name": "laravel_mysql_data",
        "Options": null,
        "Scope": "local"
    }
]

6.3 Ubicación física del volumen en el host

bash
# Ver dónde se almacenan los datos
ls -la /var/lib/docker/volumes/laravel_mysql_data/_data/

Paso 7: Prueba de Persistencia

7.1 Crear datos en la base de datos

bash
# Entrar al contenedor de MySQL
docker compose exec db mysql -u root -prootsecret

# Dentro de MySQL
CREATE DATABASE test_persist;
USE test_persist;
CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100));
INSERT INTO users (name) VALUES ('Test User');
SELECT * FROM users;
EXIT;

7.2 Detener y eliminar contenedores

bash
# Detener contenedores SIN eliminar el volumen
docker compose down

# Verificar que el volumen aún existe
docker volume ls

7.3 Volver a levantar contenedores

bash
docker compose up -d

# Verificar que los datos persisten
docker compose exec db mysql -u root -prootsecret -e "SELECT * FROM test_persist.users;"

Paso 8: Actualizaciones y Buenas Prácticas (2024-2026)

✅ Volúmenes con drivers personalizados

yaml
volumes:
  mysql_data:
    driver: local
    driver_opts:
      type: none
      device: /home/user/docker/mysql  # Ruta alternativa
      o: bind

✅ Volúmenes nombrados vs bind mounts

TipoSintaxisCaso de uso
Volumen nombradomysql_data:/var/lib/mysqlPersistencia general
Bind mount./mysql-data:/var/lib/mysqlDesarrollo, acceso directo

✅ Backup automático con volume

bash
# Crear backup del volumen
docker run --rm -v laravel_mysql_data:/data -v $(pwd):/backup alpine \
  tar czf /backup/mysql_backup_$(date +%Y%m%d).tar.gz -C /data .

✅ Restaurar backup

bash
docker run --rm -v laravel_mysql_data:/data -v $(pwd):/backup alpine \
  tar xzf /backup/mysql_backup_20240101.tar.gz -C /data

✅ Volumen para múltiples servicios

yaml
volumes:
  mysql_data:
  redis_data:
  app_storage:

Paso 9: Comandos Avanzados de Volúmenes

9.1 Ver espacio usado por volúmenes

bash
docker system df -v

9.2 Eliminar volumen huérfano

bash
# Eliminar volumen específico
docker volume rm laravel_mysql_data

# Eliminar volúmenes no usados
docker volume prune

9.3 Copiar datos entre volúmenes

bash
# Copiar datos de un volumen a otro
docker run --rm -v source_volume:/source -v target_volume:/target alpine \
  cp -av /source/. /target/

Errores Comunes y Soluciones

ErrorCausaSolución
volume name conflictsNombre en usoUsar nombre único o docker volume rm
permission deniedPermisos de escriturasudo chown -R 1001:1001 /var/lib/docker/volumes/...
no such volumeNo definido en volumes:Agregar la sección global
data lostVolumen no montadoVerificar indentación en YAML
port conflictPuerto ocupadoCambiar puerto en ports:

Resumen: Configuración de Volúmenes

yaml
# Estructura de volúmenes en docker-compose.yml
services:
  db:
    volumes:
      - nombre_volumen:/ruta/en/contenedor   # Montar volumen

volumes:
  nombre_volumen:                             # Declarar volumen
    # Opciones opcionales

Diagrama de Persistencia




Comentarios

Entradas más populares de este blog

12. Hola Mundo en Docker.

11¿Qué es Docker? y ¿Por qué debo saberlo?

14. Publish and Detached modes