Docker è un software open-source che automatizza il deployment di applicazioni utilizzando le funzionalità di isolamento delle risorse del kernel Linux per generare un sistema di container che eseguono processi in ambienti isolati. Un container non include un sistema operativo separato, come una Virtual Machine, ma sfrutta l’isolamento delle risorse propria del kernel Linux.
Docker opera, quindi, una virtualizzazione a livello di sistema operativo e non a livello di macchina, come invece avviene per le Vistual Machine. Ma di cosa si tratta precisamente?
Ecco di cosa parleremo in questo articolo:
- Container vs Virtual Machine
- Docker per le aziende e per gli sviluppatori
- Definizioni
- Prerequisiti di Docker
- Istallazione di Docker
- Hello Docker
- I comandi di Docker più utilizzati
- WordPress su Docker
- Installare WordPress con Docker Compose
- Installare WordPress in locale con Kitematic
Container vs Virtual Machine
Spesso si tende a parlare di container come delle Virtual Machine leggere. In effetti i due concetti presentano diversi punti in comune:
- entrambe le soluzioni sono progettate per fornire un ambiente isolato in cui eseguire applicazioni;
- in entrambi i casi l’ambiente viene visto come un artefatto binario che può essere spostato da un host ad un altro.
Tuttavia sussiste una differenza fondamentale sotto il profilo architetturale. Un’analogia significativa che permette una rapida comprensione della differenza tra container e Virtual Machine viene proposta dal team di Docker, che paragona una casa indipendente (Virtual Machine) ad un appartamento (container). Una casa indipendente offre protezione nei confronti di ospiti indesiderati e racchiude in sé tutta l’infrastruttura necessaria al suo funzionamento (gli impianti). Inoltre, tutte le case indipendenti hanno un numero minimo di stanze, sempre presenti indipendentemente dalla tipologia di abitazione.
Un sistema di container funziona diversamente. Esiste un unico edificio che ospita tutti gli appartamenti, dotato di tutta l’infrastruttura necessaria (gli impianti). Inoltre, all’interno dell’edificio esistono appartamenti di ogni dimensione e tipologia (ad es. monolocali, bilocali, suite, studi, ecc.), che possono quindi essere scelti in base alle necessità del momento.
In questa analogia, l’edificio è il server su cui gira il demone di Docker (Docker host); gli appartamenti sono i container che condividono le risorse dell’host.
In un ambiente basato sui container, come nel caso di Docker, gli sviluppatori possono costruire un’immagine che include esclusivamente le risorse necessarie ad eseguire una specifica applicazione, cominciando con le risorse base ed aggiungendo solo le risorse assolutamente necessarie all’esecuzione dell’applicazione.
Una Virtual Machine funziona in modo diametralmente opposto: questa consiste in un sistema operativo completo e, a seconda dell’applicaizione, lo sviluppatore può essere (o non essere) in grado di eliminare i componenti che non ritiene necessari.
Per spostare un’applicazione da una VM è necessario impacchettare in un unico file binario tutte le risorse che sono state utilizzate nello sviluppo. Con la tecnoclogia dei container, invece, l’astrazione avviene a livello di servizio. Ogni servizio rappresenta un container, e un’applicazione comprende una somma di servizi che possono essere destrutturati in componenti più piccoli. L’applicazione non esiste nel container, ma in un volume di Docker condiviso tra N container.
Docker per le aziende e per gli sviluppatori
Ciò che maggiormente caratterizza un sistema di container come quello offerto da Docker è che l’ambiente di esecuzione non è determinante. È possibile eseguire host di Docker su qualsiasi piattaforma di virtualizzazione o cloud (Docker Cloud, Docker Datacenter) grazie a Docker Machine, un tool che rende disponibili gli host di Docker a una grande varietà di piattaforme virtuali, come VMware vSphere, Microsoft Hyper-V, Azure e AWS.
A livello aziendale, dunque, Docker permette di sviluppare, distribuire ed eseguire applicazioni svincolandole dall’infrastruttura, offrendo quindi un mix di agilità, portabilità e controllo.
Ad uno sviluppatore, invece, interesserà maggiormente la capacità di Docker di automatizzare i task ripetitivi che riguardano l’istallazione e la configurazione degli ambienti di sviluppo. Grazie a tale caratteristica, lo sviluppatore può concentrarsi esclusivamente sulla sua attività di programmazione.
Non è più, quindi, necessario istallare e configurare un database, oppure passare tra versioni incompatibili di un dato linguaggio. Sviluppare applicazioni su Docker permette di confinare la complessità dell’ambiente all’interno di container che possono essere creati, condivisi ed eseguiti con estrema semplicità.
Per una descrizione più approfondita disamina di cosa sia Docker e degli usi a cui si presta, si legga qui.
Definizioni
Prima di passare agli aspetti pratici dell’analisi, è necessario precisare alcuni termini che potrebbero non essere familiari a chi si avvicini a Docker per la prima volta.
Docker
Docker è uno strumento software progettato per creare, distribuire ed eseguire applicazioni in un sistema di container.
Immagini
Un’immagine è un pacchetto eseguibile, leggero e indipendente, che include tutte le risorse necessarie all’esecuzione di un’applicazione: codice, runtime, librerie, variabili d’ambiente e file di configurazione.
In questo articolo si utilizzeranno le immagini ufficiali di PHP, MySQL e WordPress. L’elenco completo delle immagini ufficiali è disponibile nella Repository di Docker.
Container
Un container è un’istanza di runtime di un’immagine. Durante l’esecuzione, il container rimane completamente isolato dall’ambiente host, e utilizza i file e le porte dell’ambiente solo se configurato per farlo.
I container eseguono nativamente le applicazioni sul kernel della macchina ospite, e per questo garantiscono migliori performance delle Virtual Machine, che possono accedere alle risorse del sistema ospite attraverso un hypervisor.
Dockerfile
Un Dockerfile è un documento di testo che contiene tutti i comandi che possano essere utilizzati nella commandline per montare un’immagine. Utilizzando il comando docker build, l’utente può creare una build automatica che esegue in successione una serie di istruzioni da linea di comando.
In pratica, il comando docker build genera un’immagine da un Dockerfile ed un contesto. Il contesto è una serie di file collocati in un determinata location PATH o URL. Il PATH è l’indirizzo di una directory sul computer locale, mentre la URL è la location di una repository GIT.
Per maggiori informazioni, si legga la Dockerfile reference.
Prerequisiti di Docker
Docker richiede i seguenti requisiti di sistema:
- Su Mac successivo al 2010 è richiesto OS X El Capitain 10.11 o superiore con supporto per la virtualizzazione MMU (per maggiori informazioni, si legga Install Docker for Mac)
- Sui sistemi Microsoft è richiesto Windows 10 Pro a 64 bit con abilitazione alla virtualizzazione (il riferimento è Install Docker for Windows)
Per i sistemi meno recenti, Docker offre Toolbox, una soluzione legacy per i sistemi che non suportano la virtualizzazione dell’hardware, disponibile sia per Mac OS X che per Windows.
Anche nel caso di Docker Toolbox, tuttavia, vi sono dei requisiti minimi da verificare prima dell’istallazione:
- Sui sistemi OS X viene richiesto Mac OS 10.8 Mountain Lion o superiore (si legga Install Docker Toolbox on macOS)
- Sui sistemi Windows viene richiesto un sistema a 64 bit con Windows 7 o superiore (per i dettagli completi si legga Install Docker Toolbox on Windows)
Istallazione di Docker
Docker è disponibile in due versioni: Community Edition ed Enterprise Edition.
La prima versione è destinata a sviluppatori e piccole imprese che iniziano a sperimentare lo sviluppo e la distribuzione di applicazioni utilizzando la tecnologia dei container.
La seconda versione del software è invece destinata a team ed aziende che sviluppano e distribuiscono prodotti software avanzati. Essendo questo un articolo dedicato agli sviluppatori, si tratterà esclusivamente di Docker Community Edition.
Sia che si tratti di Docker per Windows che di Docker er Mac OS X, allo sviluppatore si presentano due pacchetti di istallazione:
- Stable Channel: è il pacchetto da preferire nel caso in cui si desideri una piattaforma pienamente testata ed affidabile.
- Edge Channel: questo secondo pacchetto offre le ultime funzionalità in via di sperimentazione, ma può rivelare bug e instabilità che ne sconsigliano l’utilizzo in produzione
Entrambi i pacchetti possono essere prelevati all’indirizzo https://docs.docker.com/engine/installation/
Una volta completata l’istallazione, saranno disponibili il Docker Quickstart Terminal, che avvia il client di Docker, e Kitematic, l’applicazione che fornisce l’interfaccia grafica.
Hello Docker
Una volta completata l’istallazione del pacchetto più adatto al proprio ambiente di sviluppo, si può cominciare ad utilizzare Docker sul proprio computer avviando il Docker Quickstart Terminal.
Il seguente comando permetterà di conoscere la versione di Docker istallata:
docker --version
In alternativa, è possibile utilizzare
docker -v
Se tutto funziona correttamente, dovremmo avere in risposta il numero di versione istallata sul nostro sistema, come nell’esempio che segue:
Docker version 17.07.0-ce, build 8784753
Per ottenere l’elenco dei comandi disponibili si dovrà digitare
docker --help
Infine, ci facciamo dare il benvenuto da Docker con hello-world:
docker run hello-world
La risposta ci dirà quali operazioni sono state effettuate durante l’esecuzione del comando:
- Il client di Docker ha contattato il demone di Docker;
- Il demone di Docker ha prelevato l’immagine “hello-world” dall’hub di Docker;
- Il demone di Docker ha creato un nuovo container dall’immagine recuperata: il container esegue l’applicazione che produce il testo che sarà poi visualizzato nel terminale;
- Il demone di Docker invia al client di Docker il testo prodotto; il client, a sua volta, invia il testo al terminale.
I comandi di Docker più utilizzati
Il primo passo che dobbiamo compiere è quello di procurarci delle copie locali delle immagini necessarie allo sviluppo dei nostri progetti. Abbiamo due modi di prelevare le immagini dall’hub di Docker. Il primo è tramite l’istruzione docker pull [image]:[tag], dove image è il nome dell’immagine e tag è l’etichetta che contrassegna la versione che stiamo chiedendo. Ecco un esempio:
docker pull php:7.2.0RC4-cli-stretch
Questo comando scaricherà sulla postazione locale la versione 7.2.0 RC4 di PHP. Possiamo anche scaricare l’ultima versione stabile con docker pull [image], omettendo il tag. Ad esempio:
docker pull php
In questo caso sarà fornita la versione etichettata con il tag latest. L’elenco completo delle versioni di PHP disponibili è all’indirizzo https://hub.docker.com/_/php/.
Dopo aver prelevato le immagini necessarie, possiamo chiederne un resoconto tramite il comando
docker images
Per generare un container da un’immagine si ricorre al comando docker run, come nel seguente esempio:
docker run [image]:[tag]
Oppure
docker run [image]
In questo caso sarà eseguita l’immagine con tag latest. Per avere una lista dei container in esecuzione, il comando da digitare è:
docker ps
Questo comando fornirà i seguenti dati per ognuno dei container in esecuzione:
- CONTAINER ID
- IMAGE
- COMMAND
- CREATED
- STATUS
- PORTS
- NAMES
Se l’immagine richiesta in un comando docker run non è disponibile in locale, prima dell’esecuzione Docker preleverà l’immagine stessa dall’hub. Il comando sarà eseguito solo a download completato.
Soprattutto quando si effettuano le prime prove su Docker, si tende ad avviare ripetutamente gli stessi container. Docker non permette di eseguire due istanze dello stesso container contemporaneamente, quindi sarà necessario utilizzare i seguenti comandi prima di ogni riavvio:
docker stop [container]
docker rm [container]
Il primo comando interrompe l’esecuzione del container, mentre il secondo lo rimuove. In alternativa a questi due comandi, si può forzare la rimozione del container con la seguente istruzione:
docker rm -f [container]
É anche possibile rimuovere tutti i container disponibili, utilizzando l’istruzione
docker rm -f $(docker ps -aq)
La subshell esegue il comando docker ps -aq, il quale restituisce esclusivamente gli ID di tutti i container disponibili.
In caso di errori, può essere opportuno analizzare le operazioni eseguite durante l’esecuzione di un comando, digitando la seguente istruzione:
docker logs [container]
Infine, docker inspect permette di ottenere informazioni specifiche sul container indicato, come l’indirizzo IP e la porta assegnati da Docker:
docker inspect [container]
Un primo esempio: testiamo due diverse versioni di PHP
Abbiamo modi alternativi di far girare un’applicazione PHP su Docker:
- Dalla linea di comando, creando un Dockerfile nel proprio progetto PHP
- Dalla linea di comando, senza un Dockerfile, eseguendo un singolo script PHP
Oppure:
- Con Apache, creando un Dockerfile nel proprio progetto PHP
- Con Apache, senza un Dockerfile, eseguendo un singolo script PHP
Il modo più semplice di eseguire uno script PHP è dalla linea di comando senza Dockerfile. Questa modalità è adatta a piccoli progetti composti da un solo file, che spesso, per la loro semplicità, non richiedono la scrittura di un intero Dockerfile o di un file YAML, di cui parleremo più avanti. Come esempio, supponiamo di voler provare una delle nuove funzionalità di PHP 7.2: l’introduzione del tipo object nelle Argument Type Declarations. Questo lo script index.php:
<?php
class MyClass {
public $var = '';
}
class ChildClass extends MyClass {
public $var = 'My name is Jack';
}
$child = new FirstChild;
function test(object $arg) {
return $arg->var;
}
echo test($child);
Avviamo il client di Docker ed eseguiamo la seguente istruzione dalla linea di comando:
docker run --rm -v $(pwd):/app -w /app php:7.0-cli php index.php
La prima volta che viene eseguito questo comando, Docker scarica l’immagine php:7.0-cli sulla postazione locale, decomprime i pacchetti ed esegue il comando.
Ora analizziamo la richiesta:
docker run
esegue un comando in un nuovo container;--rm
indica che il container sarà rimosso al termine dell’esecuzione;-v $(pwd):/app
dispone che venga montato un volume: il comando si compone del flag-v
e di tre campi separati dal carattere due punti. In questo caso, stiamo agganciando il volume della directory corrente del terminale ($(pwd)
) alla directory/app
del nuovo container (per maggiori informazioni sul montaggio dei volumi si legga la documentazione);-w /app
definisce la directory del container dove docker cercherà i file dell’applicazione;php index.php
è l’istruzione da eseguire.
In questo caso la risposta di Docker sarà, ovviamente, un messaggio di errore, in quanto PHP 7.0 non supporta la keyword object che abbiamo usato nello script.
Possiamo testare lo stesso script con PHP 7.2, creando un nuovo container dall’immagine php:7.2.0RC4-cli-stretch:
docker run --rm -v $(pwd):/app -w /app php:7.2.0RC4-cli-stretch php index.php
Di nuovo, alla prima esecuzione del comando, l’immagine richiesta sarà scaricata sulla postazione locale. Concluso il download e lo spacchettamento, Docker mostrerà nel terminale l’output dello script, che in questo caso sarà eseguito correttamente.
In questo primo esempio, abbiamo eseguito lo script all’interno del terminale. Utilizziamo una versione di PHP dotata di Apache per visualizzare l’output in una finestra del browser, sempre da CLI e senza il ricorso ad un Dockerfile:
docker run -d -p 80:80 --name my-apache-php-app -v "$PWD":/var/www/html php:7.2.0RC4-apache
Analizziamo questa nuova richiesta:
docker run
esegue un comando in un nuovo container;-d
esegue un container in background e stampa a video l’ID del container stesso;-p 80:80
pubblica la porta (o le porte) di un container verso l’host;--name my-apache-php-app
assegna il nome al container;-v "$PWD":/var/www/html
dispone che venga montato un volume e specifica i due PATH (locale e del container)php:7.2.0RC4-apache
individua l’immagine con il relativo tag.
Ora non resta che recuperare l’IP della Docker Machine:
docker-machine ip
La risposta sarà l’indirizzo IP da digitare nella barra degli indirizzi del browser.
WordPress su Docker
L’immagine ufficiale di WordPress richiede la preventiva istallazione dell’immagine di MySQL. Per scaricarla dalla repository di Docker basterà eseguire il seguente comando:
docker pull mysql
Quando creeremo i nostri container MySQL, avremo a disposizioni svariate viariabili d’ambiente che ne permetteranno la configurazione. Tra queste variabili ricordiamo le seguenti:
MYSQL_ROOT_PASSWORD
: questa variabile è obbligatoriaMYSQL_DATABASE
MYSQL_USER
MYSQL_PASSWORD
Dopo MySQL, istalliamo l’immagine di WordPress:
docker pull wordpress
La configurazione di un’istallazione di WordPress avviene attraverso l’assegnazione dei valori di diverse variabili d’ambiente, tra cui le seguenti:
WORDPRESS_DB_HOST
: il valore di default è costituito dell’IP e dalla porta container mysql collegatoWORDPRESS_DB_USER
: di default è “root”WORDPRESS_DB_PASSWORD
: il valore predefinito è la password dell’utente “root” del container mysql collegatoWORDPRESS_DB_NAME
: valore predefinito “wordpress”WORDPRESS_TABLE_PREFIX
: valore predefinito “”: da utilizzare solo se si desidera sovrascrivere il valore della variabile$table_prefix
del file wp-config.php
Non ci soffermiamo ulteriormente sulle variabili d’ambiente, rinviando alla lettura della documentazione di Docker e del Codex di WordPress per ogni approfondimento.
Ora possiamo creare il container del progetto con le seguenti istruzioni:
docker run --name wordpressdb -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=wordpress -d mysql:5.7
docker run -d --name wordpress -e WORDPRESS_DB_USER=root -e WORDPRESS_DB_PASSWORD=root --link wordpressdb:mysql -p 8080:80 -v "$PWD/":/var/www/html wordpress
La prima istruzione avvia il container wordpressdb dall’immagine MySQL; la seconda istruzione avvia il container wordpress. Naturalmente, i valori delle impostazioni dell’esempio andranno sostituiti con i valori corrispondenti alla propria configurazione.
Va evidenziato quanto sia poco pratico eseguire WordPress da linea di comando, sebbene possa essere importante comprendere, durante la fase di familiarizzazione con Docker, il valore di ognuna delle opzioni utilizzate. Quando, però, si diventa padroni dello strumento, conviene sfruttare le diverse soluzioni disponibili per eseguire WordPress in pochi istanti. Gli strumenti a nostra disposizione sono Docker Compose e Kitematic.
Installare WordPress con Docker Compose
Gli esempi visti in precedenza prevedono l’invio di comandi tramite l’interfaccia del terminale. Seppur corretta, questa modalità operativa presenta limiti evidenti nella gestione dei container. Una soluzione professionale ed efficiente, e tuttavia semplice da realizzare, viene offerta da Docker Compose, un tool già incluso nei pacchetti di istallazione di Docker sia per Windows che per Mac, Toolbox compreso.
Compose viene definito come un strumento per la definizione e l’esecuzione di applicazioni Docker multi-container, e il suo utilizzo si configura come un processo a tre fasi:
- Definizione dell’ambiente dell’app con un Dockerfile.
- Definizione dei servizi necessari all’app all’interno di un file docker-compose.yml, in modo che questi possano essere eseguiti in un ambiente isolato.
- Esecuzione del comando docker-compose up.
Nei progetti semplici, il Dockerfile non è sempre necessario, in quanto le opzioni specificate al suo interno possono essere inserite in un file docker-compose.yml. Quest’ultimo è un file YAML che definisce servizi, network e volumi per un’applicazione Docker. Il percorso predefinito è la root del progetto (./docker-compose.yml) e l’estensione può essere indifferentemente .yml o .yaml.
Quello che segue è il file docker-compose.yml di cui facciamo uso nel prossimo esempio:
version: '3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "80:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
volumes:
db_data:
Vediamo nel dettaglio la funzione di ognuno dei tag utilizzati:
version
stabilisce la versione del formato del file: al momento è raccomandata la versione 3 (si legga http://dockr.ly/2yAU8GY);services
definisce i servizi e imposta i dati di configurazione che saranno applicati ad ogni container, in modo molto simile alla configurazione attraverso il comando docker run;db
ewordpress
sono i nomi assegnati ai due container;image
stabilisce il nome dell’immagine corrispondente;volumes
stabilisce i volumi attraverso i quali è possibile rendere persistenti i dati: il volume può essere specificato da un percorso o dal nome di un volume già nominato;restart
riavvia il container a seconda delle impostazioni (in questo caso sempre);environment
assegna valori alle variabili d’ambiente dell’immagine;depends_on
stabilisce una dipendenza tra servizi;ports
espone le porte: possono essere specificate entrambe le porte dell’host e del container (mapping), come in questo esempio, oppure la sola porta del container. Si tenga presente che eventuali problemi di accesso ai container potrebbero essere dovuti ad un’erronea configurazione delle porte (maggiori informazioni all’indirizzo http://dockr.ly/2gyqRWg).
Una volta completato il file docker-compose.yml, questo va salvato nella root locale del progetto. Riavviamo il demone di Docker, collochiamoci dal terminale nella stessa directory e digitiamo il comando:
docker-compose up
Il terminale mostrerà le operazioni effettuate durante l’avvio dell’applicazione. Al termine delle operazioni di avvio, si potrà digitare nella barra del browser l’indirizzo IP della docker-machine.
A questo punto siamo liberi di sviluppare le nostre webapp o i nostri siti nella sicurezza che Docker terrà in memoria tutte le nostre modifiche. Terminato il lavoro su WordPress, si potrà interrompere l’esecuzione di Docker Compose in qualsiasi momento, digitando nel terminale Ctrl+c. Il terminale risponderà con le seguenti informazioni:
^CGracefully stopping... (press Ctrl+C again to force)
Killing wpproject_wordpress_1 ... done
Killing wpproject_db_1 ... done
Nota: un’analisi più dettagliata dei Dockerfile l’ho fornita su HTML.it.
Installare WordPress in locale con Kitematic
Kitematic è l’interfaccia grafica di Docker, distribuita assieme a Docker Toolbox, per semplificare e velocizzare l’utilizzo di Docker su Windows e Mac OS X.
Tramite l’interfaccia di Kitematic è possibile navigare tra le numerose immagini ufficiali presenti nella repository. È possibile, quindi, istallare le immagini e avviare i container con pochi click.
La scheda My Images propone l’elenco delle immagini disponibili nella propria istallazione di Docker. Per mettere all’opera WordPress, basterà avviare e configurare il container MySQL, e quindi il container WordPress.
L’immagine mostra la scheda delle impostazioni del container MySQL, dove sono stati inseriti nomi e valori delle variabili d’ambiente.
Seguendo la stessa logica, creiamo un nuovo container dall’immagine di WordPress e andiamo ad aggiungere nomi e valori alle variabili d’ambiente.
A questo punto non resta che scegliere il proprio tool preferito e cominciare a sviluppare applicazioni su Docker.
Nota: ho analizzato più dettagliatamente l’installazione con Kitematic su HTML.it.
Photo by frank mckenna on Unsplash
Lascia un commento