====== Docker Volumes ======
Containerele Docker sunt prin natura lor **efemere** - când un container este șters, toate datele scrise în sistemul său de fișiere dispar odată cu el. Docker Volumes rezolvă această problemă oferind un mecanism de stocare persistentă, complet decuplat de ciclul de viață al containerului.
Volumele sunt mecanismul recomandat de Docker pentru persistența datelor - preferate altor metode datorită portabilității, performanței și integrării native cu Docker Engine.
===== Tipuri de montare (mount types) =====
Docker oferă trei metode distincte prin care un container poate accesa date din afara sistemului său de fișiere intern:
==== Named Volumes ====
Stocate în directorul gestionat de Docker (implicit ''/var/lib/docker/volumes/''), volumele denumite sunt complet gestionate de Docker Engine. Sunt opțiunea recomandată pentru persistența datelor de producție.
/var/lib/docker/volumes/
└── nume_volum/
└── _data/ ← datele efective
==== Bind Mounts ====
Mapează un director sau fișier de pe **host** direct în container. Calea trebuie să existe pe sistemul gazdă și trebuie specificată cu calea absolută. Utile în development, când vrei ca modificările locale să fie reflectate imediat în container.
==== tmpfs Mounts ====
Stocate exclusiv în **memoria RAM** a gazdei, fără a scrie nimic pe disk. Datele dispar la oprirea containerului. Utile pentru informații sensibile (tokeni, parole temporare) sau date de caching cu latență minimă.
^ Caracteristică ^ Named Volume ^ Bind Mount ^ tmpfs ^
^ Stocare | Docker managed | Filesystem gazdă | Memorie RAM |
^ Persistență | ✓ Da | ✓ Da (pe host) | ✗ Nu |
^ Portabilitate | ✓ Ridicată | ✗ Depinde de cale | - |
^ Performanță Linux | ✓ Optimă | ✓ Bună | ✓ Maximă |
^ Partajare containere | ✓ Facilă | ✓ Posibilă | ✗ Nu |
^ Recomandat pentru | Date producție, DB | Development, config | Secrete, cache |
===== Named Volumes =====
==== Crearea unui volum ====
# Creare simplă
docker volume create date_aplicatie
# Creare cu opțiuni (driver explicit, labels)
docker volume create \
--driver local \
--label env=productie \
--label app=myapp \
date_aplicatie
==== Listarea și inspecția volumelor ====
# Listare toate volumele
docker volume ls
# Filtrare după driver
docker volume ls --filter driver=local
# Filtrare după label
docker volume ls --filter label=env=productie
# Inspecție detaliată (JSON)
docker volume inspect date_aplicatie
Exemplu output ''docker volume inspect'':
[
{
"CreatedAt": "2024-11-15T10:23:45Z",
"Driver": "local",
"Labels": {
"env": "productie",
"app": "myapp"
},
"Mountpoint": "/var/lib/docker/volumes/date_aplicatie/_data",
"Name": "date_aplicatie",
"Options": {},
"Scope": "local"
}
]
==== Ștergerea volumelor ====
# Ștergere volum specific (trebuie să nu fie folosit)
docker volume rm date_aplicatie
# Ștergere toate volumele nefolosite (dangling)
docker volume prune
# Ștergere cu confirmare automată (atenție în producție!)
docker volume prune --force
> ⚠️ **Atenție:** ''docker volume prune'' șterge definitiv toate volumele care nu sunt atașate la niciun container (inclusiv oprite). Nu există undo.
===== Folosirea volumelor în containere =====
==== Sintaxa --mount (recomandată) ====
Opțiunea ''%%--mount%%'' este explicită și mai ușor de citit - recomandată în scripturi și documentație:
# Named volume
docker run -d \
--mount type=volume,source=date_postgres,target=/var/lib/postgresql/data \
postgres:16
# Bind mount
docker run -d \
--mount type=bind,source=/home/user/config,target=/etc/myapp,readonly \
myapp:latest
# tmpfs mount
docker run -d \
--mount type=tmpfs,target=/tmp/cache,tmpfs-size=256m \
myapp:latest
==== Sintaxa -v (scurtă) ====
Sintaxa prescurtată cu ''-v'' este mai concisă, dar mai ambiguă (Docker determină tipul automat):
# Named volume: -v nume_volum:cale_container
docker run -d -v date_postgres:/var/lib/postgresql/data postgres:16
# Bind mount: -v /cale/absoluta/host:cale_container
docker run -d -v /home/user/app:/var/www/html nginx:alpine
# Read-only: adaugă :ro la final
docker run -d -v /home/user/config:/etc/myapp:ro myapp:latest
# tmpfs: --tmpfs cale_container:optiuni
docker run -d --tmpfs /tmp:size=256m,mode=1777 myapp:latest
==== Volume-uri cu mai multe containere ====
Același volum poate fi montat simultan în mai multe containere - util pentru partajarea datelor între servicii:
# Containerul principal scrie date
docker run -d \
--name producator \
--mount type=volume,source=date_partajate,target=/date/out \
app-producator:latest
# Containerul secundar citește aceleași date
docker run -d \
--name consumator \
--mount type=volume,source=date_partajate,target=/date/in,readonly \
app-consumator:latest
> ⚠️ Accesul concurent la același volum din mai multe containere poate cauza corupție de date dacă aplicațiile nu gestionează locking-ul. Asigură-te că aplicațiile tale sunt proiectate pentru acces concurent sau că doar un singur container scrie.
===== Bind Mounts în detaliu =====
==== Cazuri de utilizare ====
Bind mount-urile sunt ideale pentru:
* **Development activ** - modificările de cod din editor sunt vizibile imediat în container fără rebuild
* **Fișiere de configurare** - montezi ''/etc/myapp/config.yaml'' de pe host în container
* **Certificate TLS/SSL** - montezi certificatele generate (ex. via Certbot) în containerul web
* **Socket-ul Docker** - expui ''/var/run/docker.sock'' în containere de tip CI/CD sau management
# Exemplu development: codul local montat în container Node.js
docker run -d \
--name dev-server \
--mount type=bind,source=$(pwd)/src,target=/app/src \
-p 3000:3000 \
node:20-alpine npm run dev
# Montare socket Docker (ex. pentru Portainer)
docker run -d \
--name portainer \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
-p 9000:9000 \
portainer/portainer-ce:latest
==== Opțiuni avansate bind mount ====
# Read-only - containerul nu poate modifica fișierele
docker run --mount type=bind,source=/etc/ssl/certs,target=/certs,readonly nginx
# Propagation - cum sunt propagate sub-mount-urile (avansat, Linux)
# Valori: shared, slave, private, rshared, rslave, rprivate
docker run --mount type=bind,source=/data,target=/data,bind-propagation=shared myapp
===== Volume Drivers (Plugins) =====
Docker suportă drivere de volum terțe care permit stocarea datelor pe sisteme externe, nu doar local.
==== Drivere populare ====
^ Driver ^ Stocare backend ^ Caz de utilizare ^
^ ''local'' | Disk local | Default, single-node |
^ ''nfs'' | NFS share | Partajare între noduri |
^ ''vieux/sshfs'' | SFTP/SSH | Stocare remotă via SSH |
^ ''rexray/s3fs'' | Amazon S3 | Stocare obiect cloud |
^ ''convoy'' | EBS, NFS, VFS | Backup & restore integrate |
==== Exemplu: volum NFS ====
# Creare volum pe un share NFS
docker volume create \
--driver local \
--opt type=nfs \
--opt o=addr=192.168.1.100,rw,nfsvers=4 \
--opt device=:/export/date \
volum_nfs
# Folosire în container
docker run -d \
--mount source=volum_nfs,target=/date \
myapp:latest
==== Exemplu: plugin SSHFS ====
# Instalare plugin
docker plugin install vieux/sshfs
# Creare volum via SSH
docker volume create \
--driver vieux/sshfs \
--opt sshcmd=user@server:/cale/date \
--opt password=secret \
volum_ssh
===== Volumes în Docker Compose =====
Docker Compose oferă o sintaxă elegantă pentru declararea și gestionarea volumelor la nivelul stack-ului.
==== Declarare de bază ====
services:
db:
image: postgres:16
volumes:
- date_db:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: secret
app:
image: myapp:latest
volumes:
# Named volume (read-write)
- date_uploads:/var/www/uploads
# Bind mount (development)
- ./config/app.yaml:/etc/myapp/config.yaml:ro
# tmpfs
- type: tmpfs
target: /tmp/cache
tmpfs:
size: 134217728 # 128 MB în bytes
volumes:
date_db:
date_uploads:
==== Volume cu opțiuni avansate ====
volumes:
# Volum extern (creat în afara Compose, ex. manual sau alt stack)
date_externa:
external: true
# Volum cu driver NFS
date_nfs:
driver: local
driver_opts:
type: nfs
o: addr=192.168.1.100,rw
device: ":/export/date"
# Volum cu labels
date_app:
labels:
- "com.example.project=myapp"
- "com.example.env=productie"
==== Partajarea volumelor între servicii ====
services:
generator:
image: app-generator:latest
volumes:
- date_partajate:/output
processor:
image: app-processor:latest
volumes:
- date_partajate:/input:ro
depends_on:
- generator
volumes:
date_partajate:
===== Backup și Restore =====
==== Backup unui volum ====
Strategia clasică: pornești un container temporar cu volumul montat, arhivezi conținutul, copiezi arhiva pe host.
# Backup volum → arhivă tar.gz pe host
docker run --rm \
--mount source=date_aplicatie,target=/date,readonly \
--mount type=bind,source=$(pwd)/backup,target=/backup \
alpine \
tar czf /backup/backup_$(date +%Y%m%d_%H%M%S).tar.gz -C /date .
# Verificare arhivă
ls -lh backup/
==== Restore unui volum ====
# Restore dintr-o arhivă tar.gz într-un volum (existent sau nou)
docker run --rm \
--mount source=date_aplicatie,target=/date \
--mount type=bind,source=$(pwd)/backup,target=/backup,readonly \
alpine \
tar xzf /backup/backup_20241115_102345.tar.gz -C /date
# Verificare restaurare
docker run --rm \
--mount source=date_aplicatie,target=/date,readonly \
alpine ls -la /date
==== Copiere volum → volum nou ====
# Migrare date dintr-un volum vechi în unul nou
docker run --rm \
--mount source=volum_vechi,target=/sursa,readonly \
--mount source=volum_nou,target=/destinatie \
alpine \
sh -c "cp -av /sursa/. /destinatie/"
==== Backup baze de date (PostgreSQL) ====
La baze de date, e recomandat să folosești utilitarele native în loc de un simplu tar - garantezi astfel consistența datelor, indiferent dacă baza e activă în momentul backup-ului:
# Dump PostgreSQL direct în fișier pe host
docker exec postgres_container \
pg_dumpall -U postgres | gzip > backup_postgres_$(date +%Y%m%d).sql.gz
# Restore PostgreSQL
gunzip -c backup_postgres_20241115.sql.gz | \
docker exec -i postgres_container psql -U postgres
===== Inspecție și Debugging =====
==== Explorarea conținutului unui volum ====
# Metoda 1: container temporar Alpine
docker run --rm \
--mount source=date_aplicatie,target=/date,readonly \
alpine ls -laR /date
# Metoda 2: shell interactiv
docker run -it --rm \
--mount source=date_aplicatie,target=/date \
alpine sh
# Metoda 3: accesare directă pe host (necesită root)
sudo ls -la /var/lib/docker/volumes/date_aplicatie/_data
==== Verificarea volumelor unui container ====
# Listare mount-uri ale unui container
docker inspect --format='{{json .Mounts}}' nume_container | jq
# Sau mai detaliat
docker inspect nume_container | jq '.[0].Mounts'
Exemplu output:
[
{
"Type": "volume",
"Name": "date_aplicatie",
"Source": "/var/lib/docker/volumes/date_aplicatie/_data",
"Destination": "/date",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
==== Identificarea volumelor dangling ====
Volumele dangling sunt cele care nu mai sunt atașate la niciun container - ocupă spațiu inutil:
# Listare volume nefolosite
docker volume ls --filter dangling=true
# Afișare spațiu folosit de volume
docker system df -v
# Curățare volume dangling
docker volume prune
===== Permisiuni și Securitate =====
==== Problema permisiunilor cu bind mounts ====
Când folosești bind mounts, UID-ul/GID-ul procesului din container trebuie să corespundă cu proprietarul fișierelor de pe host:
# Verificare UID proces în container
docker run --rm nginx:alpine id
# uid=101(nginx) gid=101(nginx)
# Ajustare permisiuni pe host
sudo chown -R 101:101 /home/user/nginx-data
# Sau specificare user la rulare
docker run -d --user 1000:1000 myapp:latest
==== Volume read-only ====
Montarea volumelor ca read-only limitează suprafața de atac - containerul nu poate modifica date critice:
# CLI
docker run --mount source=config_date,target=/config,readonly myapp
# Docker Compose
services:
app:
volumes:
- config_date:/config:ro
==== Evitarea montării /var/run/docker.sock ====
Montarea socket-ului Docker oferă containerului control deplin asupra host-ului - echivalent cu privilegii root. Folosește-l doar când este absolut necesar (ex. Portainer, CI runners) și niciodată în aplicații expuse publicului.
===== Bune practici =====
* **Folosește named volumes pentru date de producție** - nu bind mounts; sunt portabile, ușor de backup și nu depind de structura de directoare a host-ului
* **Separă volumele per funcție** - un volum pentru date aplicație, altul pentru logs, altul pentru upload-uri; nu pune totul într-un singur volum
* **Adaugă labels volumelor** - facilitează identificarea și curățarea ulterioară (''docker volume ls --filter label=env=productie'')
* **Include backup în rutina de deployment** - automatizează backup-urile înainte de update-uri majore
* **Preferă ''--mount'' față de ''-v''** - sintaxa explicită reduce ambiguitatea și erorile
* **Nu stoca secrete în volume** - folosește Docker Secrets sau variabile de mediu gestionate (ex. Vault, sops); volumele nu sunt criptate implicit
* **Monitorizează spațiul pe disk** - volumele pot crește nelimitat; adaugă alerte pe ''/var/lib/docker/volumes''
* **Fă ''docker volume prune'' periodic** - în medii de development se acumulează rapid volume orfane
===== Referințe =====
* [[https://docs.docker.com/engine/storage/volumes/|Docker Volumes - documentație oficială]]
* [[https://docs.docker.com/engine/storage/bind-mounts/|Bind Mounts - documentație oficială]]
* [[https://docs.docker.com/engine/storage/tmpfs/|tmpfs Mounts - documentație oficială]]
* [[https://docs.docker.com/engine/extend/plugins_volume/|Volume Plugins]]
* [[https://docs.docker.com/reference/cli/docker/volume/|docker volume - referință CLI]]
{{tag>docker volume stocare persistenta bind-mount tmpfs backup devops}}