Salta el contingut

Pas 6 - Persistència amb volums

Als passos 3 i 5 hem comprovat que tot el que un contenidor escriu es perd quan s'elimina. Per a un servidor web de proves no importa, però per a una base de dades és inacceptable: actualitzar MySQL a una versió nova implica eliminar el contenidor vell i crear-ne un de nou. Els volums permeten guardar dades fora del contenidor.

Objectius del pas

  • Distingir els tres tipus de muntatge: volums, bind mounts i tmpfs.
  • Servir una web pròpia amb NGINX muntant un directori de l'amfitrió.
  • Fer persistent una base de dades amb un volum amb nom.
  • Descobrir els volums anònims i fer una còpia de seguretat d'un volum.

6.1 Tres maneres de muntar dades

flowchart LR
    subgraph AMF[Amfitrio]
        D[Directori del projecte<br/>./web]
        V[("Volum gestionat per Docker<br/>/var/lib/docker/volumes")]
        R[Memoria RAM]
    end
    subgraph CON[Contenidor]
        P1["/usr/share/nginx/html"]
        P2["/var/lib/mysql"]
        P3["/tmp"]
    end
    D -->|bind mount| P1
    V -->|volum| P2
    R -->|tmpfs| P3
    classDef a fill:#2563EB,stroke:#1E40AF,color:#FFFFFF
    classDef b fill:#16A34A,stroke:#166534,color:#FFFFFF
    class D,V,R a
    class P1,P2,P3 b
Tipus Qui gestiona on es guarda Ús típic
Volum (named volume) Docker Dades de bases de dades i serveis en producció
Bind mount Vosaltres (una ruta de l'amfitrió) Desenvolupament: editar fitxers i veure'n els canvis a l'instant; fitxers de configuració
tmpfs Memòria RAM Dades temporals o sensibles que no han de tocar el disc

En tots els casos, el directori muntat substitueix el que hi hagués en aquella ruta dins del contenidor.

6.2 Bind mount: la vostra web servida per NGINX

Creeu un directori amb una pàgina:

mkdir -p ~/docker-pas6/web && cd ~/docker-pas6
echo "<h1>La meva web servida des d'un contenidor</h1>" > web/index.html

Munteu-lo a la ruta on NGINX busca els fitxers:

docker run -d --name web -p 8080:80 \
  -v "$(pwd)/web":/usr/share/nginx/html:ro \
  nginx:1.27-alpine
  • -v RUTA_AMFITRIÓ:RUTA_CONTENIDOR: si la part de l'esquerra és una ruta (comença per / o ./), és un bind mount.
  • :ro (read-only): el contenidor només pot llegir, no modificar, els fitxers.
  • $(pwd) s'expandeix al directori actual (a PowerShell, ${PWD}).

Obriu http://localhost:8080. Ara editeu web/index.html a l'amfitrió amb el vostre editor i recarregueu la pàgina: el canvi es veu immediatament, sense tocar el contenidor. Eliminar el contenidor no afecta els fitxers, perquè sempre han estat a la vostra màquina.

La mateixa ordre amb la sintaxi --mount, més llarga però més explícita:

docker run -d --name web2 -p 8081:80 \
  --mount type=bind,source="$(pwd)/web",target=/usr/share/nginx/html,readonly \
  nginx:1.27-alpine

6.3 Volum amb nom: una base de dades que sobreviu

Creem un volum i fem-lo servir per al directori de dades de MySQL:

docker volume create dades-mysql
docker run -d --name bd \
  -e MYSQL_ROOT_PASSWORD=Sapalomera2026 \
  -e MYSQL_DATABASE=botiga \
  -v dades-mysql:/var/lib/mysql \
  mysql:8.4

Quan la part de l'esquerra de -v és un nom (sense /), és un volum gestionat per Docker. Si no existeix, Docker el crea automàticament.

Esperem que estigui llesta (docker logs -f bd) i hi posem dades:

docker exec -it bd mysql -uroot -pSapalomera2026 botiga \
  -e "CREATE TABLE clients (nom VARCHAR(50)); INSERT INTO clients VALUES ('Anna'), ('Pau');"

Ara la prova de foc: eliminem el contenidor i en creem un de nou (fins i tot podríem canviar de versió de MySQL) amb el mateix volum:

docker rm -f bd
docker run -d --name bd-nova \
  -e MYSQL_ROOT_PASSWORD=Sapalomera2026 \
  -v dades-mysql:/var/lib/mysql \
  mysql:8.4
docker exec -it bd-nova mysql -uroot -pSapalomera2026 botiga -e "SELECT * FROM clients;"

Les dades hi són. El contenidor és d'un sol ús; les dades viuen al volum.

docker volume ls
docker volume inspect dades-mysql     # "Mountpoint": "/var/lib/docker/volumes/dades-mysql/_data"

6.4 Volums anònims: una sorpresa

Mireu la llista de volums:

docker volume ls
DRIVER    VOLUME NAME
local     3c9a1f0e8b7d6c5a4e3f2b1a0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d
local     8e4d2b0a1c3f5e7d9b8a6c4e2f0d1b3a5c7e9f8d6b4a2c0e1f3d5b7a9c8e6d4
local     dades-mysql

D'on surten aquests volums amb noms tan llargs? La imatge de MySQL declara al seu Dockerfile VOLUME /var/lib/mysql. Quan al pas 5 vam arrencar MySQL sense -v, Docker va crear igualment un volum anònim per a aquesta ruta. Aquells volums continuen ocupant espai encara que ja haguem eliminat els contenidors.

docker image inspect --format '{{json .Config.Volumes}}' mysql:8.4
docker volume prune       # elimina els volums anònims que no fa servir cap contenidor

Eliminar un contenidor i el seu volum anònim alhora

docker rm -v NOM elimina també els volums anònims del contenidor. I si l'arrenqueu amb --rm, els volums anònims s'eliminen automàticament quan s'atura. Els volums amb nom no s'eliminen mai automàticament.

6.5 Còpia de seguretat d'un volum

Un volum no és una carpeta que puguem copiar fàcilment (sobretot a Docker Desktop). La tècnica habitual és fer servir un contenidor auxiliar d'un sol ús que munta el volum i un directori de l'amfitrió, i comprimeix l'un dins de l'altre:

docker stop bd-nova
docker run --rm \
  -v dades-mysql:/dades:ro \
  -v "$(pwd)":/copia \
  alpine:3.20 tar czf /copia/dades-mysql.tar.gz -C /dades .
docker start bd-nova
ls -lh dades-mysql.tar.gz

És un bon exemple de la filosofia de Docker: no cal instal·lar res a l'amfitrió; fem servir un contenidor per a una tasca concreta i el llencem.

6.6 tmpfs

docker run --rm -it --tmpfs /tmp:size=64m alpine:3.20 sh
# dins: df -h /tmp

El contingut de /tmp viu a la RAM i desapareix quan s'atura el contenidor.

Miniactivitat - Dades que sobreviuen

  1. Repetiu la miniactivitat del pas 5 amb PostgreSQL, però ara amb un volum amb nom dades-pg muntat a /var/lib/postgresql/data. Comproveu que la taula sobreviu a docker rm -f.
  2. Amb un bind mount, feu que NGINX serveixi una web vostra de dues pàgines. Modifiqueu una pàgina amb el contenidor en marxa i comproveu el canvi.
  3. Proveu d'escriure un fitxer des de dins del contenidor NGINX amb el muntatge :ro. Quin error obteniu?
  4. Feu una còpia de seguretat del volum dades-pg a un fitxer .tar.gz. Elimineu el volum, creeu-ne un de nou buit, restaureu-hi la còpia i comproveu que les dades tornen a ser-hi.

6.7 Neteja

docker rm -f web web2 bd-nova
docker volume rm dades-mysql
docker volume prune

Eliminar un volum és irreversible

docker volume rm i docker volume prune esborren les dades definitivament. Abans d'eliminar un volum amb dades importants, feu-ne una còpia de seguretat.

Resum del pas

Comanda Què fa
-v /ruta/amfitrio:/ruta/contenidor[:ro] Bind mount
-v nom-volum:/ruta/contenidor Volum amb nom (el crea si no existeix)
--tmpfs /ruta Muntatge a memòria
docker volume create / ls / inspect / rm Gestió de volums
docker volume prune Elimina els volums no utilitzats
docker rm -v NOM Elimina el contenidor i els seus volums anònims

Següent pas: ja tenim una base de dades persistent. Però una aplicació real (WordPress, per exemple) ha de parlar amb aquesta base de dades. Al Pas 7 connectarem contenidors entre ells.