Cos'è un container
Un container può essere definito come un processo isolato.
Vengono utilizzate delle funzionalità del kernel Linux come cgroup e namespace per consentire a questi
processi (i container) di essere indipendenti con gli altri processi e con altri container stessi anche se vengono eseguiti sulla stessa macchina.
Virtual Machine vs Container
Una macchia virtuale (VM) è una virtualizzazione dell'hardware del computer host affinché si possa eseguire un nuovo sistema operativo (un nuovo kernel).
Se si sceglie di utilizzare questa tecnologia per isolare le varie applicazioni in produzione, sarà necessario creare più VM.
Le macchine virtuali sono create grazie ad un software del sistema operativo host, chiamato hypervisor o anche virtual machine monitor (VVM).
L'hypervisor quindi ha il compito di creare ed eseguire VM.
Un container invece è semplicemente un processo isolato che condivide con il sistema host il suo kernel. Di conseguenza, un container è molto
più leggero di una VM (ordine di MB vs ordine di GB). Un'altra conseguenza è che il processo di import/export di un container su una nuova macchina
è molto più veloce rispetto a quello su una VM.
Di base, ogni nostra applicazione sarà un container.
Qui un disegno che riassume il concetto scritto sopra:
Virtual Machine e Container: l'uno esclude l'altro?
No, anzi, spesso in produzione vengono eseguiti container all'interno di VM.
Cos'è Docker
Docker è una tecnologia che permette la creazione di container e la gestione del loro ciclo di vita (crearli, avviarli, spegnerli, distruggerli...).
Il motore che fa funzionare Docker è chiamato Docker Engine.
Esso è basato sull'architettura client-server, dove il server è il demone Docker chiamato dockerd, che permette di effettuare le varie operazioni sui container,
mentre il client è la CLI, che tramite le API di Docker interagisce col demone Docker, attraverso un canale di comunicazione di tipo socket Unix
(il demone Docker è in ascolto su /var/run/docker.sock
).
È possibile avere client e server sulla stessa macchina, ma anche in macchine diverse. Oltre alla CLI, possiamo avere anche interfacce grafiche (ad esempio Portainer) come client Docker.
Immagini per creare container
Un'immagine è un template di sola lettura che contiene delle istruzioni per creare container.
Facendo un paragone con la programmazione ad oggetti, potremmo associare le immagini alle classi, mentre i container agli oggetti. Dalla stessa immagine si possono creare più container (così come dalla stessa classe si possono creare pi ù oggetti).
Un'immagine è basata da più layers di sola lettura. La creazione di un container, crea un ulteriore layer,
chiamato "container layer", che è l'unico scrivibile (e non è persistente).
Inoltre, spesso un'immagine è basata da una o più immagini. Ad esempio potresti creare un'immagine della
tua applicazione partendo da una immagine ubuntu.
Si possono creare immagini custom tramite un file chiamato Dockerfile (vedremo nell'articolo successivo come si usa), oppure si possono scaricare immagini da un registry, come il Docker Hub, che è il registro di default.
Installazione di Docker
Abbiamo detto che i container sfruttano delle features del kernel Linux, quindi consiglio di provare Docker su una distro Linux,
anche in VM.
Altrimenti è possibile installare Docker Desktop per Windows e MacOS, che permette di eseguire Docker in maniera virtualizzata.
A questo link trovate la guida ufficiale per installare Docker su tutti i sistemi operativi: https://docs.docker.com/engine/install/.
Creiamo un container Ubuntu: i comandi pull e run
Per verificare che l'installazione sia andata a buon fine, eseguiamo da terminale: docker version
In risposta dovremmo avere le info sul client Docker e sul server Docker.
Proviamo ora a scaricare l'immagine di Ubuntu eseguendo questo comando: docker pull ubuntu
dovremmo avere una risposta simile:
Using default tag: latest
latest: Pulling from library/ubuntu
35807b77a593: Pull complete
Digest: sha256:9d6a8699fb5c9c39cf08a0871bd6219f0400981c570894cd8cbea30d3424a31f
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest
L'immagine, non essendo presente in locale, viene scaricata dal registry di default, il Docker Hub (Downloaded newer image). Inoltre da notare il tag latest; se non specifichiamo un tag, Docker di default scarica quella con tag latest.
Per vedere la lista delle immagini scaricate, possiamo eseguire il comando: docker images
Per creare un container a partire da un'immagine, dobbiamo utilizzare il comando run seguito dal nome dell'immagine o dall'image id: docker run ubuntu
Vediamo ora la lista di container attivi: docker ps
La lista è vuota! Vediamo allora la lista di tutti i container, anche quelli non attivi: docker ps -a
Lo stato del container è Exited; questo perché i container eseguono un comando principale (con PID 1), in questo caso "bash", poi terminato il comando, si stoppano.
Eliminamo il container appena creato con il comando rm seguito dal nome del container o dal container id: docker rm priceless_hellman
Ora creiamo nuovamente il container in questo modo: docker run --name ubuntu-container -it -d ubuntu bash
Analizziamo il comando:
- Con --name specifichiamo il nome del container.
- Con -it stiamo unendo le opzioni -i e -t. Con -i, manteniamo lo STDIN anche se non siamo in attached al container (vedremo tra poco cosa significa). Con -t colleghiamo un terminale al container.
- Con -d indichiamo a Docker che vogliamo eseguire il container in modalità detach, ovvero in background.
- L'ultimo comando, dopo il nome dell'immagine, è il comando che vogliamo eseguire, in questo caso bash.
Eseguiamo di nuovo docker ps
per vedere la lista di container attivi:
Questa volta il container è UP & Running!
Stop e Start di un container
Per stoppare un container, basta eseguire il comando stop seguito dal nome del container o dal container id: docker stop ubuntu-container
Per avviarlo, basta eseguire il comando start: docker start ubuntu-container
Entriamo nel container: il comando attach
Per entrare in un container, possiamo utilizzare il comando attach, seguito dal nome del container:
docker attach ubuntu-container
Ora siamo dentro il container, con l'utente root! Eseguiamo il comando ps aux
per vedere quali processi sono attivi
nel container:
Ci sono due processi attivi, il primo, con PID 1, è il comando che abbiamo utilizzato durante la creazione del container, il secondo è il comando appena eseguito per vedere la lista di processi.
Ora usciamo dal container eseguendo il comando exit.
Vediamo di nuovo la lista di container attivi. Abbiamo una sorpresa: il container di ubuntu non è più attivo!
Questo perché, di default, eseguendo il comando exit, Docker uccide il processo principale, con PID 1. Il container è attivo solo se il processo con PID 1 è attivo.
Per far sì che quando siamo attaccati al container, esso non termini quando usciamo, dobbiamo premere CTRL+p e poi CTRL+q.
Eseguire comandi su un container avviato: il comando exec
Possiamo eseguire comandi su un container senza avere la necessità di entraci dentro.
Ad esempio potremmo scrivere una cosa del genere:
Potremmo anche entrare in un container eseguendo un altro comando bash, evitando quindi il comando attach:
Da notare che adesso i processi bash sono due. Quando usciremo dal container col comando exit, fermeremo il secondo processo bash, quindi il container continuerà ad essere UP e Running.
Conclusioni
In questo articolo introduttivo su Docker abbiamo visto rapidamente cos'è un container e qualche differenza con le VM.
Abbiamo poi visto le operazioni base di Docker per gestire il ciclo di vita di un container ed altri comandi come attach e exec.
Nel prossimo articolo vedremo come creare un'immagine custom, col Dockerfile.
Articoli su Docker: Docker
Libri consigliati su Docker e Kubernetes:
- Docker: Sviluppare e rilasciare software tramite container: https://amzn.to/3AZEGDI
- Kubernetes TakeAway: Implementa i cluster K8s come un professionista: https://amzn.to/3dVxxuP