Importare un dump MySQL in un container Docker: da una soluzione one-liner a una soluzione flessibile e riusabile

In questo articolo vedremo come sviluppare ulteriormente una soluzione minimale per l'importazione di un dump MySQL in modo da approdare ad una soluzione più avanzata ed affidabile.

La soluzione originale

La one-liner di partenza funziona, ma è rigida e poco sicura:

docker exec -i mysql-container mysql -uuser -ppassword name_db < data.sql

Problemi della one-liner originale

  • Credenziali in chiaro sulla riga di comando (visibili con ps).
  • Nomi hardcoded (container, utente, database, file).
  • Nessun supporto a dump compressi o controllo errori della pipeline.
  • Poca portabilità tra docker e docker compose.

One-liner parametrica e più sicura

Useremo variabili e l’env MYSQL_PWD per non esporre la password tra gli argomenti del processo:

CONTAINER="mysql-container"
MYSQL_USER="user"
MYSQL_PASSWORD="supersecret"
MYSQL_DB="name_db"
FILE="data.sql"

docker exec -i -e MYSQL_PWD="$MYSQL_PASSWORD" "$CONTAINER" 
mysql -u"$MYSQL_USER" --database="$MYSQL_DB" < "$FILE"

Import di dump compressi

Rileveremo automaticamente il tipo di compressione e decomprimeremo con stream:

# .sql.gz
gzip -dc data.sql.gz | docker exec -i -e MYSQL_PWD="$MYSQL_PASSWORD" "$CONTAINER" 
  mysql -u"$MYSQL_USER" --database="$MYSQL_DB"

# .sql.xz

xz -dc data.sql.xz | docker exec -i -e MYSQL_PWD="$MYSQL_PASSWORD" "$CONTAINER" 
mysql -u"$MYSQL_USER" --database="$MYSQL_DB"

Variante con Docker Compose

Con servizi gestiti da Compose, evitiamo il TTY interattivo usando -T:

docker compose exec -T -e MYSQL_PWD="$MYSQL_PASSWORD" db
  mysql -u"$MYSQL_USER" "$MYSQL_DB" < data.sql

Script riusabile: import-mysql-dump.sh

Questo script punta a essere un “drop-in” per docker exec o docker compose exec, con supporto a file compressi e errori nella pipeline.

#!/usr/bin/env bash
set -euo pipefail

usage() {
 cat << EOF
 Uso:
 ./import-mysql-dump.sh -f dump.sql[.gz|.xz] -d DB -u USER [-p PASS] (-c CONTAINER | -s SERVICE)

 Esempi:
  ./import-mysql-dump.sh -f data.sql -d app -u app -p secret -c mysql-container
  ./import-mysql-dump.sh -f dump.sql.gz -d app -u app -p secret -s db
 Note:

 * Usa -c per docker exec (container), -s per docker compose exec (service).
 * Se -p non è passato, lo script non imposta la password (usa credenziali preconfigurate).
EOF
}

FILE=""; DB=""; USERNAME=""; PASS="${PASS-}"; CONTAINER=""; SERVICE=""
while getopts ":f:d:u:p:c:s:h" opt; do
  case "$opt" in
    f) FILE="$OPTARG" ;;
    d) DB="$OPTARG" ;;
    u) USERNAME="$OPTARG" ;;
    p) PASS="$OPTARG" ;;
    c) CONTAINER="$OPTARG" ;;
    s) SERVICE="$OPTARG" ;;
    h) usage; exit 0 ;;
    ?) echo "Opzione non valida: -$OPTARG" >&2; usage; exit 1 ;;
    :) echo "L'opzione -$OPTARG richiede un argomento." >&2; usage; exit 1 ;;
  esac
done

if [[ -z "$FILE" || -z "$DB" || -z "$USERNAME" || ( -z "$CONTAINER" && -z "$SERVICE" ) ]]; then
  echo "Parametri mancanti." >&2
  usage
  exit 1
fi

if [[ ! -f "$FILE" ]]; then
  echo "File non trovato: $FILE" >&2
  exit 1
fi

# Selettore decompressore

reader_cmd=()
case "$FILE" in
  *.sql)      reader_cmd=(cat "$FILE") ;;
  *.sql.gz|*.gz) reader_cmd=(gzip -dc "$FILE") ;;
  *.sql.xz|*.xz) reader_cmd=(xz -dc "$FILE") ;;
  *) echo "Formato non supportato (usa .sql, .sql.gz o .sql.xz)" >&2; exit 1 ;;
esac

# Comando di esecuzione dentro al container

exec_cmd_base=(mysql -u"$USERNAME" "$DB")

# Aggiungo env MYSQL_PWD se fornita

env_args=()
if [[ -n "${PASS:-}" ]]; then
  env_args=(-e "MYSQL_PWD=$PASS")
fi

if [[ -n "$SERVICE" ]]; then

# Docker Compose

  exec_cmd=(docker compose exec -T "${env_args[@]}" "$SERVICE" "${exec_cmd_base[@]}")
else

# Docker

exec_cmd=(docker exec -i "${env_args[@]}" "$CONTAINER" "${exec_cmd_base[@]}")
fi

# Esecuzione pipeline con status propagation

"${reader_cmd[@]}" | "${exec_cmd[@]}"

Come usarlo

chmod +x import-mysql-dump.sh

# Docker "puro"

./import-mysql-dump.sh -f ./data.sql -d name_db -u user -p supersecret -c mysql-container

# Docker Compose (service "db")

./import-mysql-dump.sh -f ./dump.sql.gz -d app_db -u app -p secret -s db

Integrazione comoda con Makefile

Un target per standardizzare l’import in locale e CI:

# Makefile
import:
	./import-mysql-dump.sh -f $$(FILE) -d $$(DB) -u $$(USER) -p $$(PASS) -s db

# Esempio:

# make import FILE=dump.sql.gz DB=app USER=app PASS=secret

Ottimizzazioni utili

  • Charset coerente: esporta e importa in utf8mb4 per evitare problemi di encoding.
  • Dump grandi: aumenta max_allowed_packet sul server se necessario.
  • Performance: quando possibile, disabilita controlli temporaneamente nel dump con righe tipo: SET FOREIGN_KEY_CHECKS=0; SET UNIQUE_CHECKS=0; SET AUTOCOMMIT=0; e riattivali alla fine.
  • Ripetibilità: aggiungi l’import a uno script di bootstrap del progetto o a una pipeline CI.

Verifica rapida post-import

# Conta le tabelle
docker exec -i -e MYSQL_PWD="$MYSQL_PASSWORD" "$CONTAINER" 
  mysql -u"$MYSQL_USER" -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='${MYSQL_DB}';"

# Controlla righe in una tabella chiave

docker exec -i -e MYSQL_PWD="$MYSQL_PASSWORD" "$CONTAINER" 
mysql -u"$MYSQL_USER" -N -e "SELECT COUNT(*) FROM ${MYSQL_DB}.users;"

Conclusione

Partendo dalla one-liner iniziale, hai ora un flusso robusto: parametrico, compatibile con Docker e Compose, sicuro sulle credenziali e capace di gestire dump compressi. Incapsula tutto nello script proposto e rendi l’import del database un’operazione standard, affidabile e ripetibile.

Torna su