docker image vs container

In questo articolo andremo ad approfondire qual è la differenza tra una immagine Docker e un container e a realizzare un nostro Dockerfile per meglio comprendere tutte le varie fasi della virtualizzazione a container di Docker.
Abbiamo già parlato di questo sistema in un precedente tutorial in cui abbiamo imparato come si installa Docker.
Ma perché parlare di immagini e di container?

Docker image e container

Per meglio chiarire quali sono le differenze tra immagini e container, ragioniamo per analogia ad un linguaggio object oriented, dove la classe rappresenta l’immagine e l’istanza di quella classe, l’oggetto, è il container.
La stessa immagine può dar vita a più container.
La virtualizzazione a container si basa quindi fondamentalmente sulle immagini, ovvero i file reperibili sul Docker Hub e utilizzate per la creazione e l’inizializzazione di una applicazione in un nuovo contenitore Docker.
Ogni immagine è definita da un Dockerfile, un file di configurazione che contiene tutti i comandi che un utente deve eseguire per assemblare l’immagine.

I livelli del Dockerfile

Il dockerfile è un potente strumento per la definizione delle immagini grazie anche alla sua struttura a livelli, che contiene l’indicazione di comandi, librerie da utilizzare e dipendenze.
Può succedere che alcuni livelli siano presenti in più progetti e questa caratteristica garantisce il riuso dei livelli già scaricati e di conseguenza una garanzia sulle performance oltre che, non meno importante, un risparmio di tempo e spazio fisico.

Ammettiamo ad esempio di avere necessità di utilizzare le immagini di due rdbm quali postgresql e mysql (o mariadb).

Per prima cosa, ricerchiamo con il comando docker search le immagini ufficiali contenute sul dockerhub per questi due sistemi e prendiamo l’immagine ufficiale.

#docker search postgresql

docker search postgresql

#docker search mysql

docker search mysql

In questo secondo caso possiamo scegliere sia mysql che mariadb.
Scegliamo quest’ultimo e con il comando docker pull, scarichiamo le immagini in locale dal DockerHub.

# docker pull postgres

docker pull postgres

#docker pull mariadb

docker pull mariadb

Notiamo che lo status finale indica il corretto download delle immagini nelle versioni latest (identificato dal tag di default) e che in entrambi i casi sono composte di 14 layer distinti.
Osserviamo però che nel caso di mariadb, i primi 4 layer sono indicati con la dicitura “Already exists“.
Ciò significa che questi erano già stati scaricati in un precedente pull e non è quindi necessario riscaricarli.

Ognuno di questi layer è distinto da un identificativo mentre la penultima stringa Digest rappresenta univocamente l’immagine appena scaricata come hash dell’immagine stessa.

Per comprendere meglio questo aspetto dei layer di Docker, approfondiamo come si costruisce un DockerFile.

Costruiamo un DockerFile

È possibile, se necessario, crearsi i propri Dockerfile, aggiungendo livelli ad una base image, cioè un’immagine che rappresenta la base da cui partire, oppure da una immagine vuota nel caso di necessità di avere estrema libertà nella gestione.

In entrambi i casi la prima riga conterrà il comando FROM che indicherà la base image (scratch nel caso di immagine vuota).

Facciamo l’esempio di un semplice Dockerfile derivato da una immagine ufficiale ubuntu.

Creiamo il nostro Dockerfile con l’editor preferito e aggiungiamo le seguenti righe:

FROM ubuntu
RUN apt-get update
RUN apt-get install --yes apache2
COPY index.html /var/www/html/index.html

Al primo livello FROM ubuntu, si aggiungono 3 livelli che rappresentano i comandi che vogliamo aggiungere.

Eseguiamo il comando build per costruire l’immagine come descritta sul file nel seguente modo:

#docker build .

Ricordandoci il punto (.) che indica la directory corrente in cui docker cerca un dockerfile esattamente con questo nome.

Come si vede dalle immagini seguenti l’operazione di build è formata da 4 step, che corrispondono ai 4 livelli della nostra immagine.

docker build 1-4

docker build 2-4

docker build 3-4

docker build 4-4

In realtà verranno costruiti dei livelli intermedi, che hanno la funzione di cache nelle esecuzioni successive, come è possibile vedere dal comando:

#docker images -a

docker image -a

Le immagini identificate con <none> nel nome del repository sono quelle intermedie.

Per comprendere meglio, proviamo a rieseguire adesso il comando

#docker build .

docker image cache

É facile verificare che adesso il build dell’immagine è molto più veloce e riutilizza immagini che ha già in cache.
L’immagine 46115e101915 ad esempio è quella che viene riutilizzata nello step 3/4.

I container

Finora abbiamo parlato di come scaricare una image, o come costruirsi e implementarne una propria.
Ma è con il comando docker run che tutto davvero prende forma.

#docker run --rm --name ubuntu_apache -it ubuntu

docker run --rm

Questo comando infatti permette di istanziare un container sull’immagine appena creata.
Le opzioni scelte in questo caso sono quelle riferite all’interattività (-i), alla allocazione di una console TTY (-t) e alla possibilità di rimuovere direttamente il container una volta usciti (–rm).

Sulla documentazione ufficiale di Docker si trova la lista di tutte le opzioni per il comando run.

Mentre l’immagine è costituita da una serie di layer tutti in modalità in sola lettura, il container aggiunge un top layer (chiamato anche container layer) in modalità lettura-scrittura.

E ogni volta che un nuovo container viene istanziato sulla stessa immagine, un nuovo layer sempre diverso viene aggiunto a formare nuovi contenitori.

Se vogliamo avere una lista dei container, attivi e non, possiamo eseguire il comando

#docker ps -a

docker ps -a

La comodità di utilizzare i container è che è possibile facilmente e velocemente costruire un ambiente di sviluppo o di test quante volte vogliamo.

Uscendo dalla console con il comando exit, il container, per mezzo dell’opzione –rm, non esisterà più.

Proviamo adesso ad utilizzare il comando:

#docker run --name ubuntu_apache -it ubuntu

senza la precedente opzione per la rimozione.

docker run

A questo punto, se volessimo rimuoverlo manualmente dovremo usare il comando

#docker rm

seguito dall’id.

Ma non prima di averlo fermato.

Quindi la corretta sequenza dei comandi è la seguente:

docker stop rm

Se volessimo invece semplicemente riavviare il container precedentemente fermato, il comando da usare è

#docker start

e nel nostro caso di esempio sarebbe:

docker start

Conclusioni

In questo articolo abbiamo avuto modo di comprendere da alcuni esempi l’estrema semplicità con cui è possibile implementare ambienti perfettamente funzionanti, replicabili su qualsiasi macchina sfruttando la scalabilità del sistema a livelli delle immagini docker.

In seguito affronteremo anche come incrementare la potenzialità di questo strumento con meccanismi potenti come Docker-compose e Docker-swarm.

 

Credits images:
metal PNG Designed By Mark1987 from Pngtree.com
Docker Icon made by Freepik from www.flaticon.com

(Inglese)



Qual è la differenza tra una immagine Docker e un container?
Federica Mari
*****************************************

PLEASE NOTE: if you need technical support or have any sales or technical question, don't use comments. Instead open a TICKET here: https://support.iperius.net

*****************************************

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*****************************************

PLEASE NOTE: if you need technical support or have any sales or technical question, don't use comments. Instead open a TICKET here: https://support.iperius.net

*****************************************