Guida a Docker: installazione e comandi di base

19/05/2021

Docker è un progetto open source che consente la distribuzione di web application, e non solo, all’interno di container, tipicamente basati su Linux.

Ciascun container è isolato dall’altro e qualunque modifica influirà solo sul comportamento di questo. Un container, in linea generale, si distingue da una macchina virtuale per il fatto che, mentre quest’ultima rappresenta una vera e propria emulazione di uno o più computer dentro una macchina fisica, dal BIOS al sistema operativo, il container condivide il kernel col sistema operativo host, quindi ovviamente l’impatto delle risorse occupate è ridotto. Con questo non intendo dire che i container sono meglio delle macchine virtuali, poichè gli utilizzi sono completamente differenti.

Servizi di Docker

Docker consiste sostanzialmente in due servizi:

  • Docker Engine: la vera e propria runtime che si occupa della gestione dei container e delle risorse che il sistema operativo deve dedicare;
  • Docker Hub: un repository dov’è possibile reperire le immagini dei sistemi operativi, da cui partire per creare i container.

Immagini e container

Facciamo particolare distinzione tra:

  • Immagine Docker: è un’immagine immutabile del file system di un sistema operativo, tipicamente Linux. NON può quindi essere modificato;
  • Container: è un file system temporaneo derivato dall’immagine Docker, sul quale è possibile effettuare delle modifiche. Qualunque modifica viene perduta qualora il container venisse eliminato, a meno che da tale container non si generi una nuova immagine Docker.

Il container è costituito inoltre da un network stack, con associato un indirizzo IP, nonché un gruppo di processi.

Reperire Docker

Vediamo a questo punto come si installa Docker sulle piattaforme Windows e GNU/Linux, in particolare, per quest’ultimo useremo la versione Ubuntu 20.04 LTS (essendo lo scopo quello di avere macchine più stabili possibili, le LTS sono straconsigliate).

Ubuntu

Il Docker Engine è disponibile sulle piattaforme x86_64, armhf e arm64. Qualunque altra versione o pacchetto non compatibile con Docker deve essere rimosso dal sistema, in particolare è raccomandato rimuovere i pacchetti docker, docker-engine, docker.io, contianerd, runc. Per quanto concerne Ubuntu, possiamo fare riferimento alla guida ufficiale presente sul sito. Iniziamo colL dare il comando:

sudo apt purge docker docker-engine docker.io containerd runc

Non ha importanza se i pacchetti non vengano trovati, anzi è un buon punto di partenza, vuol dire che il sistema è abbastanza pulito.

Installiamo i prerequisiti con:

apt install apt-transport-https ca-certificates curl gnupg lsb-release

Installiamo la chiave GPG di Docker con (comando unico):

curl -fsSL <https://download.docker.com/linux/ubuntu/gpg> | 
sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Aggiungiamo il repository con:

echo \\
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] <https://download.docker.com/linux/ubuntu> \\
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Aggiorniamo i repository con:

sudo apt update

Quindi installiamo i pacchetti di Docker con:

sudo apt install docker-ce docker-ce-cli containerd.io

Terminata l’installazione, aggiungiamo il nostro utente al gruppo docker per poterne eseguire i comandi:

sudo gpassswd -a <nomeUtente> docker

Buona prassi, a questo punto, riavviare. Una volta completato il riavvio, apriamo un terminale e proviamo a scrivere il comando:

docker

Se tutto è andato per il verso giusto, ci comparirà un riepilogo dei comandi utilizzabili per gestire Docker.

Windows

Docker per Windows si basa sul sottosistema Windows per Linux e richiede, in primis, che il computer supporti la virtualizzazione. Qualunque CPU moderna la supporta, ma in alcuni casi potrebbe non essere abilitata nel BIOS. All’interno dello stesso cercare voci come:

  • Intel VT-x o Intel Virtualization Technology per quanto riguarda i sistemi basati su CPU Intel;
  • AMD-V o AMD Virtualization per quanto riguarda i sistemi basati su CPU AMD.

Dopodichè, rechiamoci sul sito di Docker e scarichiamo il file di installazione per Windows. Seguiamo la procedura e attendiamo il termine per procedere col riavvio.

Al successivo riavvio ci verrà chiesto di scaricare un aggiornamento del kernel Linux per il WSL, mediante un URL dal quale si potrà scaricare il file eseguibile. Tale file dovrà essere quindi eseguito per procedere con l’installazione. Una volta terminata quest’ultima, sull’altra finestra apertasi precedentemente, bisognerà cliccare su Restart.

Docker per Windows, a differenza di quello per Linu x, è dotato di un’interfaccia grafica dalla quale gestire le caratteristiche principali di Docker, anche se non ci soffermeremo tanto su questa, poichè preferirei farvi vedere l’interazione pura con Docker, ossia quella da Terminale. Ad ogni modo, strumenti, anche grafici, li vedremo in futuro.

Ciclo di vita di un container

Un container, nel momento in cui viene creato, può:

  • Essere eseguito direttamente, evitando di giungere allo stato Stopped, e saltare direttamente allo stato Running, mediante il comando run;
  • Essere creato, ma non eseguito, passando subito allo stato Stopped, mediante il comando create.

Se il container si trova in Stopped, può essere:

  • Eseguito mediante il comando start;
  • Eliminato mediante il comando rm.

Nel momento in cui il container si trova in Running, può:

  • Essere terminato mediante il comando stop;
  • Essere ucciso, che è un’azione di stop forzata, mediante il comando kill;
  • Essere messo in pausa **mediante il comando pause.

Nel momento in cui il container si trova nello stato Paused, può essere ripristinato mediante il comando unpause. Qualsiasi container eliminato mediante il comando rm passa allo stato Dead**.** Per eliminare un container, è opportuno prima portarlo allo stato Stopped.

Il comando restart fa passare il container allo stato Stopped e successivamente allo stato Running.

Il file system di un container persiste dunque finchè la macchina a stati si alterna tra Stopped e Running, mentre viiene eliminato quando si passa a Dead.

Gestione container

Questi sono i comandi di Docker basilari per gestire i container:

  • docker run <opzioni> <immagine> <argomenti>: crea un container partendo da un’immagine e lo esegue;
  • docker rename <container> <nuovoNome>: permette di rinominare un container;
  • docker start <container>: avvia il container;
  • docker stop <container>: arresta il container;
  • docker kill <container>: forza l’arresto del container;
  • docker rm <opzioni> <container>: elimina il container. È opportuno stoppare il container, a meno che non si aggiunga l’opzione -f, in modo tale da eliminare anche i container in esecuzione.

Creazione e avvio diretto di un container

Per creare e avviare direttamente un container, useremo dunque il comando:

docker run <opzioni> <immagine> <argomenti>

Modalità interattiva

Possiamo fare immediatamente un esempio. Creiamo un container contenente l’immagine di Ubuntu, con tanto di shell testuale interattiva. Possiamo utilizzare il comando:

docker run -ti ubuntu
  • Il comando run l’abbiamo visto in precedenza cosa fa;
  • L’opzione -t consente di avviare una shell testuale nel container;
  • L’opzione -i consente di abilitare lo stdin, quindi di poter interagire con la shell del container;
  • ubuntu è il nome dell’immagine. Quest’ultimo può essere accompagnato da un tag, che permette di selezionare a quale immagine siamo nello specifico interessati. Scrivere ubuntu semplicemente equivale a scrivere ubuntu:latest, e usa sempre l’immagine più recente, ubuntu:bionic permette di scaricare l’immagine di Ubuntu 18.04 LTS, e così via.. Nel Docker Hub è possibile trovare i tag supportati dall’immagine ubuntu, così come per altre immagini.

Dando quindi INVIO, qualora l’immagine non fosse già presente in loco, verrà scaricata dal Docker Hub, per cui è opportuno in tal caso avere una connessione a internet sempre attiva. Dopodichè sarà creato il container e avviata una shell testuale.

Per uscire dalla shell testuale aperta, è sufficiente usare:

exit

Argomenti

Gli argomenti sono i comandi che si intendono passare al container, ad esempio:

docker run ubuntu cat /etc/hostname

Crea al volo un container basato sull’immagine ubuntu e viene restituito l’output del file /etc/hostname, dopodichè viene stoppato.

A tal proposito, possiamo dire infatti che docker run restituisce un codice d’uscita, quindi il container viene terminato una volta che i comandi passati vengono eseguiti tutti. Inoltre, ciascun comando può essere separato dal punto e virgola.

Esempio

docker run ubuntu cat /etc/hostname; cat /etc/lsb-release

Modalità detached

Quando riusciamo a visualizzare l’output di un container, si dice che questo sia in esecuzione in foreground mode, che è la modalità di esecuzione predefinita. Un container può tuttavia essere eseguito in un’altra modalità, la quale consente l’esecuzione in background, o deatched, aggiungendo l’opzione -d al comando.

Esempio

# Crea un container che in background aggiorna i suoi repository e poi viene terminato
docker run -d ubuntu apt update

Notiamo infatti, a differenza di prima, che non ci viene mostrata alcuna shell. Naturalmente questo comando ha particolare senso quando sappiamo di voler eseguire un processo dentro il container che continuerà la sua esecuzione in background, altrimenti non essendoci alcuna interazione il container verrà terminato automaticamente.

Directory di lavoro

La directory di lavoro predefinita è /, tuttavia è possibile cambiarla mediante l’opzione -w. Ad esempio:

docker run -ti -w /etc ubuntu

Variabili d’ambiente

È possibile inoltre configurare delle variabili d’ambiente, mediante l’opzione -e. L’operazione può essere ripetuta più volte, ad esempio:

docker run -ti -e DISNEY=topolino -e DREAMWORKS=shrek ubuntu

Il container sarà configurato con le variabili d’ambiente specificate. Infatti scrivendo:

echo $DISNEY
echo $DREAMWORKS

Ci viene stampata la configurazione delle variabili vista precedentemente.

Hostname

È possibile configuare anche l’hostname del container, mediante l’opzione -h. Ad esempio:

docker run -ti -h pippo ubuntu

Scrivendo nella shell quindi:

cat /etc/hostname

Viene visualizzato l’hostname configurato in precedenza.

Impostazione del nome di un container

Di default, Docker genera un nome a caso per i container creati. Aggiungendo l’opzione —name è possibile impostare un nome personalizzato, che deve essere comunque univoco per ciascun container. Ad esempio:

docker run -ti --name paperino ubuntu

Crea un container di nome paperino derivato dall’immagine di Ubuntu.

Rimozione automatica di un container

Di default, un container Docker rimane memorizzato anche dopo l’esecuzione del comando docker run. Aggiungendo l’opzione —rm, è possibile eliminare il container una volta utilizzato. Utile quindi per un approccio usa e getta. Ad esempio:

docker run -ti --name usaegetta --rm ubuntu

A questo punto, scrivendo exit, il container, non solo sarà stoppato, ma anche eliminato.

Gestione processi

Se a questo punto apriamo un altro terminale, scrivendo il comando:

docker ps

Possiamo visualizzare l’elenco dei container in esecuzione. Ciascun container è contrassegnato da:

  • Un container ID: l’identificativo univoco del container;
  • L’immagine utilizzata;
  • Il comando in esecuzione;
  • La data di creazione;
  • Le porte utilizzate;
  • Il nome del container: notate come questo, se non specificato, verrà scelto in maniera randomica da Docker;
  • Lo stato.

Usando inoltre il comando:

docker ps -a

Nell’elenco compariranno anche i container non in esecuzione.

Identificare un container

I container possono essere identificati per nome, che viene scelto a random da Docker qualora non venisse specificato tramite l’opzione —name.

In alternativa, è possibile identificare il container per ID. Inoltre, è possibile specificare l’ID nei comandi anche in modo parziale, tenendo tuttavia presente che le cifre/lettere più a destra sono quelle più significative, per cui prestare attenzione nell’identificarlo per bene quando vi sono container con ID simili.

Rimuovere un container

Per rimuovere un container, ammesso e concesso che sia stato stoppato (altrimenti bisognerà farlo), usiamo ad esempio:

docker rm pippo # oppure il suo ID

Rimozione container zombie

Tutti i container NON in esecuzione possono essere rimossi in un colpo, utilizzando il comando:

docker container prune

Quindi digitare y e poi premere INVIO.

Avviare un container già esistente

Supponendo di aver già creato un container e quindi siamo intenzionati ad eseguirlo in modalità interattiva, possiamo usare il comando:

docker start -i <container>

Interagire con un container già avviato

È possibile interagire con un container già avviato, utilizzando il comando:

docker exec -ti <nome/ID> bash

ATTENZIONE: Uscendo dall’interazione testuale, nel caso specifico, il container non viene terminato, ma rimane comunque in esecuzione, a differenza del comando run visto precedentemente.

Comandi ispezione dei container Docker

Di seguito un elenco di comandi importanti per ispezionare un container:

  • docker ps: visto prima, visualizza i container in esecuzione. Aggiungendo l’opzone -a permette di visualizzare TUTTI i container;
  • docker logs [opzioni] container: visualizza l’output del container. Aggiungendo l’opzione -f, l’output viene seguito in tempo reale;
  • docker top container [opzioni ps]: visualizza i processi in esecuzione all’interno del container;
  • docker stats container: visualizza le statistiche di utilizzo di un container. Per uscire usare CTRL+C;
  • docker diff container: confronta le variazioni del file system del container rispetto all’immagine da cui esso deriva;
  • docker inspect container: restituisce in output la configurazione del container sotto forma di file js. Questo comando è interessante se associato alle pipe di Unix e in particolare il comando grep.

Esempio

docker inspect container | grep "IPAddress"
  • docker cp container:percorso percorsoHost: permette di copiare file dal container al sistema host;
  • docker cp percorsoHost container:percorso: permette di copiare file dal sistema host al container;
  • docker export container: permette di esportare il contenuto del container sotto forma di archivio tar;
  • docker exec container [argomenti]: permette di eseguire un comando all’interno di un container già in esecuzione. Ad esempio, docker exec -i pippo /bin/bash permette di eseguire una shell bash del container pippo;
  • docker commit container nome: crea un’immagine del file system del container, utilizzabile poi per creare altri container.
  • docker image list: visualizza la lista delle immagini situate in loco, dalle quali è possibile creare i container;
  • docker image rm immagine: permette di rimuovere un’immagine situata in loco;
  • docker image prune: permette di rimuovere le immagini non utilizzate.

Per approfondire, vi invito a guardare il seguente video:

https://youtu.be/9AikG4nbvtc