Docker Images, una introduzione
Se siete esperti di VMware e VM in generale, potete pensare a una Docker Image come ad un template. Un VM template è come una virtual machine ferma, una Docker Image è come un container non in esecuzione. Se siete programmatori ed avete famigliarità col paradigma ad oggetti, pensate alle immagini Docker come ad classi, e i relativi container come ad istanze di classi.
In un certo senso le immagini possono essere viste come costrutti di "build-time", i container di "run-time". Una immagine non potrà mai essere in esecuzione, solo i container possono esserlo partendo da una immagine. E' possibile anche fare il percorso contrario, vale a dire creare una immagine nuova partendo da un container (una volta che è stato fermato), in un certo senso cristallizzandolo creando una base per crearne un altro simile.
Si può iniziare a lavorare con le immagini Docker scaricandole da un "registry" come Docker Hub. L'operazione di pull scaricherà dal repository l'immagine in locale, e permetterà la creazione di container in esecuzione basati sull'immagine stessa.
Le immagini Docker sono concepite come layer sovrapposti che, messi insieme, la compongono. All'interno dell'immagine è presente una versione ridotta del sistema operativo, tutti i file e le dipendenze (dependencies / librerie) necessarie all'esecuzione dell'applicazione contenuta nell'immagine. E' per questo che le immagini in genere non sono molto grandi in termini di Mb.
Cancellare una immagine legata a container in esecuzione
Una volta che uno o più container sono stati fatti partire sulla base di una immagine, non è possibile cancellare quest'ultima. Per poterlo fare tutti i container basati su di essa devono essere fermati. Tentare di cancellare un'immagine senza fermare ed eliminare prima tutti i container a ad essa collegati genera un errore.
Cosa contiene una immagine
Come già detto, le immagini tendono ad essere piccole e a contenere solo le parti nel sistema operativo strettamente necessarie, oltre a librerie, dipendenze e programmi che la rendono specifica, e che permetteranno ai container che ne deriveranno di eseguire app o servizi. Per esempio, cosa fondamentale, le immagini con contengono mai il kernel del sistema operativo, tutti i container in esecuzione su un Docker Host condividono lo stesso kernel. Di solito le immagini contengono oggetti e file strettamente legati al file-system del sistema operativo. L'immagine Docker ufficiale di Ubuntu è grande circa 100Mb.
Le immagini Windows-based tendono ad essere più corpose di quelle Linux-based, a causa del modo nel quale il sistema operativo lavora.
Layers
Una immagine Docker è essenzialmente un insieme di layer, NON strettamente connessi l'uno all'altro, e di sola lettura. Quando si effettua il pulling di una immagine, si vede l'elenco dei layers di cui è composta e se ognuno di loro viene scaricato oppure era già presente nel sistema (layer sharing). Qui si vede bene. Con il comando inspect è possibile vedere l'elenco dei layer che compongono una immagine:
docker image inspect {image name}
Tutte le immagini partono da un layer di base e man mano vengono aggiunti gli altri, a seconda delle istruzioni Dockerfile usate per construire (build) l'immagine. Non tutte le istruzioni Dockerfile aggiungono layer nuovi, alcune si limitano ad aggiungere metadata all'immagine.
docker image history {image name}
Il comando history non elanca strettamente i layer che compongono l'immagine, piuttosto la storia della build dell'immagine stessa, includendo i layer ma non solo.
Un esempio molto basico di composizione di una imaggine potrebbe essere l'uso del layer base di Ubuntu, l'aggiunta del package PHP aggiungerebbe un layer, il package MariaDb andrebbe a porre in cima un terzo layer e così via. Una immagine risulta essere il frutto della combinazione dei layer che la compongono. L'ordine di composizione dei layer è fondamentale, e un file in un layer posto in cima può oscurare (non sovrascrivere poichè i layer sono read-only) lo stesso file ma posizionato in un layer di livello più basso. L'immagine ottenuta nasce dalla funsione dei file che compongono i layer, tenedo conto di eventuali "coperture". Il tutto ricorda molto la composizione di una immagine con GIMP o Photoshop, usando layer sovrapposti dove ognuno di loro può oscurare elementi di immagine presenti nei layer che stanno sotto.
In sintesi, una immagine è un oggetto di configurazione composto da alcuni metadata e un elenco di layer. I layer sono gli oggetti che contengono i dati veri e propri, ognuno è indipendente dall'altro e di fatto non sono "consapevoli" di far parte di una immagine insieme ad altri layer. Come le immagini sono indentificate da un digest, dipendente dal loro contenuto, così pure i layer hanno il loro digest. Cambiare il contenuto di un layer comporta il cambio del digest, è per quello che è facile identificarli univocamente. Dal punto di vista logico si tratta di oggetti immutabili.