Prima di parlare di volumi è necessaria una precisazione.
In Docker esistono due tipologie di dati: quelli persistenti e quelli non persistenti.
I dati persistenti sono quelli che devono essere preservati nel tempo ed eventualmente condivisi tra più container (es. Database, media, …).
I dati non persistenti invece sono quelli che possono essere anche persi perché utili solo nel contesto di riferimento, ad esempio i file temporanei.
Docker prevede nativamente delle modalità per la gestione di entrambe le tipologie di dato.
Ogni container è dotato del proprio spazio di memorizzazione dei dati non persistenti che viene poi deallocato nel momento in cui il container viene cancellato.
Per quanto riguarda la gestione dei dati persistenti Docker fornisce due modalità, i Volumi e i Bind Mounts.
Volumi
Osservando il Dockerfile relativo all’immagine redis:6-alpine (ho scelto questa perché leggermente più semplice e immediata), notiamo che ad un certo punto compare: VOLUME /data
.
Questo è il percorso di default dove redis persiste i dati.
Questa dicitura sta a significare che tutto quello che il container andrà a scrivere (o leggere) nel percorso /data
verrà gestito in un volume.
La cosa interessante di ciò è che redis funziona come se quel percorso fosse relativo ad una cartella locale, senza avere consapevolezza del fatto che quel percorso punta ad un volume.
Come vengono creati e collegati i volumi ai container?
Quando viene istanziato un container da un”immagine che prevede uno o più volumi, vengono creati i volumi necessari e collegati al container al percorso specificato nel Dockerfile.
Questo significa che, di default, vengono creati dei nuovi volumi ogni volta che viene istanziato un container.
I file che scriviamo in quel percorso rimarranno nel volume fino a quando non cancelleremo manualmente il volume o l’applicazione non li rimuova.
Per capire meglio come funziona questo meccanismo analizziamo il comportamento di un container istanziato sull’immagine di redis:6-alpine.
Qualora non lo avessimo già fatto, scarichiamo l’immagine con il comando:
docker image pull redis:6-alpine
Analizziamo ora tale immagine con il comando:
docker image inspect redis:6-alpine
L’output di tale comando sarà un oggetto nel quale sono illustrati i metadati dell’immagine. In questo caso è rilevante notare l’oggetto Volumes che è così composto:
"Volumes": {
"/data": {}
}
Ora eseguiamo il container:
docker container run -d --name redis-6-a redis:6-alpine
se eseguiamo:
docker container inspect redis-6-a
L’oggetto relativo al volume rimarrà lo stesso, tuttavia se poniamo attenzione troveremo un array contenente un oggetto per ogni mount. Ogni oggetto definisce a quale volume è in relazione il percorso locale al container specificato nel Dockerfile (nel nostro caso, abbiamo solo un volume corrispondente al percorso /data
). Vengono specificati i percorsi dove vengono persistiti i file del volume, la tipologia di mount e se l’accesso è ReadWrite o meno.
Nel mio caso:
"Mounts": [
{
"Type": "volume",
"Name": "d622368372a22112e3658d322b5933859d39646f4ab38d6d015dbb6a7c4690e1",
"Source": "/var/lib/docker/volumes/d622368372a22112e3658d322b5933859d39646f4ab38d6d015dbb6a7c4690e1/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
Abbiamo parlato di cancellazione dei volumi, ma al momento non sappiamo come si fa.
Per la gestione dei volumi Docker ha messo a disposizione una CLI.
CLI dei Volumi
Per esplorare i comandi messi a disposizione utilizziamo:
docker volume help
ottenendo:
Usage: docker volume COMMAND
Manage volumes
Commands:
create Create a volume
inspect Display detailed information on one or more volumes
ls List volumes
prune Remove all unused local volumes
rm Remove one or more volumes
Abbiamo già analizzato questi comandi anche se in altri contesti, quindi non andrei in dettaglio.
Eseguendo docker volume ls
possiamo vedere il volume appena creato:
DRIVER VOLUME NAME
local d622368372a22112e3658.....
In modo del tutto simile alle immagini vediamo che si possono fare operazioni di creazione, rimozione, ispezione e pulizia dei volumi.
Named Volume
Gestendo i container e i volumi in questo modo abbiamo un problema, ogni nuovo container genera un nuovo volume.
Nel caso di redis, abbiamo eseguito il container con il comando:
docker container run -d --name redis-6-a redis:6-alpine
Il comando run mette a disposizione un flag -v dedicato ai volumi.
Modificando l’istruzione in questo modo:
docker container run -d --name redis-6-a -v redis-data:/data redis:6-alpine
Andiamo a dire che il volume di redis, che è montato sul percorso /data ora ha il nome: “redis-data”.
Questo tipo di volume è interessante perché se è già presente un volume con lo stesso nome, verrà utilizzato quello al posto di uno nuovo.
Eseguendo nuovamente docker volume ls
possiamo vedere il volume appena creato:
DRIVER VOLUME NAME
local d622368372a22112e3658.....
local redis-data
Bind Mounts
Un bind mount è una sorta di symlink, viene fatto un collegamento logico (mount) di un file o di una cartella dell’host in un file o una cartella del container.
Se il file o la cartella dell’host non è definita nel container, allora verrà creata contestualmente alla creazione del mount che può avvenire solo runtime. Non è necessario quindi dichiarare il bind mount nel Dockerfile come era invece previsto per i volumi.
Non devono essere dichiarati nel Dockerfile perché possono essere dichiarati solo runtime.
Per farlo mediante il comando docker container run dobbiamo utilizzare uno dei seguenti flag:
docker run -v /path/assoluto/host:path/container/da/workdir
docker run --volume /path/assoluto/host:path/container/da/workdir
docker run --mount /path/assoluto/host:path/container/da/workdir
Un consiglio che mi sento di dare è di utilizzare quest’ultima risorsa quando è veramente necessario o in fase di sviluppo per vedere direttamente le modifiche a mano a mano che vengono implementate. Quando possibile è consigliabile utilizzare i volumi.
—
Prosegui su: Container e docker-compose