Negli ultimi dieci anni, la soluzione di vir­tua­liz­za­zio­ne Docker ha alterato dra­sti­ca­men­te il modo in cui un software è costruito, di­stri­bui­to e gestito. A dif­fe­ren­za dei suoi pre­de­ces­so­ri, le macchine virtuali (VM), Docker vir­tua­liz­za le singole ap­pli­ca­zio­ni. Quindi, un container Docker è un container di ap­pli­ca­zio­ni o software.

Il termine “container software” si basa sui container fisici, come quelli usati sulle navi. Nella logistica, i container come unità stan­dar­diz­za­te sono ciò che ha reso possibile le moderne catene di vendita al dettaglio. Un container può essere tra­spor­ta­to su qualsiasi nave, camion o treno pro­get­ta­to per questo scopo. Ciò in buona parte funziona in­di­pen­den­te­men­te dal contenuto del container. All’esterno, il container è dotato di in­ter­fac­ce stan­dar­diz­za­te. Nel caso dei container Docker accade qualcosa di molto simile.

Registra il tuo dominio
  • Domain Connect gratuito per una con­fi­gu­ra­zio­ne facile del DNS
  • Cer­ti­fi­ca­to SSL Wildcard gratuito
  • Pro­te­zio­ne privacy inclusa

Cos’è un container Docker?

Cos’è esat­ta­men­te un container Docker? Sentiamo cosa hanno da dire gli svi­lup­pa­to­ri Docker in merito:

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.“ – Fonte: https://www.docker.com/why-docker

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

A dif­fe­ren­za di un container fisico, un container Docker esiste in un ambiente virtuale. Un container fisico è as­sem­bla­to sulla base di una specifica stan­dar­diz­za­ta. Per i container virtuali il processo di creazione è molto simile. Un container Docker è infatti creato da un modello im­mu­ta­bi­le chiamato “immagine”. Un’immagine Docker contiene le di­pen­den­ze e le im­po­sta­zio­ni di con­fi­gu­ra­zio­ne ne­ces­sa­rie per creare un container.

Proprio come molti container fisici possono derivare da una singola specifica, un qualsiasi numero di container Docker può essere creato da una singola immagine. I container Docker co­sti­tui­sco­no quindi la base per i servizi scalabili e gli ambienti ri­pro­du­ci­bi­li per lo sviluppo di ap­pli­ca­zio­ni. Possiamo creare un container da un’immagine e anche salvare un container esistente in una nuova immagine. È inoltre possibile eseguire, mettere in pausa e fermare i processi all’interno di un container.

A dif­fe­ren­za della vir­tua­liz­za­zio­ne basata su macchina virtuale (VM), un container Docker non contiene il proprio sistema operativo (OS, “Operating System”) ma tutti i container in ese­cu­zio­ne su un host Docker accedono allo stesso kernel OS. Quando Docker viene di­stri­bui­to su un host Linux, viene uti­liz­za­to il kernel Linux esistente. Se il software Docker viene eseguito su un sistema non-Linux, viene uti­liz­za­ta un’immagine minima del sistema Linux tramite un hy­per­vi­sor o una macchina virtuale.

A ogni container viene assegnata una certa quantità di risorse di sistema al momento dell’ese­cu­zio­ne la quale include RAM, core della CPU, memoria di massa e di­spo­si­ti­vi di rete (virtuali). Tec­ni­ca­men­te, i “cgroups” (ab­bre­via­zio­ne di “control groups”) limitano l’accesso di un container Docker alle risorse di sistema. I “kernel na­me­spa­ces” sono uti­liz­za­ti per dividere le risorse del kernel e di­stin­gue­re i processi l’uno dall’altro.

Ester­na­men­te, i container Docker co­mu­ni­ca­no tramite la rete. Per fare ciò, dei servizi specifici si occupano della scansione delle porte esposte. Questi sono di solito dei server web o database. I container stessi sono con­trol­la­ti sul ri­spet­ti­vo host Docker tramite l’API Docker e possono essere avviati, fermati e rimossi. Il client Docker fornisce un’in­ter­fac­cia a riga di comando (CLI) con i comandi ap­pro­pria­ti per ognuna di queste ope­ra­zio­ni.

Qual è la dif­fe­ren­za tra i container e le immagini Docker?

I termini “container Docker” e “immagine Docker” causano spesso con­fu­sio­ne. Questo non sorprende dato che è un po’ come il paradosso dell’uovo e della gallina: un container viene creato da un’immagine; tuttavia, un container può anche essere salvato come una nuova immagine. Diamo un’occhiata alle dif­fe­ren­ze tra i due concetti in dettaglio.

Un’immagine Docker è un modello inerte. L’immagine occupa solo uno spazio minimo su un disco rigido e non fa altro. Al contrario, il container Docker è un’istanza “vivente”. Un container Docker in ese­cu­zio­ne ha un com­por­ta­men­to, in­te­ra­gi­sce con l’ambiente. Inoltre, un container ha uno stato che cambia nel tempo, uti­liz­zan­do una quantità variabile di RAM.

Pro­ba­bil­men­te conoscete già i concetti di “classe” e “oggetto” della pro­gram­ma­zio­ne orientata agli oggetti (OOP). La relazione tra un container Docker e un’immagine Docker è simile alla relazione tra un oggetto e la sua classe associata. Una classe esiste solo una volta e può creare molti oggetti simili. La classe stessa viene caricata a partire da un file di codice sorgente. Nell’universo Docker viene adoperato un pro­ce­di­men­to affine. Un modello viene creato da un’unità sorgente, un “Doc­ker­fi­le”, che a sua volta crea molte istanze:

  Codice sorgente Modello Istanza
Concetto Docker Doc­ker­fi­le Immagine Docker Container Docker
Analogia nella pro­gram­ma­zio­ne Codice sorgente di classe loaded class oggetto istan­zia­to
Consiglio

In questo articolo ci riferiamo al container Docker come a una “istanza in ese­cu­zio­ne” dell’immagine associata. I termini “istanza” e “istan­zia­re” sono astratti. Se ri­scon­tra­te dif­fi­col­tà nel com­pren­der­li, vi con­si­glia­mo di usare un piccolo trucco mnemonico. So­sti­tui­te “istan­zia­re” con “ri­ta­glia­re” nella vostra mente. Anche se non vi è una relazione diretta tra le parole, esiste una forte cor­ri­spon­den­za tra i loro si­gni­fi­ca­ti in termini in­for­ma­ti­ci. Pensate al principio in questo modo: proprio come si usa una formina per ri­ta­glia­re molti biscotti simili da un impasto, si istan­zia­no molti oggetti simili da uno stesso modello. Quindi, istan­zia­re sta a indicare il pro­ce­di­men­to at­tra­ver­so il quale un modello crea un oggetto.

Come viene costruito un container Docker?

Per capire come viene costruito un container Docker, può aiutare fare ri­fe­ri­men­to alla me­to­do­lo­gia “twelve-factor app”. Questa è una raccolta di dodici principi fon­da­men­ta­li per la co­stru­zio­ne e il fun­zio­na­men­to di un software orientato ai servizi. Sia Docker che l’app a dodici fattori risalgono al 2011. L’app a dodici fattori aiuta gli svi­lup­pa­to­ri a pro­get­ta­re ap­pli­ca­zio­ni Software as a Service (Saas) secondo standard specifici. Questi includono:

  • Usare formati di­chia­ra­ti­vi per l’au­to­ma­zio­ne della con­fi­gu­ra­zio­ne per mi­ni­miz­za­re tempi e costi per i nuovi svi­lup­pa­to­ri che si uniscono al progetto;
  • Avere un contratto pulito con il sistema operativo sot­to­stan­te, offrendo la massima por­ta­bi­li­tà tra gli ambienti di ese­cu­zio­ne;
  • Essere adatto alla di­stri­bu­zio­ne sulle piat­ta­for­me cloud moderne, ovviando alla necessità di server e am­mi­ni­stra­zio­ne dei sistemi;
  • Mi­ni­miz­za­re la di­ver­gen­za tra sviluppo e pro­du­zio­ne, per­met­ten­do il de­ploy­ment continuo per la massima agilità;
  • Essere in grado di scalare senza cam­bia­men­ti si­gni­fi­ca­ti­vi agli strumenti, all’ar­chi­tet­tu­ra o alle pratiche di sviluppo.

La struttura di un container Docker si basa su questi stessi principi. Inoltre, un container Docker include i seguenti com­po­nen­ti, che esa­mi­ne­re­mo in dettaglio di seguito:

  1. Sistema operativo container e union file system
  2. Com­po­nen­ti software e con­fi­gu­ra­zio­ne
  3. Variabili d’ambiente e con­fi­gu­ra­zio­ne runtime
  4. Porte e volumi
  5. Processi e registri

Sistema operativo e union file system

A dif­fe­ren­za di una macchina virtuale, un container Docker non contiene il proprio sistema operativo. Invece, tutti i container in ese­cu­zio­ne su un host Docker accedono a un kernel Linux condiviso. Nel container è incluso solo uno strato di ese­cu­zio­ne minimo. Questo di solito include un’im­ple­men­ta­zio­ne della libreria standard C e una shell Linux per l’ese­cu­zio­ne dei processi. Di seguito vi mostriamo una pa­no­ra­mi­ca dei com­po­nen­ti dell’immagine ufficiale “Alpine Linux”:

Kernel Linux Libreria standard C Comandi Unix
Dall’host Musl libc BusyBox

Un’immagine Docker consiste in una pila di livelli di file system di sola lettura. Un livello descrive le modifiche al file system nel livello sot­to­stan­te. Usando uno speciale union file system come overlay2, i livelli sono so­vrap­po­sti e unificati in un’in­ter­fac­cia coerente. Quando si crea un container Docker da un’immagine viene aggiunto un livello protetto da scrittura a un altro livello de­scri­vi­bi­le. Tutte le modifiche fatte al file system vengono in­cor­po­ra­te nel livello de­scri­vi­bi­le usando la strategia “copy-on-write”.

Com­po­nen­ti software e di con­fi­gu­ra­zio­ne

A partire dal sistema operativo minimo del container, i com­po­nen­ti software ag­giun­ti­vi vengono in­stal­la­ti su un container Docker. Questo è di solito seguito da ulteriori passaggi di im­po­sta­zio­ne e con­fi­gu­ra­zio­ne. Per l’in­stal­la­zio­ne vengono uti­liz­za­ti i metodi standard:

  • tramite un gestore di pacchetti di sistema come apt, apk, yum, brew, ecc.;
  • tramite un gestore di pacchetti per linguaggi di pro­gram­ma­zio­ne come pip, npm, composer, gem, cargo, ecc.;
  • com­pi­lan­do nel container at­tra­ver­so make, mvn, ecc.

Di seguito vi mostriamo alcuni esempi di com­po­nen­ti software co­mu­ne­men­te usati nei container Docker:

Area d’ap­pli­ca­zio­ne Com­po­nen­ti software
Linguaggi di pro­gram­ma­zio­ne PHP, Python, Ruby, Java, Ja­va­Script
Strumenti per lo sviluppo node/npm, React, Laravel
Sistemi di database MySQL, Postgres, MongoDB, Redis
Server web Apache, nginx, lighttpd
Cache e proxy Varnish, Squid
Sistemi di gestione del contenuto WordPress, Magento, Ruby on Rails

Variabili d’ambiente e con­fi­gu­ra­zio­ni di runtime

Seguendo la me­to­do­lo­gia dell’app a dodici fattori, la con­fi­gu­ra­zio­ne del container Docker è me­mo­riz­za­ta in variabili d’ambiente, chiamate “Env-Vars”. Qui, il termine “con­fi­gu­ra­zio­ne” viene inteso come tutti i valori che cambiano tra i diversi ambienti, quali lo sviluppo o il sistema di pro­du­zio­ne. Questo spesso include nomi host e cre­den­zia­li di database.

I valori delle variabili d’ambiente in­fluen­za­no il com­por­ta­men­to del container. Si usano due metodi prin­ci­pa­li per rendere di­spo­ni­bi­li le variabili d’ambiente all’interno di un container:

1. De­fi­ni­zio­ne nel Doc­ker­fi­le

La di­chia­ra­zio­ne ENV dichiara una variabile d’ambiente nel Doc­ker­fi­le. È possibile assegnare anche un valore pre­de­fi­ni­to opzionale che entra in vigore se la variabile d’ambiente è vuota all’avvio del container.

2. Tra­sfe­ri­men­to all’avvio del container

Per accedere a una variabile d’ambiente nel container che non è stata di­chia­ra­ta nel Doc­ker­fi­le, è possibile tra­sfe­ri­re la variabile all’avvio del container, il che funziona per singole variabili tramite parametri della riga di comando. Inoltre, si può tra­sfe­ri­re un “file env”, che definisce diverse variabili d’ambiente insieme ai loro valori.

Di seguito vi mostriamo come tra­sfe­ri­re una variabile d’ambiente all’avvio del container:

docker run --env <env-var> <image-id></image-id></env-var>

È utile tra­sfe­ri­re un file env per molte variabili d’ambiente:

docker run --env-file /path/to/.env <image-id></image-id>
N.B.

Il comando “docker inspect” può essere uti­liz­za­to per vi­sua­liz­za­re le variabili d’ambiente presenti nel container insieme ai loro valori. Pertanto, è ne­ces­sa­rio fare at­ten­zio­ne quando si uti­liz­za­no dei dati riservati nelle variabili d’ambiente.

Quando si avvia un container da un’immagine, possono essere tra­sfe­ri­ti dei parametri di con­fi­gu­ra­zio­ne. Questi includono la quantità di risorse di sistema allocate che al­tri­men­ti è il­li­mi­ta­ta. Inoltre, i parametri di avvio sono uti­liz­za­ti per definire porte e volumi per il container. Ne di­scu­te­re­mo più a fondo nel prossimo paragrafo. I parametri di avvio possono so­vra­scri­ve­re qualsiasi valore pre­de­fi­ni­to nel Doc­ker­fi­le. Di seguito vi mostriamo alcuni esempi.

Per allocare un core della CPU e 10 megabyte di RAM nel container Docker all’avvio bisognerà inserire il seguente codice:

docker run --cpus="1" --memory="10m" <image-id></image-id>

Per aprire le porte definite nel Doc­ker­fi­le all’avvio del container si dovrà procedere come segue:

docker run -P <image-id></image-id>

Per mappare la porta TCP 80 dell’host Docker sulla porta 80 del container Docker bisognerà inserire quanto segue:

docker run -p 80:80/tcp <image-id></image-id>

Porte e volumi

Un container Docker contiene un’ap­pli­ca­zio­ne isolata dal mondo esterno. Per di­mo­stra­re la sua utilità, deve essere possibile l’in­te­ra­zio­ne con l’ambiente. Pertanto, esistono dei modi per scambiare dati tra host e container, così come tra più container. Le in­ter­fac­ce stan­dar­diz­za­te per­met­to­no ai container di essere usati in diversi ambienti.

La co­mu­ni­ca­zio­ne dall’esterno con i processi in ese­cu­zio­ne nel container avviene su porte di rete esposte. Questa utilizza i pro­to­col­li standard TCP e UDP. Ad esempio, im­ma­gi­nia­mo un container Docker che contiene un server web. Questo scansiona la porta TCP 8080. Il Doc­ker­fi­le dell’immagine Docker contiene anche la riga 'EXPOSE 8080/tcp'. Bisognerà avviare il container con 'docker run -P' e accedere al server web su 'http://localhost:8080'.

Le porte sono usate per co­mu­ni­ca­re con i servizi in ese­cu­zio­ne nel container. Tuttavia, in molti casi, per scambiare dati può essere utile usare un file condiviso tra il container e il sistema host. Questo è il motivo per cui Docker riconosce diversi tipi di volumi:

  • Volume de­no­mi­na­to - rac­co­man­da­to
  • Volumi anonimi - vengono persi quando il container viene rimosso
  • Supporti bind- storici e non rac­co­man­da­ti; per­for­man­ti
  • Supporti Tmpfs - situati nella RAM; solo su Linux

Le dif­fe­ren­ze tra i tipi di volume sono sottili e la scelta del tipo più adatto dipende molto dall’uso che se ne fa. Una de­scri­zio­ne det­ta­glia­ta di tali dif­fe­ren­ze, tuttavia, andrebbe oltre lo scopo di questo articolo.

Processi e registri

Un container Docker di solito incapsula un’ap­pli­ca­zio­ne o un servizio. Il software eseguito all’interno del container forma un insieme di processi in ese­cu­zio­ne. I processi in un container Docker sono isolati dai processi in altri container o dal sistema host. I processi possono essere avviati, fermati ed elencati all’interno di un container Docker con­trol­la­to tramite la riga di comando o tramite l’API Docker.

I processi in ese­cu­zio­ne emettono con­ti­nua­men­te delle in­for­ma­zio­ni di stato. Seguendo la me­to­do­lo­gia dell’app a dodici fattori, vengono uti­liz­za­ti dei flussi di dati standard STDOUT e STDERR per l’output. L’output su questi due flussi di dati può essere letto con il comando “docker logs”. Anche Il co­sid­det­to “logging driver” può essere usato in questo caso. Il driver di re­gi­stra­zio­ne pre­de­fi­ni­to scriverà i log in formato JSON.

Come e dove si usano i container Docker?

Al giorno d’oggi Docker è usato in tutte le parti del ciclo vitale del software. Questo include sviluppo, test e fun­zio­na­men­to. I container in ese­cu­zio­ne su un host Docker sono con­trol­la­ti tramite l’API Docker. Il client Docker accetta i comandi dalla riga di comando mentre degli speciali strumenti di or­che­stra­zio­ne vengono usati per con­trol­la­re i cluster di container Docker.

Lo schema di base per di­stri­bui­re i container Docker ha orien­ta­ti­va­men­te la seguente struttura:

  1. L’host Docker scarica l’immagine Docker dal registro.
  2. Il container Docker viene creato e avviato dall’immagine.
  3. L’ap­pli­ca­zio­ne nel container viene eseguita finché il container non viene fermato o rimosso.

Di seguito mostriamo due esempi di di­stri­bu­zio­ne di container Docker.

Di­stri­bui­re i container Docker nell’ambiente di sviluppo locale

L’uso dei container Docker è par­ti­co­lar­men­te popolare nello sviluppo di software. Di solito, un software viene svi­lup­pa­to da un team di spe­cia­li­sti. Per questo scopo viene uti­liz­za­to un insieme di strumenti di sviluppo noto come una catena di strumenti. Ogni strumento è in una versione specifica e l’intera catena funziona solo se le versioni sono com­pa­ti­bi­li tra loro. Inoltre, gli strumenti devono essere con­fi­gu­ra­ti cor­ret­ta­men­te.

Per as­si­cu­rar­si che l’ambiente di sviluppo sia coerente, gli svi­lup­pa­to­ri usano Docker. Un’immagine Docker viene creata una volta sola e contiene l’intera toolchain cor­ret­ta­men­te con­fi­gu­ra­ta. Ogni svi­lup­pa­to­re del team estrae l’immagine Docker dalla propria macchina locale e avvia un container da essa. Lo sviluppo avviene quindi all’interno del container e se vi è un cam­bia­men­to nella toolchain l’immagine viene ag­gior­na­ta cen­tral­men­te.

Di­stri­bu­zio­ne di container Docker in cluster or­che­stra­ti

I data center dei fornitori di hosting e dei fornitori di Platform as a Service (PaaS) usano cluster di container Docker. Ogni servizio (load balancer, server web, server di database, ecc.) viene eseguito nel proprio container Docker. Allo stesso tempo, un singolo container può gestire solo un certo carico. Un software di or­che­stra­zio­ne monitora i container, il loro carico e il loro stato. L’or­che­stra­to­re avvia ulteriori container quando il carico aumenta. Questo approccio permette ai servizi di scalare ra­pi­da­men­te in risposta al cam­bia­men­to delle con­di­zio­ni.

Vantaggi e svantaggi della vir­tua­liz­za­zio­ne basata su container di Docker

I vantaggi della vir­tua­liz­za­zio­ne con Docker possono essere visti in par­ti­co­la­re se messi a confronto con l’uso di macchine virtuali (VM). I container Docker sono molto più leggeri delle VM, possono essere avviati più ve­lo­ce­men­te e consumano meno risorse. Le immagini alla base dei container Docker sono anche più piccole di diversi ordini di grandezza. Infatti, mentre le immagini delle macchine virtuali sono di solito ab­ba­stan­za grandi, da centinaia di MB a qualche GB, le immagini Docker partono da pochi MB.

Tuttavia, la vir­tua­liz­za­zio­ne basata su container di Docker presenta anche alcuni svantaggi. Poiché un container non contiene il proprio sistema operativo, l’iso­la­men­to dei processi in ese­cu­zio­ne non è del tutto perfetta. L’uso di molti container comporta un alto grado di com­ples­si­tà. Inoltre, Docker è un sistema evoluto e al giorno d’oggi la piat­ta­for­ma Docker ha troppe funzioni. Gli svi­lup­pa­to­ri stanno quindi lavorando duramente per dividere i singoli com­po­nen­ti.

Vai al menu prin­ci­pa­le