Per defecte, les dades emmagatzemades dins un contenidor són efímeres. Quan el contenidor s’elimina, totes les dades desapareixen amb ell. Això és un problema greu per a aplicacions que necessiten conservar informació: bases de dades, fitxers pujats pels usuaris, configuracions, etc.
Docker resol aquest problema amb els volums, un mecanisme que permet emmagatzemar dades fora del sistema de fitxers del contenidor. En aquest article explorarem els diferents tipus de volums, com gestionar-los amb Docker Compose i les bones pràctiques per garantir la persistència i seguretat de les nostres dades.
L’efimeritat #
Quan arrencam un contenidor, Docker crea una capa de lectura/escriptura sobre la imatge base. Totes les modificacions (fitxers creats, dades guardades, configuracions canviades) s’emmagatzemen en aquesta capa. El problema és que aquesta capa:
- Desapareix quan el contenidor s’elimina: Un
docker compose downelimina els contenidors i, amb ells, totes les dades. - No es comparteix entre contenidors: Si volem que dos contenidors accedeixin a les mateixes dades, no ho podem fer amb la capa d’escriptura.
- Té un rendiment limitat: El sistema de fitxers per capes (overlay filesystem) és més lent que l’accés directe al disc.
Per evitar aquests problemes, Docker ofereix els volums.
Tipus de volums #
Docker suporta tres tipus principals de volums: temporals, amb nom i bind mounts.
Volums amb nom #
Els volums amb nom són gestionats completament per Docker. S’emmagatzemen en un directori especial del sistema (/var/lib/docker/volumes/ a Linux) i són l’opció recomanada per a la majoria de casos.
services:
db:
image: postgres:18-alpine
volumes:
- postgres-data:/var/lib/postgresql
volumes:
postgres-data:Avantatges:
- Docker gestiona la ubicació i els permisos automàticament.
- Fàcils de fer backup i restaurar.
- Funcionen igual en qualsevol sistema operatiu.
- Poden ser compartits entre múltiples contenidors.
Bind mounts #
Els bind mounts munten un directori o fitxer de l’amfitrió directament dins el contenidor. Són útils per a desenvolupament o quan necessitem accedir a les dades des de l’amfitrió.
services:
webapp:
image: nginx:alpine
volumes:
- ./src:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/nginx.conf:roLa sintaxi és <ruta_amfitrió>:<ruta_contenidor>[:opcions]. Si la ruta de l’amfitrió comença amb ./ o /, Docker la interpreta com un bind mount.
Avantatges:
- Accés directe als fitxers des de l’amfitrió.
- Ideal per a desenvolupament (canvis immediats sense reconstruir).
- Útil per muntar fitxers de configuració.
Inconvenients:
- Depèn de l’estructura de directoris de l’amfitrió.
- Problemes de permisos entre l’amfitrió i el contenidor.
- Manco portable entre diferents sistemes.
Volums tmpfs #
Els volums tmpfs emmagatzemen dades a la memòria RAM. Les dades no es persisteixen al disc i desapareixen quan el contenidor s’atura.
services:
webapp:
image: myapp:latest
tmpfs:
- /tmp
- /var/cacheCasos d’ús:
- Dades temporals que no necessiten persistència.
- Informació sensible que no volem escriure al disc.
- Millora del rendiment per a caus temporals.
Declaració de volums #
Docker Compose suporta dues sintaxis per definir volums, curta i llarga. La sintaxi curta és més concisa:
services:
db:
image: postgres:18-alpine
volumes:
- postgres-data:/var/lib/postgresql
- ./backups:/backupsLa sintaxi llarga és més explícita i permet més opcions:
services:
db:
image: postgres:18-alpine
volumes:
- type: volume
source: postgres-data
target: /var/lib/postgresql
- type: bind
source: ./backups
target: /backups
read_only: trueOpcions de muntatge #
Podem afegir opcions al final de la definició del volum:
:ro— Només lectura (read-only). El contenidor no pot modificar el contingut.:rw— Lectura i escriptura (per defecte).
volumes:
- ./config:/app/config:ro # El contenidor només pot llegir
- data:/app/data:rw # Lectura i escriptura (per defecte)Secció volumes
#
Els volums amb nom s’han de declarar a la secció volumes del nivell superior, bé en format abreujat:
volumes:
postgres-data: # Declaració mínima
services:
db:
image: postgres:18-alpine
volumes:
- postgres-data:/var/lib/postgresqlBé en format llarg:
volumes:
postgres-data: # Declaració amb opcions
driver: local
driver_opts:
type: none
device: /srv/postgresql
o: bind
services:
db:
image: postgres:18-alpine
volumes:
- postgres-data:/var/lib/postgresqlSi no especificam opcions, Docker crea un volum amb la configuració per defecte.
Cicle de vida dels volums #
Els volums amb nom es creen automàticament quan executam docker compose up. Però, a diferència dels contenidors, no s’eliminen amb docker compose down. Si, a més d’aturar i eliminar els contenidors, volem també eliminar els volums, usarem el paràmetre --volumes:
docker compose down --volumes--volumes elimina permanentment les dades. Usa-la amb precaució.
Inspecció de volums #
Podem llistar i inspeccionar els volums amb les comandes de Docker. Per llistar tots els volums usarem:
docker volume lsPer inspeccionar un volum concret usarem:
docker volume inspect metabase_postgres-dataI per veure l’espai utilitzat usarem:
docker system df -vLa sortida de docker volume inspect mostra informació detallada del volum, com el nom, el tipus de muntatge o la ubicació al disc:
[
{
"Name": "metabase_postgres-data",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/metabase_postgres-data/_data",
"Labels": {
"com.docker.compose.project": "metabase"
}
}
]Volums orfes #
Amb el temps, podem acumular volums que ja no estan associats a cap contenidor. Per llistar-los usarem:
docker volume ls -f dangling=trueI per eliminar-los usarem:
docker volume pruneExemple pràctic #
Posem en pràctica el que hem après amb Metabase, una eina de business intelligence que permet crear dashboards i analitzar dades. Metabase necessita una base de dades per emmagatzemar la seva configuració, usuaris i dashboards.
Abans de res, anem a crear el directori de feina:
mkdir --parents ~/Projects/metabase
cd ~/Projects/metabaseEstructura del projecte #
L’estructura necessària per aquest projecte és molt simple:
metabase/
├── compose.yaml
└── .envFitxer .env
#
El fitxer .env contendrà les credencials d’accés a la base de dades PostgreSQL:
# PostgreSQL
POSTGRES_USER=metabase
POSTGRES_PASSWORD=canvia-aquest-password
POSTGRES_DB=metabaseFitxer compose.yaml
#
Per a aquest exemple pràctic usarem dos serveis i un volum al nostre fitxer compose.yaml:
services:
metabase:
image: metabase/metabase:v0.59.x
container_name: metabase
depends_on:
db:
condition: service_healthy
environment:
MB_DB_TYPE: postgres
MB_DB_HOST: db
MB_DB_PORT: 5432
MB_DB_DBNAME: ${POSTGRES_DB}
MB_DB_USER: ${POSTGRES_USER}
MB_DB_PASS: ${POSTGRES_PASSWORD}
ports:
- "3000:3000"
restart: unless-stopped
db:
image: postgres:18-alpine
container_name: metabase-db
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres-data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
postgres-data:Observa com:
- El servei
dbté un volum amb nompostgres-dataper persistir les dades de PostgreSQL. - Metabase usa
depends_onambcondition: service_healthyper esperar que PostgreSQL estigui llest. - El healthcheck de PostgreSQL verifica que la base de dades accepta connexions.
Arrencada #
Usarem la comanda habitual per arrencar els serveis:
docker compose up --detachMetabase tarda uns minuts en inicialitzar-se la primera vegada. Podem seguir el progrés amb:
docker compose logs --follow metabaseQuan vegem el missatge Metabase Initialization COMPLETE, podem accedir a http://localhost:3000/ i completar la configuració inicial.
Persistència #
Per comprovar que les dades persisteixen, seguim aquests passos:
- Accedim a Metabase i cream un usuari administrador.
- Aturam i eliminam els contenidors:
docker compose down- Tornam a arrencar els serveis:
docker compose up --detach- Accedim a
http://localhost:3000/i verificam que podem iniciar sessió amb l’usuari creat anteriorment.
Les dades s’han conservat perquè estan emmagatzemades al volum postgres-data, que no s’elimina amb docker compose down.
Pèrdua de dades #
Ara vegem què passa si eliminam els volums:
docker compose down --volumes
docker compose up --detachSi accedim a http://localhost:3000/, veurem l’assistent de configuració inicial com si fos la primera vegada. Les dades s’han perdut perquè hem eliminat el volum.
Bones pràctiques #
Algunes recomanacions per gestionar volums de manera efectiva en entorns de producció:
Persistència:
- Usa sempre volums amb nom per a dades importants (bases de dades, fitxers d’usuari).
- Mai eliminis volums sense estar segur que tens una còpia de seguretat.
- Documenta quins volums conté cada projecte i què emmagatzemen.
Bind mounts:
- Reserva els bind mounts per a desenvolupament i fitxers de configuració.
- Usa
:rosempre que el contenidor no necessiti escriure. - Compte amb els permisos: l’usuari del contenidor ha de poder accedir als fitxers.
Seguretat:
- Fes còpies de seguretat regulars dels volums amb dades crítiques.
- Revisa periòdicament els volums orfes i elimina’ls.
- Si el volum emmagatzema dades sensibles, avalua usar xifrat al sistema de fitxers de l’amfitrió1, o a nivell d’aplicació, si aquesta ho suporta2.
Organització:
- Usa noms descriptius per als volums.
- Un volum per a cada tipus de dades (no barregis dades de diferents serveis).
Exercicis #
Es proposen dos exercicis pràctics per facilitar l’aprenentatge progressiu.
Exercici 1 #
Còpies de seguretat de PostgreSQL
En aquest exercici es proposa configurar un sistema de còpies de seguretat per a la base de dades PostgreSQL de Metabase. Passos:
-
Parteix de l’exemple de Metabase de l’article.
-
Crea un directori
backups/al directori del projecte.mkdir --parents ~/Projects/metabase/backups -
Modifica el
compose.yamlper afegir un bind mount que munti el directori/backups/dins el contenidor de PostgreSQL. -
Executa una còpia de seguretat manualment amb
pg_dump:docker exec metabase-db pg_dump --username=metabase \ --dbname=metabase --format=c \ --file=/backups/metabase_$(date +%Y%m%d_%H%M%S).pgc -
Verifica que el fitxer s’ha creat correctament al directori
backups/.
Pista: El bind mount ha de permetre escriptura des del contenidor.
Respostes
Crea el directori de backups:
mkdir --parents ~/Projects/metabase/backupsModifica el fitxer compose.yaml per afegir el bind mount al servei db:
services:
...
db:
...
volumes:
- postgres-data:/var/lib/postgresql
- ./backups:/backups
volumes:
postgres-data:Reinicia els serveis per aplicar els canvis:
docker compose up --detachExecuta la còpia de seguretat:
docker exec metabase-db pg_dump --username=metabase \
--dbname=metabase --format=c \
--file=/backups/metabase_$(date +%Y%m%d_%H%M%S).pgcVerifica que el fitxer s’ha creat:
ls -lh ~/Projects/metabase/backups/Exercici 2 #
Backup, catàstrofe i restauració
En aquest exercici es proposa simular una pèrdua total de dades i restaurar el sistema des d’una còpia de seguretat. Passos:
- Parteix de l’exemple de Metabase amb la còpia de seguretat de l’exercici anterior.
- Assegura’t de que tens una còpia de seguretat recent al directori
~/Projects/metabase/backups/. - Accedeix a Metabase i crea alguna configuració addicional, e.g., afegeix un document usant l’opció
New > Document, que pots guardar a dins laPersonal collectiondel teu usuari. - Fes una nova còpia de seguretat amb
pg_dump.docker exec metabase-db pg_dump --username=metabase \ --dbname=metabase --format=c \ --file=/backups/metabase_$(date +%Y%m%d_%H%M%S).pgc - Simula una catàstrofe eliminant tots els volums:
docker compose down --volumes - Arrenca els serveis de nou (es crearà un volum buit):
docker compose up --detach - Espera que PostgreSQL estigui llest i restaura la còpia de seguretat:
docker exec metabase-db pg_restore --username=metabase \ --dbname=metabase --clean --if-exists --jobs=2 \ /backups/metabase_YYYYMMDD_HHMMSS.pgc - Accedeix a
http://localhost:3000/i verifica que les dades s’han recuperat.
En un entorn de producció, les còpies de seguretat s’executen de forma automatitzada, e.g., amb cron job o un servei dedicat, i s’emmagatzemen en un lloc segur fora del servidor, e.g., un bucket d’S3 o un servidor de backups.
Respostes
Assegura’t que tens l’stack de Metabase funcionant amb dades (usuari creat, configuració inicial completada amb base de dades de prova per defecte de Metabase).
Fes una còpia de seguretat:
docker exec metabase-db pg_dump --username=metabase \
--dbname=metabase --format=c \
--file=/backups/metabase_pre_catastrofe.pgcVerifica que el backup existeix:
ls -lh ~/Projects/metabase/backups/Simula la catàstrofe:
docker compose down --volumesArrenca els serveis amb volums nous (buits):
docker compose up --detachEspera que PostgreSQL estigui llest (pots verificar-ho amb els logs):
docker compose logs --follow dbQuan vegis database system is ready to accept connections, restaura el backup:
docker exec metabase-db pg_restore --username=metabase \
--dbname=metabase --clean --if-exists --jobs=2 \
/backups/metabase_pre_catastrofe.pgcAccedeix a http://localhost:3000/ i inicia sessió amb les credencials que tenies abans de la catàstrofe. Hauries de veure els documents de prova que vares crear, o configuracions, preguntes i dashboards.
-
Per exemple, usant fscrypt per a xifrar fitxers o LUKS per a xifrar el disc. ↩︎
-
Per exemple, si usam PostgreSQL, usant Transparent Data Encryption. ↩︎