#!/bin/bash
DB_CONTAINER_NAME="my_prod_postgres"
BACKUP_DIR="/opt/backups/db"
DATE=$(date +%Y-%m-%d_%H-%M-%S)
# Creăm backup-ul direct din container, fără să expunem porturi
docker exec -t $DB_CONTAINER_NAME pg_dumpall -U postgres | gzip > "$BACKUP_DIR/db_backup_$DATE.sql.gz"
# Testăm imediat restore-ul într-un container temporar izolat
docker run --name pg_test -d -e POSTGRES_PASSWORD=test postgres:15-alpine
sleep 5
gunzip -c "$BACKUP_DIR/db_backup_$DATE.sql.gz" | docker exec -i pg_test psql -U postgres
if [ $? -eq 0 ]; then
echo "Restore test trecut cu succes!"
docker stop pg_test && docker rm pg_test
else
echo "Eroare critică: Backup-ul este corupt!" >&2
docker stop pg_test && docker rm pg_test
exit 1
fiAm văzut prea des oameni care își pierd datele din baza de date rulată în Docker pentru că au confundat bind mount-urile cu named volumes sau au făcut backup direct pe fișierele brute. Hai să lămurim cum facem asta corect, fără downtime și fără surprize neplăcute la restore. Am pățit-o și eu acum câțiva ani la un proiect cu vreo 15.000 de utilizatori, când un simplu script de curățenie a șters din greșeală un folder mapat greșit.
Named Volumes vs Bind Mounts: Unde punem baza de date?
Dacă folosești -v /data/db:/var/lib/mysql (bind mount) pentru o bază de date în producție, te joci cu focul. Bind mount-urile sunt excelente pentru development, când vrei să vezi instant modificările de cod din container în timp real. În schimb, vin la pachet cu mari probleme de permisiuni pe Linux și cu un overhead masiv de performanță pe macOS sau Windows.
Named volumes (de exemplu -v db_data:/var/lib/mysql) sunt gestionate exclusiv de Docker în /var/lib/docker/volumes/.
Aici avem un trade-off sincer. Named volumes oferă performanță nativă maximă și izolare totală, dar sunt mai greu de accesat direct din sistemul gazdă pentru un simplu copy-paste. Totuși, pentru baze de date (Postgres, MySQL, Mongo), named volumes reprezintă singura opțiune corectă în producție.
De ce este greșit să copiezi fișierele brute ca backup
Mulți începători cred că dacă dau un tar pe folderul de volume în timp ce baza de date rulează, au rezolvat backup-ul. Este total greșit. Bazele de date scriu în memorie și pe disc în mod constant prin procese asincrone (WAL, jurnale de tranzacții). Dacă prinzi o scriere în desfășurare în timpul copierii, backup-ul tău va fi corupt. Când vei avea nevoie de el, baza de date pur și simplu nu va mai porni.
Soluția corectă este să folosești utilitarele dedicate ale bazei de date (pg_dump, mysqldump) rulate direct în interiorul containerului.
Strategia de backup și restore testat automat
Am implementat pe un server de producție un flow destul de simplu, dar extrem de eficient. Ideea e simplă: facem backup-ul folosind docker exec, iar imediat după, ridicăm un container temporar și izolat în care importăm backup-ul proaspăt făcut. Dacă importul eșuează, scriptul ne trimite o alertă pe Slack. Așa am eliminat frica de "backup de tip Schrödinger" (cel care e valid doar până când ai nevoie de el).
Mai jos am lăsat un script de Bash pe care îl folosesc pentru un container de Postgres. Face backup-ul comprimat direct în gazdă și apoi validează restore-ul pe loc.
Pentru baze mari, de peste 50GB, procesul de validare locală prin restore zilnic poate deveni lent și consumator de resurse. În cazul ăsta, e mai bine să muți testarea restore-ului pe o mașină separată de staging sau de CI/CD.
Voi cum procedați cu bazele de date în Docker? Vă bazați pe snapshot-uri la nivel de VPS sau aveți un sistem de backup logic similar cu cel de mai sus?