Docker è una tec­no­lo­gia per la vir­tua­liz­za­zio­ne di ap­pli­ca­zio­ni software basata su container. L’approccio main­stream basato sui container di Docker ha tra­sfor­ma­to lo sviluppo di ap­pli­ca­zio­ni negli ultimi anni. Questo ha infatti in­fluen­za­to tutte le diverse aree di sviluppo, compreso il modo in cui le ap­pli­ca­zio­ni e i com­po­nen­ti sono costruiti e i servizi software di­stri­bui­ti, spo­stan­do­li dallo sviluppo alla pro­du­zio­ne. Con Docker, tutti questi processi vengono eseguiti in modo diverso rispetto a prima.

Non sono cambiati solo i processi di sviluppo ma anche l’ar­chi­tet­tu­ra del software, spo­stan­do­si da soluzioni globali mo­no­li­ti­che verso cluster di “mi­cro­ser­vi­zi” leggeri. Questo a sua volta ha reso i sistemi ri­sul­tan­ti più complessi. Negli ultimi anni, il software Ku­ber­ne­tes si è affermato per gestire ap­pli­ca­zio­ni multi-container.

Lo sviluppo della vir­tua­liz­za­zio­ne basata su container è lungi dall’essere finito, rimanendo quindi un settore en­tu­sia­sman­te. In questo articolo, vi spie­ghe­re­mo come funziona Docker come tec­no­lo­gia di base e vedremo cosa ne ha motivato lo sviluppo.

N.B.

Il nome “Docker” ha diversi si­gni­fi­ca­ti. È usato come sinonimo del software stesso, per designare il progetto open source su cui si basa e per indicare una società sta­tu­ni­ten­se che gestisce com­mer­cial­men­te vari prodotti e servizi.

Una breve storia di Docker

Il software ori­gi­na­ria­men­te ri­la­scia­to con il nome di “Docker” è stato costruito sulla base della tec­no­lo­gia Linux Container (LXC). LXC è stata poi so­sti­tui­ta dalla lib­con­tai­ner, propria di Docker. Nel tempo sono stati aggiunti nuovi com­po­nen­ti software mentre Docker ha con­ti­nua­to a crescere e a diventare lo standard per la vir­tua­liz­za­zio­ne basata su container. Dallo sviluppo di Docker sono emerse ca­rat­te­ri­sti­che come con­tai­nerd, un runtime per container con l’im­ple­men­ta­zio­ne pre­de­fi­ni­ta runC. Oggi, entrambi i progetti sono gestiti dalla Cloud Native Computing Foun­da­tion (CNCF) e dalla Open Container Ini­tia­ti­ve (OCI).

Oltre al team Docker, aziende tec­no­lo­gi­che leader come Cisco, Google, Huawei, IBM, Microsoft e Red Hat sono coinvolte nello sviluppo di Docker e delle tec­no­lo­gie correlate. Inoltre, ora anche Windows è uti­liz­za­to come ambiente nativo per i container Docker, oltre al kernel Linux. Di seguito ri­por­tia­mo alcune delle prin­ci­pa­li tappe nella storia evolutiva di Docker:

Anno Tappe fon­da­men­ta­li nello sviluppo di Docker
2007 Tec­no­lo­gia cgroups integrata nel kernel Linux
2008 Rilascio di LXC; tec­no­lo­gia basata su cgroup e namespace Linux come è avvenuto per Docker in seguito
2013 Rilascio di Docker come open source
2014 Docker diventa di­spo­ni­bi­le su Amazon EC2
2015 Rilascio di Ku­ber­ne­tes
2016 Docker di­spo­ni­bi­le su Windows 10 Pro via Hyper-V
2019 Docker di­spo­ni­bi­le su Windows Home via WSL2
Consiglio

Alla fine dell’articolo si discuterà in dettaglio di ciò che ha motivato lo sviluppo di Docker e le tec­no­lo­gie di vir­tua­liz­za­zio­ne simili.

Cos’è Docker?

La fun­zio­na­li­tà prin­ci­pa­le di Docker è la vir­tua­liz­za­zio­ne basata su container delle ap­pli­ca­zio­ni. Questo è in contrasto con la vir­tua­liz­za­zio­ne eseguita con macchine virtuali (VM). Con Docker, il codice dell’ap­pli­ca­zio­ne, comprese tutte le di­pen­den­ze, è im­pac­chet­ta­to in un’immagine. Il software Docker esegue l’ap­pli­ca­zio­ne im­pac­chet­ta­ta in un container Docker. Le immagini possono essere spostate tra i sistemi ed eseguite su qualsiasi sistema che esegue Docker.

Citazione

“Con­tai­ners are a stan­dar­di­zed unit of software that allows de­ve­lo­pers to isolate their app from its en­vi­ron­ment […]“ – Af­fer­ma­zio­ne di uno svi­lup­pa­to­re Docker, fonte: https://www.docker.com/why-docker

“I container sono un’unità stan­dar­diz­za­ta di software che permette agli svi­lup­pa­to­ri di isolare la loro ap­pli­ca­zio­ne dal suo ambiente [...]” – (Tra­du­zio­ne: IONOS)

Come nel caso della di­stri­bu­zio­ne delle macchine virtuali (VM), un obiettivo primario dei container Docker è quello di isolare l’ap­pli­ca­zio­ne in ese­cu­zio­ne. A dif­fe­ren­za delle VM, tuttavia, non viene vir­tua­liz­za­to il sistema operativo completo. Invece, Docker alloca alcune risorse del sistema operativo e dell’hardware a ogni container. Un’immagine Docker può creare un qualsiasi numero di container ed eseguirli pa­ral­le­la­men­te. Questo è il modo in cui vengono im­ple­men­ta­ti i servizi cloud scalabili.

Anche se si parla di Docker come un unico software, in realtà si tratta di più com­po­nen­ti software che co­mu­ni­ca­no at­tra­ver­so l’API del Docker Engine. Inoltre, viene uti­liz­za­ta una manciata di oggetti Docker speciali, come le già citate immagini e i container. I workflow specifici di Docker sono composti da com­po­nen­ti software e oggetti Docker. Diamo un’occhiata a come in­te­ra­gi­sco­no in dettaglio.

Software Docker

La base del software Docker è il “Docker-Engine”. Questo è usato prin­ci­pal­men­te per gestire e con­trol­la­re i container e le immagini sot­to­stan­ti. Per delle fun­zio­na­li­tà ulteriori vengono usati degli strumenti speciali, necessari prin­ci­pal­men­te per gestire le ap­pli­ca­zio­ni che con­si­sto­no in gruppi di container.

Docker Engine

Il Docker Engine viene eseguito su un sistema locale o su un server e consiste di due com­po­nen­ti:

  1. Il demone Docker (Dockerd): è sempre in ese­cu­zio­ne in back­ground e scansiona le richieste API del Docker Engine. Dockerd risponde ai comandi ap­pro­pria­ti per gestire i container e altri oggetti Docker.
  2. Il client Docker (Docker): è un programma a riga di comando. Il client Docker è usato per con­trol­la­re il Docker Engine e fornisce comandi per creare e costruire container Docker, così come creare, ottenere ed ef­fet­tua­re il controllo delle versioni delle immagini Docker.

Docker Engine API

L’API del Docker Engine è un’API REST che si in­ter­fac­cia con il demone Docker. Per integrare l’API del Docker Engine in progetti software sono di­spo­ni­bi­li “kit di sviluppo software” (SKD) ufficiali per Go e Python. Esistono anche librerie simili per più di una dozzina di altri linguaggi di pro­gram­ma­zio­ne. È possibile accedere all’API tramite la riga di comando uti­liz­zan­do il comando Docker. Inoltre, potete accedere di­ret­ta­men­te all’API uti­liz­zan­do cURL o strumenti simili.

Strumenti Docker

Quando si usano macchine virtuali, spesso si usano sistemi composti da diversi com­po­nen­ti software. Al contrario, la vir­tua­liz­za­zio­ne basata su container di Docker favorisce la for­ma­zio­ne di cluster di mi­cro­ser­vi­zi li­be­ra­men­te ac­cop­pia­ti. Questi sono adatti a soluzioni cloud di­stri­bui­te che offrono un alto grado di mo­du­la­ri­tà e alta di­spo­ni­bi­li­tà. Tuttavia, questi tipi di sistemi diventano sempre più complessi. Per gestire le ap­pli­ca­zio­ni con­tai­ne­riz­za­te in modo ef­fi­cien­te, si uti­liz­za­no strumenti software speciali noti come “or­che­stra­to­ri”.

Docker Swarm e Docker Compose sono due strumenti ufficiali di Docker di­spo­ni­bi­li per or­che­stra­re cluster di container. Il comando “Docker swarm” può essere uti­liz­za­to per combinare più motori Docker in un motore virtuale. I singoli motori possono quindi essere gestiti su più sistemi e in­fra­strut­tu­re. Il comando “Docker compose” è usato per creare ap­pli­ca­zio­ni multi-container note come “stack”.

L’or­che­stra­to­re Ku­ber­ne­tes, ori­gi­na­ria­men­te svi­lup­pa­to da Google, è più user-friendly di Swarm e Compose. Per questo motivo è quindi diventato uno standard am­pia­men­te uti­liz­za­to nell’industria IT. Le società di hosting e altri fornitori di soluzioni “Software as a Service” (SaaS) e “Platform as a Service” (PaaS) uti­liz­za­no sempre più Ku­ber­ne­tes come in­fra­strut­tu­ra sot­to­stan­te.

Oggetti Docker

I workflow nell’eco­si­ste­ma Docker sono il risultato di come gli oggetti Docker in­te­ra­gi­sco­no tra loro. Questi vengono gestiti tramite la co­mu­ni­ca­zio­ne con l’API del Docker Engine. Di seguito esa­mi­nia­mo ogni tipo di oggetto in dettaglio.

Immagine Docker

Un’immagine Docker è un modello di sola lettura volto a creare uno o più container identici. Le immagini Docker sono fon­da­men­tal­men­te i semi del sistema, uti­liz­za­te per im­pac­chet­ta­re e di­stri­bui­re le ap­pli­ca­zio­ni.

Per con­di­vi­de­re le immagini Docker vengono uti­liz­za­ti vari re­po­si­to­ry. Esistono sia re­po­si­to­ry pubblici che privati. In data corrente, esistono più di cinque milioni di immagini diverse di­spo­ni­bi­li per il download sul popolare servizio “Docker Hub”. I comandi Docker “Docker pull” e “Docker push” sono usati per scaricare un’immagine da un re­po­si­to­ry o per con­di­vi­der­la.

Le immagini Docker sono costruite a livelli. Ogni livello rap­pre­sen­ta un cam­bia­men­to specifico dell’immagine. Questo si traduce in un continuo controllo delle versioni delle immagini, che permette un rollback a uno stato pre­ce­den­te. Un’immagine esistente può essere usata come base per crearne una nuova.

Doc­ker­fi­le

Un Doc­ker­fi­le è un file di testo che descrive la struttura di un’immagine Docker. Un Doc­ker­fi­le è simile a uno script di ela­bo­ra­zio­ne batch, cioè il file contiene comandi che de­scri­vo­no un’immagine. Quando si esegue un Doc­ker­fi­le, i comandi vengono elaborati uno dopo l’altro. Ogni comando crea un nuovo livello nell’immagine Docker. Quindi si può anche pensare a un Doc­ker­fi­le come a una sorta di ricetta usata come base per creare un’immagine.

Container Docker

Ora passiamo al concetto prin­ci­pa­le dell’universo Docker: i container Docker. Mentre un’immagine Docker è un modello inerte, un container Docker è un’istanza attiva e in ese­cu­zio­ne di un’immagine. Un’immagine Docker esiste lo­cal­men­te in una singola copia e occupa uno spazio di ar­chi­via­zio­ne minimo. Al contrario, una stessa immagine Docker può creare diversi container che possono a loro volta essere eseguiti allo stesso tempo.

Ogni container Docker consuma una certa quantità di risorse di sistema per poter fun­zio­na­re, come l’uso della CPU, la RAM, le in­ter­fac­ce di rete, ecc. Un container Docker può essere creato, avviato, fermato e distrutto. È anche possibile salvare lo stato di un container in ese­cu­zio­ne come una nuova immagine.

Volume Docker

Come abbiamo visto, un container Docker in ese­cu­zio­ne viene creato a partire da un’immagine non mo­di­fi­ca­bi­le. Che dire invece dei dati che vengono uti­liz­za­ti all’interno del container e devono essere con­ser­va­ti oltre la sua vita utile? In questo caso vengono uti­liz­za­ti i volumi Docker. Un volume Docker esiste al di fuori di un container specifico. Quindi diversi container possono con­di­vi­de­re un volume. I dati contenuti nel volume sono ar­chi­via­ti sul file system dell’host. Ciò significa che un volume Docker è come una cartella condivisa su una macchina virtuale.

Come funziona Docker?

Il principio di fun­zio­na­men­to di base di Docker agisce in modo simile alla tec­no­lo­gia di vir­tua­liz­za­zio­ne LXC pre­ce­den­te­men­te svi­lup­pa­ta: entrambi si basano sul kernel Linux ed eseguono la vir­tua­liz­za­zio­ne basata su container. Sia Docker che LXC combinano due obiettivi con­trad­dit­to­ri:

  1. I container in ese­cu­zio­ne con­di­vi­do­no lo stesso kernel Linux, ren­den­do­li più leggeri delle macchine virtuali.
  2. I container in ese­cu­zio­ne sono isolati l’uno dall’altro e hanno accesso solo a una quantità limitata di risorse di sistema.

Per rag­giun­ge­re questi obiettivi, sia Docker che LXC fanno uso dei co­sid­det­ti “kernel namespace” e “gruppi di controllo” (Control Groups). Diamo un’occhiata a come funziona questo processo in dettaglio.

Kernel Linux

Il kernel Linux è il com­po­nen­te centrale del sistema operativo open source GNU/Linux e in quanto tale gestisce l’hardware e controlla i processi. Quando si esegue Docker al di fuori di Linux, è ne­ces­sa­rio ricorrere all’utilizzo di un hy­per­vi­sor o di una macchina virtuale che fornisca la fun­zio­na­li­tà del kernel Linux. Su macOS viene uti­liz­za­to xhyve, un derivato dell’iper­vi­so­re BSD bhyve. Su Windows 10, Docker utilizza l’hy­per­vi­sor Hyper-V.

Kernel namespace

I namespace sono una ca­rat­te­ri­sti­ca del kernel Linux. Essi dividono le risorse del kernel as­si­cu­ran­do così che i processi rimangano separati gli uni dagli altri. Un processo ef­fet­tua­to in un namespace può accedere solo alle risorse del kernel di quello stesso namespace. Di seguito vi mostriamo una pa­no­ra­mi­ca dei namespace uti­liz­za­ti su Docker:

Namespace De­scri­zio­ne Spie­ga­zio­ne
UTS Iden­ti­fi­ca­zio­ne di sistema Assegna ai container i nomi host e di dominio
PID ID di processo Ogni container usa il proprio namespace per gli ID di processo; i PID di altri container non sono ac­ces­si­bi­li; in questo modo, due processi in container diversi possono usare lo stesso PID senza entrare in conflitto.
IPC Co­mu­ni­ca­zio­ne in­ter­pro­ces­sua­le I namespace IPC isolano i processi in un container in modo che non possano co­mu­ni­ca­re con i processi in altri container.
NET Risorse network Assegna risorse di rete separate come indirizzi IP o tabelle di routing a un container
MNT Punti di montaggio del file system Limita il file system dell’host a una sezione stret­ta­men­te definita dal punto di vista del container

Gruppi di controllo

I gruppi di controllo, di solito ab­bre­via­ti come cgroup, sono usati per or­ga­niz­za­re ge­rar­chi­ca­men­te i processi Linux. A un processo (o gruppo di processi) viene assegnata una quantità limitata di risorse di sistema che include RAM, core della CPU, memoria di massa e di­spo­si­ti­vi di rete (virtuali). Mentre i namespace isolano i processi l’uno dall’altro, i gruppi di controllo limitano l’accesso alle risorse di sistema. Questo assicura che l’intero sistema rimanga fun­zio­na­le quando si uti­liz­za­no più container.

Quali sono i vantaggi di Docker?

Diamo uno sguardo alla storia dello sviluppo del software per capire i vantaggi di Docker. Com’è e com’è stato costruito, fornito ed eseguito il software? Quali parti del processo sono cambiate dra­sti­ca­men­te? Il software è la con­tro­par­te dell’hardware, il computer fisico. Senza software, il computer è solo un grumo di materia. Mentre l’hardware è fisso e im­mu­ta­bi­le, il software può essere ricreato e per­so­na­liz­za­to. L’in­te­ra­zio­ne dei due livelli risulta nel me­ra­vi­glio­so mondo digitale.

Software su una macchina fisica

Tra­di­zio­nal­men­te, il software è stato creato per essere eseguito su una macchina fisica. Tuttavia, si possono ri­scon­tra­re alcune dif­fi­col­tà. Il software può essere eseguito solo su un certo hardware e può, ad esempio, ri­chie­de­re un certo pro­ces­so­re.

Inoltre, dei software più complessi di solito non fun­zio­na­no in modo com­ple­ta­men­te autonomo, ma sono integrati in un eco­si­ste­ma software. Questo include un sistema operativo, librerie e di­pen­den­ze. Affinché queste possano in­te­ra­gi­re cor­ret­ta­men­te, bisogna che siano di­spo­ni­bi­li le giuste versioni di tutti i com­po­nen­ti. Esiste anche una con­fi­gu­ra­zio­ne che descrive come i singoli com­po­nen­ti sono collegati tra loro.

Se volete eseguire diverse ap­pli­ca­zio­ni pa­ral­le­la­men­te su una macchina, sorgono ra­pi­da­men­te dei conflitti di versione. Un’ap­pli­ca­zio­ne può ri­chie­de­re una versione di un com­po­nen­te che è in­com­pa­ti­bi­le con un’altra ap­pli­ca­zio­ne. Nel peggiore dei casi, ogni ap­pli­ca­zio­ne dovrebbe essere eseguita su una diversa macchina fisica. Tuttavia, le macchine fisiche sono costose e non possono essere scalate fa­cil­men­te. Quindi molte volte, se i requisiti in termini di risorse di un’ap­pli­ca­zio­ne crescono, è ne­ces­sa­rio optare per una nuova macchina fisica.

Un altro problema deriva dal fatto che il software in sviluppo è usato in diversi ambienti. Uno svi­lup­pa­to­re scrive il codice sul sistema locale e lo testa nello stesso sistema. L’ap­pli­ca­zio­ne passa at­tra­ver­so diverse fasi di test prima di andare in pro­du­zio­ne, incluso un ambiente di test per la garanzia della qualità o un ambiente di staging per essere testato dal team di prodotto.

Inoltre, i diversi ambienti spesso esistono su diverse macchine fisiche. Ciò comporta quasi sempre delle dif­fe­ren­ze nelle versioni del sistema operativo, delle librerie e nella con­fi­gu­ra­zio­ne. Come si può con­ci­lia­re tutto questo? Per giunta, se gli ambienti dif­fe­ri­sco­no l’uno dall’altro, i test perdono il loro valore e se un sistema non funziona cor­ret­ta­men­te deve essere so­sti­tui­to. Come si può garantire una certa coerenza? Af­fron­ta­re questi problemi su macchine fisiche è molto difficile.

Macchine virtuali: un passo nella giusta direzione

I problemi appena citati relativi alle macchine fisiche hanno portato all’aumento della po­po­la­ri­tà delle macchine virtuali (VM). L’idea di base è quella di integrare un livello tra l’hardware e il sistema operativo o il sistema operativo host e i sistemi operativi guest. Una VM di­sac­cop­pia l’ambiente ap­pli­ca­ti­vo dall’hardware sot­to­stan­te. La specifica com­bi­na­zio­ne di sistema operativo, ap­pli­ca­zio­ne, librerie e con­fi­gu­ra­zio­ne può essere ri­pro­dot­ta da un’immagine. Oltre a isolare com­ple­ta­men­te un’ap­pli­ca­zio­ne, questo permette agli svi­lup­pa­to­ri di rag­grup­pa­re diverse ap­pli­ca­zio­ni in un “di­spo­si­ti­vo”.

Le immagini delle VM possono essere spostate tra le macchine fisiche e più sistemi operativi vir­tua­liz­za­ti possono essere eseguiti pa­ral­le­la­men­te. Questo assicura che l’ap­pli­ca­zio­ne sia scalabile. Tuttavia, la vir­tua­liz­za­zio­ne del sistema operativo richiede molte risorse ed è eccessiva per utilizzi semplici.

I vantaggi della vir­tua­liz­za­zio­ne basata su container di Docker

Le immagini usate nella vir­tua­liz­za­zio­ne basata su container non hanno bisogno di un sistema operativo. La vir­tua­liz­za­zio­ne basata su container è più leggera e fornisce quasi lo stesso iso­la­men­to delle VM. Un’immagine container combina il codice dell’ap­pli­ca­zio­ne con tutte le di­pen­den­ze ne­ces­sa­rie e la con­fi­gu­ra­zio­ne giusta. Le immagini sono portabili tra sistemi e i container costruiti su di esse possono essere ri­pro­dot­ti. I container possono essere usati in vari ambienti, come sviluppo, pro­du­zio­ne, test e staging. Il controllo della versione dei livelli e delle immagini fornisce anche una buona dose di mo­du­la­ri­tà.

Di seguito rias­su­mia­mo i vantaggi chiave della vir­tua­liz­za­zio­ne delle ap­pli­ca­zio­ni basata su Docker rispetto all’uso di una VM. Un container Docker:

  • non contiene il proprio sistema operativo e l’hardware simulato;
  • condivide il kernel del sistema operativo con altri container ospitati sullo stesso sistema;
  • è leggero e compatto in termini di utilizzo delle risorse rispetto a un’ap­pli­ca­zio­ne basata su VM;
  • si avvia più ve­lo­ce­men­te di una macchina virtuale;
  • può essere eseguito in più istanze della stessa immagine allo stesso tempo;
  • può essere usato insieme ad altri servizi basati su container tramite or­che­stra­zio­ne;
  • è ideale per lo sviluppo locale.
Vai al menu prin­ci­pa­le