Introduzione
Docker è un'applicazione che semplifica il processo di gestione dei processi delle applicazioni attraverso i container. I container consentono di eseguire le applicazioni in dei processi con risorse isolate. I container sono simili a delle macchine virtuali, tuttavia hanno il vantaggio di essere più facili da trasportare, utilizzano meno risorse e sono più dipendenti dal sistema operativo del server in cui vengono eseguiti.
In questo tutorial vedremo come installare e utilizzare la Community Edition (CE) di Docker su Ubuntu 20.04. Installeremo Docker, lavoreremo con i container e le con le immagini e infine aggiungeremo un'immagine a una Repository di Docker.
Prerequisiti
Per seguire con facilità questo tutorial avrai bisogno di:
- Un server con Ubuntu configurato seguendo la guida iniziale, quindi con un account sudo e con un firewall di base.
- Un account su Docker Hub, necessario per creare le proprie immagini e aggiungerle a Docker Hub, come faremo più avanti in questa guida.
Step 1 - Installare Docker
Il pacchetto per installare Docker è disponibile nella repository ufficiale di Ubuntu, tuttavia non è sempre aggiornato all'ultima versione. Per essere sicuri di installare sempre l'ultima versione, andremo ad installare Docker direttamente dalla repository ufficiale.
Per farlo andiamo ad aggiungere una nuova sorgente per il pacchetto, aggiungeremo poi la Chiave GPG da Docker per essere sicuri che i download siano validi e infine installeremo il pacchetto.
Come prima cosa aggiorniamo l'elenco dei nostri pacchetti:
sudo apt update
Poi andiamo a installare alcuni pacchetti necessari per consentire ad apt di lavorare con pacchetti tramite HTTPS:
sudo apt install apt-transport-https ca-certificates curl software-properties-common
Aggiungiamo la Chiave GPG per la repository ufficiale di Docker al nostro sistema:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
E aggiungiamo anche la repository di Docker all'elenco delle sorgenti di apt:
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
Questo comando servirà anche ad aggiornare il nostro database di pacchetti, aggiungendo i pacchetti di Docker dalla repository che abbiamo appena specificato.
Prima di procedere con l'installazione, dobbiamo essere sicuri che l'installazione venga eseguita dalla repository di Docker e non da quella di Ubuntu, eseguiamo quindi il comando:
apt-cache policy docker-ce
L'output dovrebbe essere simile al seguente, anche se ovviamente la versione di Docker potrebbe cambiare:
docker-ce:
Installed: (none)
Candidate: 5:19.03.9~3-0~ubuntu-focal
Version table:
5:19.03.9~3-0~ubuntu-focal 500
500 https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
Possiamo vedere come docker-ce
non sia installato, ma nel caso volessimo installarlo verrebbe scaricato dalla repository di Docker per Ubuntu (focal
).
Ora siamo finalmente pronti ad installare Docker:
sudo apt install docker-ce
Ora Docker dovrebbe essere stato installato, il demone avviato e il processo impostato in modo da essere avviato al boot del sistema.
Controlliamo che sia in esecuzione con:
sudo systemctl status docker
Nell'ouput dovremmo poter vedere che il servizio è attivo e in esecuzione:
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2020-05-19 17:00:41 UTC; 17s ago
TriggeredBy: ● docker.socket
Docs: https://docs.docker.com
Main PID: 24321 (dockerd)
Tasks: 8
Memory: 46.4M
CGroup: /system.slice/docker.service
└─24321 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
Instando Docker non avremo solamente accesso al servizio di Docker (demone) ma anche all'utility di Docker da riga di comando (docker
) e al client Docker. Più avanti nel tutorial vedremo come utilizzare docker
da riga di comando.
Step 2 - Eseguire i Comandi Docker senza Sudo (Opzionale)
Di default i comandi docker
possono essere utilizzare solamente dall'utente root o dagli utenti appartenendo al gruppo docker, creato durante il processo di installazione di Docker. Infatti, se proverai ad eseguire un domando docker senza aggiungere sudo prima di esso, o senza essere nel gruppo docker, vedrai questo messaggio:
docker: Cannot connect to the Docker daemon. Is the docker daemon running on this host?.
See 'docker run --help'.
Se vuoi evitare di dover aggiungere sudo ogni volta che vuoi eseguire un comando docker
, puoi aggiungere il tuo utente al gruppo docker appena citato:
sudo usermod -aG docker ${USER}
Per rendere effettivi i cambiamenti, è necessario uscire dal server e rientrare, oppure eseguendo il seguente comando:
su - ${USER}
Ti verrà chiesto di eseguire la password del tuo utente e poi potrai continuare. Una volta effettuato l'accesso puoi confermare che il tuo utente sia nel gruppo docker digitando il seguente comando:
groups
gbfactory sudo docker
Se devi aggiungere al gruppo docker un utente a cui non hai effettuato l'accesso, puoi eseguire il comando dichiarando esplicitamente il nome dell'utente:
sudo usermod -aG docker nome_utente
Durante il resto dell'articolo daremo per scontato che i comandi docker
vengano eseguiti da un utente all'interno del gruppo docker. Se non hai eseguito questo step dovrai aggiungere sudo
prima del comando.
Step 3 - Utilizzare il Comando Docker
L'utilizzo del comando docker
consiste nel passare una serie di opzioni e comandi seguiti dai relativi parametri. La sintassi di base è la seguente:
docker [opzione] [comando] [parametri]
Per vedere un elenco di tutti i comandi disponibili puoi digitare:
docker
Alla versione di Docker 20.10.14, la lista completa di tutti i sottocomandi disponibili include:
attach Attach local standard input, output, and error streams to a running container
build Build an image from a Dockerfile
commit Create a new image from a container's changes
cp Copy files/folders between a container and the local filesystem
create Create a new container
diff Inspect changes to files or directories on a container's filesystem
events Get real time events from the server
exec Run a command in a running container
export Export a container's filesystem as a tar archive
history Show the history of an image
images List images
import Import the contents from a tarball to create a filesystem image
info Display system-wide information
inspect Return low-level information on Docker objects
kill Kill one or more running containers
load Load an image from a tar archive or STDIN
login Log in to a Docker registry
logout Log out from a Docker registry
logs Fetch the logs of a container
pause Pause all processes within one or more containers
port List port mappings or a specific mapping for the container
ps List containers
pull Pull an image or a repository from a registry
push Push an image or a repository to a registry
rename Rename a container
restart Restart one or more containers
rm Remove one or more containers
rmi Remove one or more images
run Run a command in a new container
save Save one or more images to a tar archive (streamed to STDOUT by default)
search Search the Docker Hub for images
start Start one or more stopped containers
stats Display a live stream of container(s) resource usage statistics
stop Stop one or more running containers
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
top Display the running processes of a container
unpause Unpause all processes within one or more containers
update Update configuration of one or more containers
version Show the Docker version information
wait Block until one or more containers stop, then print their exit codes
Per vedere le opzioni disponibili per un determinato comando puoi usare:
docker sottocomando_docker --help
Puoi anche visualizzare le informazioni generali relative a Docker puoi utilizzare:
docker info
Step 4 - Lavorare con le Immagini Docker
I contenitori Docker (containers) sono creati da immagini Docker (images). Di default, Docker recupera le immagini dal Docker Hub, un registro gestito direttamente da Docker, ovvero l'azienda dietro il progetto Docker. Tutti possono hostare le proprie immagini Docker sul Docker Hub, di conseguenza la maggior parte dei programmi e delle distribuzioni Linux hanno una loro immagine sull'Hub.
Per controllare se puoi accedere e scaricare le immagini dal Docker Hub, puoi scrivere:
docker run hello-world
All'interno dell'output vedrai se Docker funziona correttamente:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:bfea6278a0a267fad2634554f4f0c6f31981eea41c553fdf5a83e95a41d40c38
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
Docker inizialmente non era in grado di trovare l'immagine hello-world
in locale, quindi l'ha scaricata dal Docker Hub, che è la repository predefinita. Una volta che l'immagine è stata scaricata, Docker creerà un contenitore dall'immagine ed eseguirà l'applicazione all'interno del contenitore, mostrando il messaggio.
Puoi anche cercare le immagini disponibili nel Docker Hub utilizzando il comando docker
e il sottocomando search
. Per esempio, possiamo cercare l'immagine di Ubuntu con:
docker search ubuntu
Questo script recupererà dal Docker Hub una lista di tutte le immagini il cui nome contiene quello che è stato specificato nel comando. In questo caso l'output sarà simile al seguente:
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Ubuntu is a Debian-based Linux operating sys… 14048 [OK]
websphere-liberty WebSphere Liberty multi-architecture images … 283 [OK]
ubuntu-upstart DEPRECATED, as is Upstart (find other proces… 112 [OK]
...
Nella colonna OFFICIAL, la dicitura OK indica che l'immagine è supportata dall'azienda dietro il progetto. Una volta aver identificato l'immagine di tuo interesse, puoi scaricarla in locale utilizzando il sottocomando pull.
Per scaricare in locale l'immagine ufficiale di ubuntu puoi eseguire il seguente comando:
docker pull ubuntu
Apparirà il seguente output:
Using default tag: latest
latest: Pulling from library/ubuntu
e0b25ef51634: Pull complete
Digest: sha256:9101220a875cee98b016668342c489ff0674f247f6ca20dfc91b91c0f28581ae
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest
Dopo che un'immagine sarà stata scaricata, puoi eseguire un contenitore utilizzando l'immagine scaricata con il sottocomando run
. Come abbiamo visto con l'esempio hello-world
, se un'immagine non è stata scaricata quando docker
viene eseguito con il comando run
, il client Docker scaricherà l'immagine automaticamente, poi eseguirà un container con quell'immagine.
Per vedere l'elenco delle immagini che sono state scaricate in locale, scrivi:
docker images
L'output sarà simile al seguente:
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 1d622ef86b13 3 weeks ago 73.9MB
hello-world latest bf756fb1ae65 4 months ago 13.3kB
Come vedremo più avanti nel tutorial, le immagini che utilizzi per eseguire i container possono essere modificare e utilizzate per generare nuovi immagini, che potranno anche poi essere caricate sul Docker Hub o in altri registri Docker.
Step 5 - Eseguire un Docker Container
Il contenitore hello-world
che abbiamo eseguito nello step precedente è un esempio di un contenitore che viene eseguito e si ferma dopo aver stampato un messaggio testuale. I contenitori possono essere ovviamente molto più utili di così, e possono anche essere interattivi. Sono simili alle macchine virtuali, però molto più efficienti dal punto di vista delle risorse utilizzate.
Come esempio, andiamo ad eseguire un container utilizzando l'ultima immagine di Ubuntu. La combinazione dei flag -i
e -t
consentono di avere una shell interattiva e potrai accedere al container:
docker run -it ubuntu
Il prompt dei comandi dovrebbe cambiare per riflettere il fatto che stai ora lavorando all'interno di un container e dovrebbe diventare simile al seguente:
root@d9b100f2f636:/#
Come puoi vedere è stato aggiunto l'ID del container al prompt, che in questo caso è d9b100f2f636
. Questo ID è fondamentale per identificare il container quando vogliamo cancellarlo.
Ora possiamo eseguire qualsiasi comando all'interno del container. Per esempio, possiamo aggiornare il database dei pacchetti interno al container. Non dobbiamo aggiungere il prefisso sudo dato che stiamo già lavorando con l'utente root:
root@d9b100f2f636:/# apt update
Poi possiamo anche installare delle applicazioni, per esempio Node.js:
root@d9b100f2f636:/# apt install nodejs
Questo comando installerà la versione di Node.js contenuta nella repository ufficiale di Ubuntu. Quando l'installazione sarà completa, possiamo verificare che Node.js sia stato installato:
root@d9b100f2f636:/# node -v
Vedremo in console il numero della versione installata:
v12.22.9
Qualsiasi cambiamento che faremo all'interno di questo container avrà effetti solamente sul container in questione.
Per uscire dal container possiamo scrivere il seguente comando nel prompt:
exit
Step 6 - Gestire i Container Docker
Con un utilizzo costante di Docker potremmo arrivare a lavorare con molti container in esecuzione su una stessa macchina. Per visualizzare tutti i contenitori attivi possiamo utilizzare:
docker ps
L'output sarà simile al seguente:
CONTAINER ID IMAGE COMMAND CREATED
In questa guida abbiamo avviato due container, il primo dall'immagine hello-world, il secondo dall'immagine ubuntu. Entrambe i container attualmente non sono più in esecuzione, ma esistono comunque sul sistema.
Per vedere quindi tutti i container, sia attivi che inattivi, possiamo eseguire lo stesso comando di prima aggiungendo il flag -a
:
docker ps -a
Vedremo quindi un output come il seguente:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c08a7a0d0e4 ubuntu "bash" About a minute ago Exited (0) 7 seconds ago dazzling_taussig
587000e49d53 hello-world "/hello" 5 minutes ago Exited (0) 5 minutes ago adoring_kowalevski
Possiamo vedere l'ultimo container che abbiamo creato passando il flag -l
:
docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c08a7a0d0e4 ubuntu "bash" 3 minutes ago Exited (0) 2 minutes ago dazzling_taussig
Per avviare un container bloccato, utilizza docker start
, seguito dall'ID del container oppure dal nome del container. Possiamo avviare il container di Ubuntu utilizzando l'ID 1c08a7a0d0e4
:
docker start 1c08a7a0d0e4
Verrà avviato il container, e possiamo utilizzare docker ps
per visualizzare il suo stato:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c08a7a0d0e4 ubuntu "bash" 6 minutes ago Up 8 seconds dazzling_taussig
Per fermare un container in esecuzione possiamo utilizzare docker stop
, seguito dall'ID o dal nome del container. Questa volta proviamo a fermare il container utilizzando il nome assegnato ad esso da Docker, in questo caso dazzling_taussig
:
docker stop dazzling_taussig
Se dovessimo decidere che un container non è più necessario, possiamo eliminarlo utilizzando il comando docker rm
, seguito ancora una volta dall'ID o dal nome del container. Possiamo utilizzare il comando docker ps -a
per trovare l'ID o il nome del container associato all'immagine hello-world
e rimuoverlo.
docker rm adoring_kowalevski
Possiamo avviare un nuovo container con un nome specifico utilizzando il parametro --name
. Possiamo anche utilizzare il parametro --rm
per creare un container che si rimuove automaticamente quando viene fermato. Per maggiori informazioni si può consultare l'output del comando docker run help
.
Step 7 - Creare un'Immagine Docker a partire da un Container modificato
Abbiamo visto che avviando un'immagine Docker possiamo creare, modificare e cancellare file proprio come è possibile fare in una macchina virtuale. Questi cambiamenti saranno applicati solo al container in questione. Possiamo anche avviare e fermare il container, ma una volta che lo elimineremo con il comando docker rm
, tutti i cambiamenti saranno persi per sempre.
In questa sezione vedremo come salvare un container, nello stato in cui si trova, in una nuova immagine Docker.
Dopo aver installato Node.js all'interno del nostro container Ubuntu, abbiamo ancora un container in esecuzione a partire da un'immagine, tuttavia il container è diverso dall'immagine utilizzata per crearlo. Potremmo però voler riutilizzare questo container con Ubuntu e Node.js in futuro, anche come base per nuove immagini.
Possiamo eseguire il commit dei cambiamenti effettuati ad una nuova istanza dell'immagine Docker utilizzando il seguente comando:
docker commit -m "Il cambiamento che è stato fatto" -a "Nome Autore" id_container repository/nome_nuova_immagine
Il flag -m
serve per specificare il messaggio del commit, che aiuta gli altri a sapere che cambiamenti sono stati fatti, mentre -a
specifica l'autore dei cambiamenti. Il parametro id_container
è l'ID del contenitore che ci siamo annotati prima, mentre lavoravamo sul container dalla sessione Docker interattiva. A meno che tu non abbia più repository diverse, repository
è il tuo nome utente sul Docker Hub.
Per esempio, per l'utente gbfactory
e il container con ID d9b100f2f636
, il comando sarà:
docker commit -m "Installato Node.js" -a "gbfactory" d9b100f2f636 gbfactory/ubuntu-nodejs
Quando eseguiamo il commit su un'immagine, una nuova immagine sarà salvata in locale sulla tua macchina. Più avanti nella guida vedremo come inviare (push) un'immagine sul registro del Docker Hub, in modo che anche altre persone possano effettuare l'accesso.
Elencando le immagini Docker vedremo anche l'immagine che abbiamo appena salvato, così come quella vecchia da cui deriva l'immagine nuova:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
gbfactory/ubuntu-nodejs latest 7c1f35226ca6 7 seconds ago 179MB
...
In questo esempio, ubuntu-nodejs
è la nuova immagine, che deriva dall'immagine ubuntu
scaricata dal Docker Hub. Come possiamo notare dalla differenza nella dimensione delle due immagini, intuiamo che i cambiamenti sono stati salvati nella nuova immagine. Quindi, la prossima volta che si sarà necessario creare un container con Ubuntu e Node.js già installato, potrete usare questa nuova immagine.
Possiamo anche costruire le immagini da un Dockerfile
, che consente di automatizzare l'installazione dei programmi in una nuova immagine. Tuttavia, questo va oltre lo scopo di questa guida.
Step 8 - Pubblicare un'Immagine sul Docker Hub
Il passo successivo dopo aver creato una nuova immagine partendo da un'immagine esistente, è quello di condividerla con il pubblico sul Docker Hub oppure su un'altra repository Docker. Per eseguire questa operazione, chiamata push, è necessario avere un account sul Docker Hub.
Come prima cosa, iniziamo la procedura di accesso al Docker Hub:
docker login -u username-account-docker
Successivamente ci verrà chiesto di inserire la password del nostro account sul Docker Hub. Se i dati sono corretti, l'accesso sarà eseguito con successo.
Nota: se il nome utente scelto sul registro Docker è diverso dal nome utente locale utilizzato per creare l'immagine, dovrai taggare la tua immagine con il nome utente del registro. Per esempio, utilizzando l'immagine creata durante lo step precedente, il comando sarebbe:
docker tag gbfactory/ubuntu-nodejs nomeutente-dockerhub/ubuntu-nodejs
Ora siamo pronti per fare il push della nostra immagine:
docker push username-dockerhub/nome-immagine-docker
Se per esempio dobbiamo caricare l'immagine ubuntu-nodejs
all'interno della repository gbfactory, il comando sarebbe:
docker push gbfactory/ubuntu-nodejs
Questo processo potrebbe richiedere un po' di tempo per il completamento dato che deve caricare l'immagine, ma quando sarà completato, l'output sarà simile al seguente:
The push refers to a repository [docker.io/gbfactory/ubuntu-nodejs]
e3fbbfb44187: Pushed
5f70bf18a086: Pushed
a3b5c80a4eba: Pushed
7f18b442972b: Pushed
3ce512daaf78: Pushed
7aae4540b42d: Pushed
...
Dopo aver completato il caricamento dell'immagine sul registro, dovrebbe essere visibile sulla dashboard del tuo account, dovrebbe apparire come nell'immagine seguente:
Se il vostro tentativo di push è fallito con un'errore simile a questo, il motivo è probabilmente da ricercarsi sul login non effettuato o non andato a buon fine:
The push refers to a repository [docker.io/gbfactory/ubuntu-nodejs]
e3fbbfb44187: Preparing
5f70bf18a086: Preparing
a3b5c80a4eba: Preparing
7f18b442972b: Preparing
3ce512daaf78: Preparing
7aae4540b42d: Waiting
unauthorized: authentication required
Proviamo ad eseguire nuovamente l'accesso con docker login
e ripetiamo il push. Poi verifichiamo se la nostra immagine esiste sulla repository del Docker Hub.
Possiamo ora utilizzare il comando docker pull gbfactory/ubuntu-nodejs
per scaricare l'immagine su una nuova macchina e utilizzarla per avviare un nuovo container.
Conclusione
In questo tutorial abbiamo visto come installare Docker, lavorare con le immagini e con i container e anche come eseguire il push di un'immagine modificata al Docker Hub.