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: coming soon

Immagine: Acqua foto creata da wirestock – it.freepik.com