La Containerization (che in italiano dovrebbe corrispondere alla parola bruttina Containerizzazione) è la tecnologia che negli ultimi anni è sicuramente al centro dell’attenzione ed è entrata a far parte del bagaglio tecnico di qualsiasi developer, perché permette di sviluppare meglio e più in fretta. La sua architettura molto pulita ci permette di sfruttare al meglio le risorse (hardware) scomponendo un’applicazione in servizi più piccoli chiamati images, immagini.
In questo mini-tutorial, analizzeremo un modello di file docker-compose che ci permetterà di settare l’ambiente della nostra applicazione e lanciare tutti i comandi necessari per completare il setup, con un approccio semplice ed elegante.
Come da titolo, vedremo come fare il setup per un ambiente perfetto per un’applicazione PHP servita da Apache, con il supporto del classico database MySQL e senza dimenticare un tool importante come phpMyAdmin.
Con Docker, un classico ambiente configurato in questo modo è assai gestibile e facile da rendere subito operativo. Assomiglia sì al classico environment LAMP, ma con una portabilità ed una semplicità di gestione molto maggiori. Ognuno dei servizi girerà come un microservice, in questo modo potremmo creare un solo file YML (il docker-compose.yml
) che creerà i vari servizi, che resteranno isolati fra di loro.
Il vantaggio principale dei Containers, è la scalabilità che essi offrono, sia per quanto riguarda un eventuale flusso CI/CD sia per la condivisione delle immagini tra tutto il team di sviluppo. Basterà condividere il file docker-compose.yml inserendolo nel repository Git, così ciascuno potrà far girare un ambiente identico per sviluppare e testare l’applicazione, indipendentemente dal sistema operativo sul quale si lavora.
In questa guida vedremo:
- Creare un container locale con PHP+Apache
- Testarlo con un semplice script PHP
- Creare un database MySQL e testarlo con PHP
Utilizzeremo l’archivio pubblico delle immagini, il Docker hub, per il nostro container.
Prerequisiti
- Assicuriamoci di aver installato l’applicazione Docker sul nostro computer
- Conoscenza basilare di PHP e un minimo di SQL
- Conoscenza basilare di Docker e di come funziona un container
- Conoscenza base dei comandi linux e dei comandi docker-compose
Installiamo e lanciamo Docker
Dopo aver scaricato ed installato Docker, lanciamo l’applicazione desktop per assicurarci che l’engine sia in esecuzione.
Per saperne di più, apriamo il terminale sul nostro computer ed eseguiamo il comando:
docker version
Avremo una risposta simile a questa:
Luca-Murante:~ luca$ docker version Client: Cloud integration: v1.0.22 Version: 20.10.11 API version: 1.41 Go version: go1.16.10 Git commit: dea9396 Built: Thu Nov 18 00:36:09 2021 OS/Arch: darwin/amd64 Context: default Experimental: true Server: Docker Engine - Community Engine: Version: 20.10.11 API version: 1.41 (minimum version 1.12) Go version: go1.16.9 Git commit: 847da18 Built: Thu Nov 18 00:35:39 2021 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.4.12 GitCommit: 7b11cfaabd73bb80907dd23182b9347b4245eb5d runc: Version: 1.0.2 GitCommit: v1.0.2-0-g52b36a2 docker-init: Version: 0.19.0 GitCommit: de40ad0
Proviamo, a questo punto, a visualizzare i container attivi, naturalmente l’elenco sarà vuoto:
Il file docker-compose.yml
Questo file ci permetterà di avere una configurazione stabilita nel nostro progetto in modo pragmatico. Ci permetterà, nello specifico di configurare i servizi e di replicare il nostro container all’infinito, basterà appunto includere il file docker-compose.yml
nel progetto. Nel nostro caso, utilizzeremo immagini ufficiali provenienti dal Docker hub, come PHP Apache e MySQL.
Creiamo quindi nel nostro progetto un file chiamato docker-compose.yml
.
Per prima cosa, indicheremo che versione di Docker compose intenderemo utilizzare. Al momento della scrittura di questa guida, l’ultima versione è la 3.8. Iniziamo poi a definire i vari servizi che useremo e, passo passo, aggiungeremo alcune direttive per configurarli.
version: '3.8'
services:
php-apache-environment:
container_name:
Setup di PHP e Apache
Per il setup del nostro container PHP + Apache, impostiamo alcune direttive “base”:
• Il nome del container: è soltanto il nome che ci servirà per alcune operazioni, non indicandolo Docker assegnerà un nome casuale. Utilizzeremo php-apache
• L’immagine di partenza di quel servizio: utilizzeremo una delle immagini ufficiali presenti sul repository Docker Hub. In questo caso, il nostro servizio utilizzerà php:8.0-apache
• Il volume: questa direttiva imposta la corrispondenza tra la nostra working directory e la directory nel container corrispondente a quella di Apache
Nel nostro caso:
volumes:
- ./php/src:/var/www/html/
• Le porte sulle quali sarà disponibile il servizio:
ports:
- 8000:80
In questo caso significa che il servizio risponde alla porta 80 all’interno del container, ma espone verso di noi la porta 8000. È questa la porta che visiteremo dal nostro browser, es. localhost:8000
Ecco una prima versione del nostro docker-compose.yml
:
version: '3.8'
services:
php-apache-environment:
container_name: php-apache
image: php:8.0-apache
volumes:
- ./php/src:/var/www/html/
ports:
- 8000:80
Testiamolo subito: lanciamo il comando docker-compose up. Docker scaricherà automaticamente le immagini dal repository e lancerà i container del nostro file.
Aprendo Docker desktop, vedremo i container up and running:
Come vi anticipavo, adesso possiamo puntare il browser a http://localhost:8000
Sì, è un errore, ma significa che php-apache sta girando!
Facciamo girare un po’ di PHP
Nel nostro file docker-compose.yml
abbiamo indicato come volume questo:
volumes:
- ./php/src:/var/www/html/
Significa cioè che i file del nostro progetto dovranno essere nella sottodirectory php/src
.
Creiamo un file index.php:
<?php
echo "Hello there, this is a PHP Apache container";
?>
Aggiorniamo la pagina:
Ci siamo! Abbiamo containerizzato il nostro progetto PHP.
Setup del container MySQL
A questo punto aggiungiamo al nostro progetto il servizio MySQL, in modo da poterci garantire l’utilizzo di un database con PHP.
Sempre all’interno del nostro file docker-compose.yml
, aggiungiamo una nuova voce db. Impostiamo inoltre l’autenticazione (username, password, password di root) per il servizio.
Aggiungiamo la direttiva restart: always
in modo da far ripartire il servizio ogniqualvolta facciamo un cambiamento nel file.
db:
container_name: db
image: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: MYSQL_ROOT_PASSWORD
MYSQL_DATABASE: MY_DATABASE
MYSQL_USER: MYSQL_USER
MYSQL_PASSWORD: MYSQL_PASSWORD
ports:
- "9906:3306"
Creiamo un Dockerfile
Docker può creare automaticamente le immagini partendo da un Dockerfile
. Un Dockerfile
è un semplice file di testo che contiene dei comandi, proprio come quelli che daremmo noi da terminale, che servono ad “assemblare” un’immagine.
Nel nostro caso, ci servirà per esempio che l’interprete PHP “contenuto” nell’immagine php:8.0-apache specificata nel nostro docker-compose.yml
abbia la capacità di connettersi a mysql: per questo motivo dobbiamo installare alcune librerie. Proprio come se fossimo in una macchina virtuale, possiamo farlo specificando alcuni comandi nel nostro Dockerfile
. Per prima cosa, quindi, andiamo a creare un file vuoto chiamato appunto Dockerfile
(senza estensione). Aggiungiamo:
FROM php:8.0-apache
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
RUN apt-get update && apt-get upgrade -y
A questo punto, introduciamo una nuova direttiva nel nostro docker-compose.yml
, ovvero da depends_on
:. Questa direttiva dice ad ogni servizio che dipende da un altro, sempre presente nel file, e che quindi va lanciato in giusta sequenza.
Ecco, a questo punto, come dovrebbe essere il nostro docker-compose.yml
:
version: '3.8'
services:
php-apache-environment:
container_name: php-apache
build:
context: ./php
dockerfile: Dockerfile
depends_on:
- db
volumes:
- ./php/src:/var/www/html/
ports:
- 8000:80
db:
container_name: db
image: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: MYSQL_ROOT_PASSWORD
MYSQL_DATABASE: MYSQL_DATABASE
MYSQL_USER: MYSQL_USER
MYSQL_PASSWORD: MYSQL_PASSWORD
ports:
- "9906:3306"
Lanciamo il comando docker-compose up per fare il pull delle immagini e lanciare i container. Adesso dovremmo vedere anche il servizio MySQL attivo nel nostro Docker Desktop:
Scriviamo un po’ di PHP
A questo punto, modifichiamo il nostro file index.php per testare la connessione al servizio MySQL conteinerizzato che abbiamo appena lanciato:
<?php
// index.php
// Il nome del servizio definito in >docker-compose.yml.
$host = 'db';
$user = 'MYSQL_USER';
$pass = 'MYSQL_PASSWORD';
$conn = new mysqli($host, $user, $pass);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
} else {
echo "Connected to MySQL server successfully!";
}
?>
Salviamo e refreshiamo la pagina http://localhost:8000
nel browser:
Fatto, adesso che PHP Apache e MySQL sono up e funzionano siamo pronti per sviluppare.
Installiamo phpMyAdmin
Adesso che la connessione a MySQL è fatta, potrebbe essere molto (ma molto) utile avere phpMyAdmin per gestire il database. Anche per lui c’è un’immagine già pronta da utilizzare, dovremmo soltanto configurarla un minimo e sarà pronta.
Aggiungiamo al nostro docker-compose.yml
una nuova sezione:
phpmyadmin:
image: phpmyadmin/phpmyadmin
ports:
- '8080:80'
restart: always
environment:
PMA_HOST: db
depends_on:
- db
Come vedi abbiamo specificato di voler utilizzare l’immagine phpmyadmin/phpmyadmin
, di mappare la porta alla 8080 e di utilizzare il servizio MySQL specificato in PMA_HOST: db
Eseguiamo di nuovo docker-compose up e per fare il pull e l’esecuzione di questo nuovo container.
Puntiamo il browser verso http://localhost:8080
e dovremmo vedere la pagina di login di phpMyAdmin.
Come loggarci? Con nome utente e password impostate nel docker-compose.yml
nella sezione db. (MYSQL_USER
e MYSQL_PASSWORD
)
Conclusioni
Con questo mini tutorial abbiamo visto come in poche decine di minuti possiamo containerizzare il nostro progetto PHP/Apache e MySQL. Questo è solo un punto di partenza, perché Docker ci permetterà di ottenere grandi benefici, pensiamo ad esempio ad aggiungere un servizio come Redis al nostro progetto o semplicemente alla sua portabilità: includiamo docker-compose.yml
e Dockerfile
nel nostro repository git e saremo in grado di riprendere lo sviluppo del nostro progetto ovunque ci troviamo.