Mentre guardi video su Netflix o ascolti musica su Spotify, ti sei mi chiesto quanti altri nel mondo stiano facendo la stessa cosa in quel preciso istante? Sarebbe un pensiero stimolante non solo da utente ma anche da programmatore! Ti farebbe riflettere sulla mole di dati che le applicazioni gestiscono, sull’intensità dei loro flussi real-time e sul carico di lavoro che schizza alle stelle nei momenti di picco. Ciò, al giorno d’oggi, significa una sola cosa: la progettazione di sistemi software ha bisogno di flessibilità e scalabilità.
In pratica, ha bisogno di tutto ciò per cui le tecnologie a container sono nate. In questa guida parliamo proprio di questo e soprattutto di Docker, la soluzione leader in questo ambito.
Nei decenni che hanno reso lo sviluppo software un bisogno impellente per la società – parliamo degli anni Ottanta e Novanta – si sono affermate due pratiche assolutamente necessarie:
La programmazione orientata agli oggetti permetteva di organizzare al meglio la rete di informazioni gestita dall’applicazione. In maniera, potremmo dire, piramidale i dati costituivano oggetti, questi venivano collegati in strutture dinamiche le quali a loro volta entravano a far parte di aggregazioni più grandi. Ciò permetteva di inglobare in maniera organizzata funzionalità che via via venivano richieste. Un approccio di questo tipo, oltre a tutti i risultati che l’Ingegneria del Software aveva portato, offriva la possibilità di realizzare grandi piattaforme di lavoro che con l’avvento di Internet potevano essere raggiunte attraverso la Rete offrendo servizi ovunque.
Da un punto di vista progettuale però la struttura a monolite regnava. Si finiva per creare software a corpo unico che accedevano all’esterno, per lo più, solo per attingere a fonti dati quali database, file, archivi di vario genere ma per il resto tendevano a conservare al proprio interno ogni funzionalità.
Il monolite venne messo in crisi dai tempi e dalle necessità che la crescente informatizzazione e diffusione della connettività portavano. Tra i principali svantaggi possiamo individuarne alcuni fortemente accentuati dalle dimensioni crescenti del software:
Insomma, in generale, i problemi delle applicazioni monolitiche sono ravvisabili nelle loro dimensioni e unicità che impediscono la gestione autonoma delle singole componenti.
Tutto ciò trova soluzioni efficacissime nei container e nelle architetture a microservizi.
Un container è un ambiente che crea un contesto completo per l’esecuzione di un’applicazione. Può essere considerato una forma di virtualizzazione, ma a differenza delle macchine virtuali non ha bisogno dell’installazione di un sistema operativo, occupa il minimo spazio indispensabile e non richiede l’allocazione fissa di risorse come la RAM.
Un container è basato su un’immagine che rappresenta tutto il suo contenuto e definisce quale applicativo sarà eseguito al suo interno. L’immagine, una volta chiusa, continua a mantenere la sua composizione fissa e da essa potranno essere animati molteplici container: una buona progettazione delle immagini è al centro di qualsiasi strategia di containerizzazione.
Facciamo qualche esempio. Ti serve usare dei database MySQL, ma non hai voglia di installare il servizio sul computer? Con una tecnologia a container puoi predisporre o scaricare un’immagine con all’interno tale software ed eseguirla in un container. Vuoi provare un nuovo linguaggio? Sperimentare Node.js, Go, Rust o qualsiasi altro? Idem: scarichi un’immagine e la usi. E così via per qualsiasi altro ambiente ti possa interessare.
Allo stesso modo, quando avrai finito di completare la tua applicazione potrai predisporre un’immagine in cui definirai ciò che le occorre per funzionare bene e le risorse di cui avrà bisogno per poi impacchettare il tutto. Da quel momento in poi potrai avviare quanti container vuoi con la tua applicazione in esecuzione.
Questo è il funzionamento in generale ma ora andiamo a vedere come questi concetti vengano applicati in Docker.
Docker (https://www.docker.com/) è un prodotto open source, realizzato in linguaggio Go, disponibile in due versioni principali:
Noi ci riferiremo da ora a Docker CE, il più utilizzato dagli sviluppatori per varie ragioni: primo, perché gratuito; secondo, perché piattaforma ricca e completissima per soluzioni professionali anche di livello notevole; terzo, perché contiene elementi fondamentali presenti nella versione EE ed in ogni altra piattaforma basata su Docker.
Docker è disponibile in ogni piattaforma. Per Windows e MacOS possiamo installare Docker DesktopDocker Desktop seppur ponendo attenzione alle caratteristiche hardware e di sistema operativo che richiede.
Per Linux, ambiente nativo e adattissimo a Docker, si può procedere con l’installazione del Docker Engine, il motore fondamentale sia mediante pacchetti precompilati disponibili nei repository sia attraverso scaricamento del prodotto.
Una volta installato Docker si può iniziare con la creazione di container partendo da una delle tante immagini già esistenti e disponibili nel principale servizio on-line dedicato alla loro distribuzione: Docker HubDocker Hub.
Il primo strumento per l’invio di comandi a Docker è il suo tool da riga di comando. E’ necessario prenderci confidenza sin da subito e Docker Hub mette a disposizione un modo velocissimo per farlo: l’immagine hello-world (https://hub.docker.com/_/hello-world) che già dal nome presuppone il suo ruolo di “esempio zero” di questa tecnologia.
Basta aprire l’interfaccia da riga di comando del proprio sistema (terminale, Prompt dei comandi, shell o in qualsiasi modo si chiami nella macchina che stiamo usando) e digitare:
$ docker run hello-world
Il risultato sarà una serie di righe contenenti, tra l’altro, un saluto di benvenuto, alcune indicazioni di massima nonché indicazioni per ulteriori prove: questa è la dimostrazione che Docker è stato installato correttamente.
Alla prima riga di output, probabilmente Docker riporterà il messaggio “Unable to find image ‘hello-world:latest’ locally” la quale suona un po’ come un errore ma in realtà sta dicendo semplicemente che, visto che non possediamo ancora in locale l’immagine hello-world con tag “latest”, Docker l’ha dovuta scaricare da Docker Hub per poterla eseguire.
Il comando “docker” visto in precedenza è il client da riga di comando di cui dicevamo tramite il quale potremo veicolare tutte le direttive per la gestione delle immagini e dei container. Viene seguito in genere da una parola chiave che specifica un’azione – in questo caso run ma ne esistono molte altre –, dal nome dell’oggetto sui cu si applica (qui l’immagine hello-world) e da una serie di opzioni a seconda dei casi.
Altro esempio super-rapido è l’avvio di un’immagine che contenga un sistema di programmazione. Proviamo a farci avviare Node.js senza averlo installato nel sistema:
$ docker run -it node
Viene in questo modo scaricata ed eseguita l’immagine node, ufficiale per Node.js, e lanciata in modalità interattiva ovvero in ascolto per ricevere i comandi dell’utente.
Avremo così una console in cui poter digitare direttive Javascript:
$ docker run -it node > console.log(‘Ciao Node.js!’) Ciao Node.js! > a = 5 5 > b = 6 6 > somma=a+b 11 > .exit
Tutte le righe che iniziano con il simbolo > compongono la sessione di lavoro in Node.js all’interno del container e, al termine, con .exit abbiamo chiuso l’interazione con questo ambiente: ciò pone anche fine all’esecuzione del container stesso riportandoci alla console del sistema operativo.
I container nascono per lavorare insieme e collaborare tra loro. L’abbandono del monolite porta inesorabilmente alla creazione di architetture a microservizi dove ogni porzione del software viene sostituito da un container che la interpreta. Con un approccio simile, l’applicazione diventa la composizione di una moltitudine di nodi che dialogano tra loro portando al superamento di tutti i limiti che il monolite presentava:
Il primo step per rendere un’applicazione multi-container sarà imparare la sintassi di Docker Compose, un tool che con un semplice file in formato YAML sarà in grado avviare insieme e secondo determinate politiche una serie di servizi interpretati da vari container.
Per installazioni di dimensioni molto estese, si passa poi all’orchestrazione dove più nodi Docker mettono a disposizione i propri container per creare reti sempre più articolate di componenti applicative. Le principali soluzioni di orchestrazione sono Swarm, nativo e sempre disponibile in Docker, e Kubernetes, ideato da Google e affermatosi con forza in ogni scenario. Nomi di questo genere sono ricorrenti in tutte le piattaforme Cloud che offrono – ormai è doveroso – la possibilità di realizzare applicazioni distribuite su più container.
Docker ormai è un passaggio obbligato nelle situazioni applicative ed è uno strumento che può arricchire molto il bagaglio culturale di un programmatore. E’ un mondo a parte, ricco di concetti particolari, ma non bisogna farsi spaventare perché è perfettamente abbordabile, interessantissimo, gratuito e si rende utile sin dai primi momenti di utilizzo. Una volta conosciutolo, non si installerà mai più un database, un linguaggio o un tool solo per provarlo: si cercherà un’immagine su Docker Hub e la si avvierà. Rotto il ghiaccio poi, poche righe di Docker Compose permetteranno di avviare ambienti di lavoro più articolati composti da più elementi. Si avranno così conoscenze importanti, acquisite in pochissimo tempo: basterà qualche ora di studio dei nostri videocorsi.