· 9 min read
Un environnement de développement Symfony 5 avec Docker et Docker-compose
Je ne sais pas vous, mais installer une base de données MySQL, un phpMyAdmin ou encore un Apache avec PHP, même avec des solutions packagées (WAMP, MAMP ou autres joyeusetés de ce genre) n'est pas ma plus grande passion ! Et pas envie de me prendre la tête à tout ré-installer si je change de machine !
Je ne sais pas vous, mais installer une base de données MySQL, un phpMyAdmin ou encore un Apache avec PHP, même avec des solutions packagées (WAMP, MAMP ou autres joyeusetés de ce genre) n’est pas ma plus grande passion ! Et pas envie de me prendre la tête à tout ré-installer si je change de machine !
Comme beaucoup d’entre vous j’utilise Docker pour mes environnements de développements, et depuis plusieurs mois également sur des applications en productions. Cela me facilite grandement le quotidien, uniformise les environnements sur les postes des développeurs, et réduit fortement le “chez moi ça marche” !
Bref, développer avec Docker c’est le pied. Enfin, c’est le pied une fois que l’on a réussi à faire fonctionner ses projets dans ce nouveau paradigme. Cet article et cette vidéo ont pour objectif de vous proposer une solution pour mettre en place un environnement complet pour vos projets de développements avec Symfony 5 ou Symfony 4 (ou même tout autres type de projets PHP).
Prérequis
J’écris cet article depuis un environnement Linux, mais transposable facilement sur Mac OS ou (un peu plus difficilement) sur Windows.
- Docker : https://www.docker.com/get-started
- Docker-compose: https://docs.docker.com/compose/install/
- C’est tout ;-)
Création du fichier docker-compose.yml
Je crée un fichier docker-compose.yml dans un répertoire qui contiendra mon environnement.
$ mkdir docker_symfony
$ cd docker_symfony
$ touch docker-compose.yml
Pour savoir dans quel format je dois écrire le docker-compose.yml, je vérifie la version de Docker disponible sur mon poste. Dans cas, je suis en version 19.03.12 lors de l’écriture de l’article.
$ docker -v
Docker version 19.03.12, build 48a66213fe
Dans la documentation de Docker le format du docker-compose.yml qui correspond à ma version de Docker (c’est rétro-compatible) est la version 3.8. Je débute donc l’écriture du docker-compose.yml en spécifiant le numéro de version.
version: "3.8"
services:
Conteneur MySQL
Il est temps désormais de spécifier nos “services” au sens docker-compose du terme. Commençons par le plus simple, la base de donnée MySQL.
version: "3.8"
services:
db:
image: mysql
container_name: db_docker_symfony
restart: always
volumes:
- db-data:/var/lib/mysql
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
networks:
- dev
Vous aurez noté l’utilisation de l’option MYSQL_ALLOW_EMPTY_PASSWORD: ‘yes’, qui nous permet d’indiquer que nous autorisons les comptes sans mot de passe (Nous ne somme pas en production !). Cela vous simplifiera l’utilisation avec le compte root lorsque vous customiserez vos fichiers .env de Symfony.
Pour que la configuration ci-dessus fonctionne, il faut ajouter à la fin du docker-compose.yml les références au “networks” et mettre en place un volume pour le stockage des fichiers de la base de données.
networks:
dev:
volumes:
db-data:
Conteneur phpMyAdmin
Qui dit base de donnée MySQL dit phpMyAdmin non ? Au moins en environnement de développement.
phpmyadmin:
image: phpmyadmin
container_name: phpmyadmin_docker_symfony
restart: always
depends_on:
- db
ports:
- 8080:80
environment:
PMA_HOST: db
networks:
- dev
Pas de surprise à cette étape, vous noterez juste l’utilisation de “depends_on” qui permet au conteneur phpMyAdmin d’attendre le conteneur MySQL avant de démarrer.
Comme pour le conteneur MySQL, nous utilisons l’option “restart: always” qui permet d’indiquer au deamon Docker de redémarrer ce conteneur en cas d’arrêt de celui-ci.
Conteneur Maildev
Pour de très nombreux projets j’ai besoin d’envoyer des mails, alors je vous propose d’inclure la solution maildev à votre environnement Docker. En gros maildev va intercepter vos mails (il joue le rôle d’un serveur SMTP) et vous les présenter dans une petite interface graphique. Indispensable !
maildev:
image: maildev/maildev
container_name: maildev_docker_symfony
command: bin/maildev --web 80 --smtp 25 --hide-extensions STARTTLS
ports:
- "8081:80"
restart: always
networks:
- dev
L’utilisation de la commande “bin/maildev —web 80 —smtp 25 —hide-extensions STARTTLS” permet d’éviter des messages d’erreur plus tard avec Symfony, ne l’oubliez pas ;-)
Conteneur Apache et Php
Attaquons-nous au plus gros morceau, le conteneur Apache/Php. Pour cette partie allons construire nous même notre image à l’aide d’un Dockerfile. Commençons pas créer un fichier Dockerfile dans un sous-répertoire “php”.
$ mkdir php
$ cd php
$ touch Dockerfile
Nous n’allons pas partir de zéro, et allons simplement compléter une image déjà existante de php 7.4 avec Apache. Pour cela nous débutons l’écriture de notre Dockerfile en spécifiant l’image depuis laquelle on débute.
FROM php:7.4-apache
Nous y ajoutons ensuite un certain nombre de librairies et d’outil (composer notamment). C’est dans ce fichier qu’il faudra intervenir si vous devez ajouter des libraires ou des extensions php (j’ai mis quelques exemples dans ce fichier).
J’utilise cette liste d’extensions docker-php pour m’aider (il y a peut-être des listes plus récente, mais celle donne une bonne base).
FROM php:7.4-apache
RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
RUN apt-get update \
&& apt-get install -y --no-install-recommends locales apt-utils git libicu-dev g++ libpng-dev libxml2-dev libzip-dev libonig-dev libxslt-dev;
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && \
echo "fr_FR.UTF-8 UTF-8" >> /etc/locale.gen && \
locale-gen
RUN curl -sSk https://getcomposer.org/installer | php -- --disable-tls && \
mv composer.phar /usr/local/bin/composer
RUN docker-php-ext-configure intl
RUN docker-php-ext-install pdo pdo_mysql gd opcache intl zip calendar dom mbstring zip gd xsl
RUN pecl install apcu && docker-php-ext-enable apcu
WORKDIR /var/www/
Une fois cette étape de création du Dockerfile, il ne nous reste qu’à l’ajouter au docker-compose. Créons doncunservice “www” dans notre docker-compose.yml.
www:
build: php
container_name: www_docker_symfony
ports:
- "8741:80"
volumes:
- ./php/vhosts:/etc/apache2/sites-enabled
- ./:/var/www
restart: always
networks:
- dev
Les plus observateurs aurons noté que nous n’utilisons plus le mot clé “image”, mais “build” dans ce service. Vous l’aurez compris, cele permet au docker-compose de comprend que pourceservice, il doit utiliser un build d’un Dockerfile.
Pour info, il est possible de lancer le build avec la commande suivante, (si vous ne le faites, cette opération sera réalisée au premier lancement de votre docker-compose).
$ docker-compose build
Vous aurez également noté, que dans les volumes nous mappons un répertoire “/php/vhosts” dans le conteneur. Ce fichier n’existe pas, il est temps de s’en occuper. L’objectif de laisser ce fichier en dehors du conteneur et de pouvoir facilement le modifier sans avoir à re-builder l’image à chaque modification.
$ mkdir vhosts
$ cd vhosts
$ touch vhosts.conf
Pour le contenu de ce fichier, nous nous baserons (quelques toutes petites modifications visibles ci-dessous) sur la version proposer dans la documentation de Symfony.
<VirtualHost *:80>
ServerName localhost
DocumentRoot /var/www/project/public
DirectoryIndex /index.php
<Directory /var/www/project/public>
AllowOverride None
Order Allow,Deny
Allow from All
FallbackResource /index.php
</Directory>
# uncomment the following lines if you install assets as symlinks
# or run into problems when compiling LESS/Sass/CoffeeScript assets
# <Directory /var/www/project>
# Options FollowSymlinks
# </Directory>
# optionally disable the fallback resource for the asset directories
# which will allow Apache to return a 404 error when files are
# not found instead of passing the request to Symfony
<Directory /var/www/project/public/bundles>
FallbackResource disabled
</Directory>
ErrorLog /var/log/apache2/project_error.log
CustomLog /var/log/apache2/project_access.log combined
# optionally set the value of the environment variables used in the application
#SetEnv APP_ENV prod
#SetEnv APP_SECRET <app-secret-id>
#SetEnv DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name"
</VirtualHost>
On teste le bon fonctionnement ?
À ce stade vous devriez avoir envie de savoir comment on va se servir de cette configuration. Commençons par lancer la stack docker-compose. Le 1er lancement va être bien plus long que les autres car docker-compose va faire le build de votre image “php”.
$ docker-compose up-d
Starting db_docker_symfony ... done
Starting www_docker_symfony ... done
Starting maildev_docker_symfony ... done
Starting phpmyadmin_docker_symfony ... done
Lançons maintenant la création d’un nouveau projet Symfony (et changeons le propriétaire du fichier par l’utilisateur courant pour pouvoir les modifier facilement par la suite).
$ docker exec www_docker_symfony composer create-project symfony/website-skeleton project
$ sudo chown -R $USER ./
Une fois l’installation terminée, en vous rendant à l’adresse http://127.0.0.1:8741/, vous devriez voir la page standard d’un nouveau projet Symfony ! Félicitation, mais c’est pas encore terminé ne vous emballez pas ;-).
Histoire de poursuivre notre test jusqu’au bout, testons l’utilisation de la base de données depuis le projet Symfony. Pour cela modifions le fichier .env pour y ajouter la référence à la base de données de notre conteneur MySQL.
DATABASE_URL=mysql://root:@db_docker_symfony:3306/db_name?serverVersion=5.7
Créons la base de donnée depuis la CLI de Symfony. Pour nous simplifier les prochaines commandes, nous allons entrer dans le shell du conteneur “www”.
$ docker exec -it www_docker_symfony bash
/var/www# cd project
/var/www/project# php bin/console doctrine:database:create
Created database \`db_name\` for connection named default
Mettons en place une entité “Test” avec un champ “test” de type string (et toutes les valeurs par défaut proposés par le Maker Bundle de Symfony), créons la migration associée puis exécutons la migration.
(Rappel : les commandes sont exécutées dans le shell du conteneur “www”)
/var/www/project# php bin/console make:entity
/var/www/project# php bin/console make:migration
/var/www/project# php bin/console doctrine:migrations:migrate
Nous pouvons vérifier dans phpMyAdmin que la création est effective à l’adresse http://127.0.0.1:8080/.
Bon, on arrive à la fin de ce bien trop long article, il ne nous reste plus qu’a testé l’envoie de mails depuis Symfony.
Commençons pas indiquer l’adresse de notre serveur SMTP maildev dans le .env.
MAILER_DSN=smtp://maildev_docker_symfony:25
Créons un contrôleur “MailController” en charge de l’envoie d’un mail de test avec le Maker Bundle de Symfony. (Toujours dans le shell du conteneur “www”).
/var/www/project# php bin/console make:controller
Modifions le fichier /src/MailController.php nouvellement créé pour lui ajouté l’envoie d’un mail de test à chaque chargement de la page.
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\Annotation\Route;
class MailController extends AbstractController
{
/**
* @Route("/mail", name="mail")
*/
public function index(MailerInterface $mailer): Response
{
$email = (new Email())
->from('hello@example.com')
->to('you@example.com')
->subject('Test de MailDev')
->text('Ceci est un mail de test');
$mailer->send($email);
return $this->render('mail/index.html.twig', [
'controller_name' => 'MailController',
]);
}
}
Chargeons la page http://127.0.0.1:8741/mail plusieurs fois d’affilée et vérifions la bonne réception des mails dans maildev à l’adresse http://127.0.0.1:8081/.
Conclusions et dépôt GitLab
J’espère que cet article et cette vidéo vous ont permis de mettre en place un environnement de développement Symfony avec Docker. Cette approche n’est certainement pas la seule qui existe, elle a le mérite de fonctionner simplement et rapidement.
L’idée, plutôt que de vous fournir une solution prête à l’emploie, était d’expliquer chaque étape de la construction de notre stack de développement avec Docker, cela vous permettra de la faire évoluer selon vos besoins.
Vous trouverez l’ensemble des fichiers (à l’exception du projet de démonstration Symfony) sur ce dépôt GitLab.