Architettura Docker
La tecnologia Docker, basata sui container, viene adottata in ogni fase del ciclo di vita delle applicazioni. Andiamo a scoprire i concetti base di questa tecnologia.

Share

Reading time: 6 minutes

Fino a qualche anno fa la configurazione di un server per uno o più servizi era un lavoro difficile, lungo e con molte insidie. Ad esempio, far coesistere due applicazioni web basate su linguaggi di programmazione diversi e/o librerie diverse poteva far riscontrare alcuni problemi che minavano il corretto funzionamento dei sistemi. Non era infatti possibile creare spazi di lavoro separati che non interferissero tra di loro poiché ogni servizio richiedeva qualcosa al sistema operativo del server. Inoltre, aggiornamenti del server o dei programmi potevano compromettere il funzionamento di uno o più servizi.

Negli anni si è quindi sviluppato un sistema che fornisse un’astrazione uniforme dal sistema operativo sottostante, rendendo molto più facile per gli sviluppatori e amministratori del sistema testare e rilasciare programmi e servizi.
Il Docker risolve gran parte dei problemi che abbiamo introdotto in precedenza. Questa tecnologia prevede una virtualizzazione a livello di sistema operativo, anche conosciuta come container. Attenzione a non confondere questa tipologia di virtualizzazione con quella hardware.

Docker è disponibile per le piattaforme Linux, MacOS e Windows 10. Per Windows e Mac esiste il Docker Desktop che è un’applicazione facile da installare e che permette di costruire e condividere applicazioni e microservizi containerizzati. Inoltre include Docker Engine, Docker CLI client, Docker Compose, Docker Compose, Notary, Kubernetes e Credential Helper. Per Linux le istruzioni differiscono in base alla distribuzione utilizzata. Trovate tutti i dettagli per ciascun sistema operativo nella guida ufficiale.

In questo articolo andremo ad introdurre i concetti fondamentali su cui si basa Docker. Se volete però partire subito ad utilizzarlo vi consiglio il video “How to get started with Docker” presentato all conferenza DockerCon del 2020.

Se volete invece cercare il libro più adatto alle vostre esigenze questi sono alcuni suggerimenti.

Docker engine

Il Docker Engine è un’applicazione client-server costituita da:

  • un server che è un processo demone (dockerd)
  • un API REST che specifica le interfacce che i programmi possono usare per comunicare con il demone
  • un’interfaccia a linea di comando (CLI) (docker)

Poiché viene utilizzata un’architettura client-server, il client istruisce tramite le API REST il demone che si occupa di “buildare”, gestire e distribuire i container Docker. Possono funzionare sia sulla stessa macchina o su macchine diverse.
In figura è riportato uno schema dell’architettura.

Architettura Docker

Docker CLI

Il Docker CLI è il modo principale per interagire con Docker. Mediante le API REST il Docker CLI invia una richesta al demone Docker per eseguire un particolare task. I comandi più comuni sono i seguenti:

docker build
docker pull
docker run
docker exec 

Per una panoramica completa di tutti i comandi vi rimandiamo alla guida ufficiale.

Immagini Docker

Un’immagine è un modello di sola lettura con le istruzioni per la creazione di un contenitore Docker. Spesso un’immagine è basata su un’altra immagine, con qualche ulteriore personalizzazione. Per esempio, si può costruire un’immagine che si basa sull’immagine ubuntu, ma installa il server web Apache e la propria applicazione, così come i dettagli di configurazione necessari per far funzionare l’applicazione.

L’immagine Docker è un template di sola lettura che costituisce la base di qualsiasi applicazione sviluppata con questa tecnologia. Potremmo paragonarla ad uno script shell che prepara un sistema con le impostazioni e le librerie necessarie. Tutte le immagini Docker si basano a loro volta su un’altra immagine. L’immagine base è tipicamente un sistema operativo linux. Per mantenere la dimensione delle immagini più piccola possibile si usano di solito pacchetti di sistemi operativi come Alpine che è una distribuzione Linux di solo 5MB.

Si possono creare le proprie immagini o si possono utilizzare solo quelle create da altri e pubblicate in un registro. Esistono molte immagini pre-costruite per alcuni degli stack di applicazioni più comuni, come Ruby on Rails, Django, PHP-FPM con nginx, e così via. Per costruire invece la propria immagine, si crea un Dockerfile. Come vedremo in seguito, il Dockerfile definisce i passi necessari per creare un’immagine ed eseguirla. Ogni istruzione in un Dockerfile crea un livello nell’immagine. Quando si modifica il Dockerfile e si ricostruisce l’immagine, vengono ricostruiti solo i livelli che sono stati modificati. Questo fa parte di ciò che rende le immagini così leggere, piccole e veloci, se confrontate con altre tecnologie di virtualizzazione. L’immagine create può essere poi condivisa pubblicamente sul Docker Hub.

Container

Quando un’immagine viene eseguita su un host, questa genera un processo chiamato container. Essendo Docker scritto in linguaggio di programmazione Go, sfrutta diverse caratteristiche del kernel Linux per fornire le sue principali funzionalità. Tra queste c’è l’utilizzo del namespace che serve ad isolare il workspace di ciascun container. Per impostazione predefinita, un container è relativamente ben isolato dagli altri container e dalla macchina host. È possibile tuttavia definire il livello di isolamento della rete, dello storage o di altri sottosistemi sottostanti.

Mediante i comandi del Docker CLI è possibile creare, avviare, fermare, spostare o cancellare un contenitore. Inoltre, un container può essere collegato a una o più reti e/o ad un archivio.

 

Attenzione

Un aspetto importante da comprendere è che quando un container è in esecuzione, le modifiche vengono applicate al livello del container. Pertanto, quando il container viene rimosso, ogni cambiamento del suo stato non viene salvato. Per ovviare alla perdita dello stato si utilizzano storage persistenti mediante la definizione di volumi.

Se siete curiosi di vedere come si crea un container da zero in linguaggio Go vi consiglio di vedere il talk di Liz Rice. Durante la creazione di un semplice container, è possibile capire come Docker funziona nonostante alcuni aspetti non vengano trattati.

Dockerfile

Un Dockerfile è un documento di testo che contiene in sequenza tutti i comandi che un utente può chiamare sulla linea di comando per assemblare un’immagine. Per costruire un’immagine è necessario lanciare il comando:

$ docker build 

Un semplice esempio di Dockerfile è il seguente:

FROM ubuntu:latest
LABEL author="flowygo"
LABEL description="An example Dockerfile"
RUN apt-get install python
COPY hello-world.py
CMD python hello-world.py 

E’ possibile però complicare il Dockerfile a piacimento per realizzare immagini con le caratteristiche desiderate.

Nella tabella sottostante riportiamo i comandi più utilizzati all’interno del Dockerfile.

IstruzioneDescrizione
FROMimmagine di base da utilizzare per le successive istruzioni
WORKDIRimposta l'attuale directory di lavoro per le istruzioni RUN, CMD,ENTRYPOINT, COPY e ADD
COPYcopia i file nel contenitore
ADDsimile al comando COPY supporta l'estrazione di archivi e di URL remote
RUNesegue tutti i comandi in un nuovo livello in cima all'immagine corrente e creerà un nuovo livello disponibile per i prossimi passi nel Dockerfile
CMDfornisce i valori predefiniti per un contenitore in esecuzione
ENTRYPOINTindica il comando da eseguire all'avvio del container
ENVimposta le variabili d'ambiente dell'immagine
VOLUMEcrea una directory sull'host che viene montata su un percorso specificato all'interno dell'istruzione
LABELaggiunge metadati ad un'immagine come coppia chiave/valore
EXPOSEspecifica le porte di rete su cui il container può ricevere
ONBUILDviene eseguito dopo il completamento della costruzione del Dockerfile corrente. ONBUILD viene eseguito in qualsiasi immagine figlia derivata dall'immagine corrente

Per lanciare un container basato sulla nuova immagine basta eseguire il comando

$ docker run 

Per interagire invece con il container si usa il comando

$ docker exec  

Mediante quest’ultimo comando è possibile ad esempio aprire una shell all’interno del container o eseguire un programma. Per ogni dettaglio vi rimandiamo alla documentazione ufficiale.


Linee guida e raccomandazioni per la scrittura dei Dockerfile

Per scrivere correttamente di un Dockerfile è necessario seguire alcune linee guida.

Creare container “effimeri”

L’immagine definita dal vostro Dockerfile dovrebbe generare container il più possibile “effimeri”. Con il termine “effimero” si intende che il container può essere fermato e distrutto, poi ricostruito e sostituito con un minimo assoluto di set up e configurazione.


Escludere file non necessari dal contesto di build

Per non aumentare i tempi di esecuzione per la costruzione di una nuova immagine e quindi di un container è necessario mantenere al minimo il contesto. Quando si lancia il comando docker build, se non diversamente specificato, la cartella corrente viene usata come contesto per costruire la nuova immagine. Per evitare di includere file non necessari, e di conseguenza aumentare le dimensioni dell’immagine, si può usare il file .dockerignore.

Minimizzare i layer

E’ importante ridurre il numero di layer dell’immagine. A partire da Docker 1.10 e superiori, solo le istruzioni RUN, COPY e ADD creano nuovi layer. Si consiglia di combinare insieme alcuni comandi per ridurre il numero di layer creati. Dove possibile, utilizzare build multistage e copiare solo gli artefatti necessari nell’immagine finale. Ciò permette di includere applicazioni o file necessari nelle fasi intermedie, senza però aumentare le dimensioni dell’immagine finale.

Utilizzare build multistage

Con i build multistage, si utilizzano più dichiarazioni FROM nel Dockerfile. Ogni istruzione FROM può utilizzare un’immagine diversa, ed ognuna di esse inizia una nuova fase di build. È possibile copiare selettivamente gli artefatti da una fase all’altra, lasciando tutto ciò che non si vuole nell’immagine finale. In questo modo la dimensione dell’immagine finale sarà ridotta e conterrà solo gli artefatti necessari. Per maggiori dettagli vi rimandiamo alla documentazione ufficiale.

Non installare pacchetti non necessari

Al fine di ridurre la complessità, le dipendenze, le dimensioni dei file e i tempi di compilazione, si consiglia di installare solo i pacchetti strettamente necessari.

Approfondimento: aggiungere un utente al docker group

Nelle installazioni su server Linux, il demone Docker si lega ad un socket Unix invece che ad una porta TCP. Per default quel socket Unix è di proprietà dell’utente root e gli altri utenti possono accedervi solo usando sudo. Il demone Docker, pertanto, funziona sempre come utente root.

Per evitare di lanciare tutti i comandi del Docker CLI come superuser, è necessario legare l’utente al gruppo docker. Di solito questo gruupo viene creato al momento dell’installazione. Diversamente lo si può aggiungere mendiante il comando

$ sudo groupadd docker 

A questo punto potete aggiungere un utente al gruppo docker

$ sudo usermod -aG docker $USER 

Per vedere che l’inclusione dell’utente sia avvenuta con successo, bisogna che l’utente effettui il logout dalla shell corrente. Dopodiché si può verificare di poter eseguire comandi docker senza sudo.

$ docker run hello-world 

Il comando riportato scarica un’immagine di prova e la esegue in un container. Quando il container viene eseguito, stampa un messaggio informativo ed esce.

Ulteriori dettagli sono disponibili a questa pagina.

Letture consigliate

More To Explore

Intelligenza artificiale

Gradio: applicazioni web in python per AI [parte 3]

Con Gradio è possibile creare applicazioni web per i nostri modelli di machine learning e AI in poche righe di codice. Mediante alcuni esempi, vedremo le funzionalità avanzate disponibili, quali l’autenticazione, il caching e l’elaborazione dei file in ingresso. Costruiremo anche un chatbot e un classificatore di immagini partendo da modelli pre-addestrati. Infine discuteremo come distribuire il nostro progetto in pochi semplici passi.

Intelligenza artificiale

Gradio: applicazioni web in python per AI [parte2]

Gradio è una libraria python che ci permette di creare applicazioni web in modo veloce e intuitivo per i nostri modelli di machine learning e AI. Le nostre applicazioni richiedono sempre un’interazione con l’utente e una personalizzazione del layout. Scopriamo, mediante degli esempi, come migliorare le nostre applicazioni.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Design with MongoDB

Design with MongoDB!!!

Buy the new book that will help you to use MongoDB correctly for your applications. Available now on Amazon!