Come Installare WordPress su Docker

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

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.

L'architettura di una Virtual Machine
L’architettura di una Virtual Machine

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.

Diagramma del funzionamento di un container
Diagramma del funzionamento di un container

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.

Container e VM a confronto
Container e VM a confronto

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.

Package Software
Immagine tratta da What is a Container

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)
La scheda Gestione attività di Windows 10
La scheda Gestione attività di Windows 10

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:

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.

L'installer di Docker Toolbox per Mac OS X
L’installer di Docker Toolbox per Mac OS X

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.

Applicazioni istallate da Docker Toolbox su Mac OS X
I folder delle applicazioni istallate da Docker Toolbox su Mac OS X

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.
hello-world
L’output del comando hello-world

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.

php:7.0-cli
Il terminale di un Mac durante il download dell’immagine php:7.0-cli

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.

IP Docker machine
L’indirizzo IP da cui è possibile accedere al container di Docker

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 è obbligatoria
  • MYSQL_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 collegato
  • WORDPRESS_DB_USER: il valore di default è “root”
  • WORDPRESS_DB_PASSWORD: il valore predefinito è la password dell’utente “root” del container mysql collegato
  • WORDPRESS_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 e wordpress 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.

WordPress
La pagina di istallazione di WordPress

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.

MySQL in Kitematic
La scheda delle impostazioni del container MySQL in Kitematic

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.

Kitematic
La scheda delle impostazioni del container WordPress in Kitematic

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.