Contao & Docker

Contao & Docker

Docker est un outil incroyable pour le monde du développement. Grâce à sa logique de conteneur, formant des environnements spécifiques en un fichier de configuration, il permet de simuler des infrastructures de production sur nos postes de développement, ce qu’on peut apparenter à une machine virtuelle.

Grâce à ce système, on met en place des tests, unitaires ou/et d’intégration, qui ont pour objectif de valider toutes les fonctionnalités que l’on développe, avec de nombreuses contraintes et sur des configurations différentes. On s’assure ainsi que rien ne casse lors de la livraison !

Dans cet article, nous allons voir comment mettre en place une image Docker pour Contao et quelques cas à la marge !

Nous ne ferons pas un tutoriel sur l’installation de Docker sur votre poste de travail, il y a tellement de possibilités que résumer ça nécessite un article dédié, que l’on fera peut-être un jour. En attendant, il existe de nombreuses documentations, dont celle officielle de Microsoft, ainsi que celle de Docker, pour configurer le WSL et Docker sur son Windows 10/11.

Lexique

Autant pour vous que pour moi, je vais faire un point sur les termes employés par Docker, que l’on utilise dans cet article.

On parle d’abord d’image Docker. Il s’agit vulgairement de la configuration d’un conteneur. Lorsque vous exécutez une image Docker, l’outil créé un conteneur à partir des instructions données par l’image. C’est dans cette image que vous indiquez également tout ce dont vous avez besoin pour travailler. (image = ISO/disque d’installation de Windows avec possibilité d’installer/configurer des logiciels/composants en même temps)

Puis, vous avez le conteneur. Il s’agit de l’image en mouvement, en quelque sorte. Vous pouvez lire ce qu’il s’y passe et accéder à vos applications que vous développez. Lorsque vous interrompez un conteneur, il est généralement détruit. À part les éléments mentionnés lors de la configuration, tout sera recréé lors de sa nouvelle exécution. (conteneur = votre PC sur lequel vous avez installé votre Windows)

Mise en place

Pour mettre en place notre environnement, nous avons créé 3 fichiers :

  • Celui qui contient les variables d’environnements : .env
  • Celui qui contient la configuration de l’image : docker-compose.yml
  • Celui qui contient les instructions de création : DockerFile

Commençons par le commencement, le fichier .env !

Variables d’environnement

Dans ce fichier, vous allez mettre toutes les variables propres à votre projet. Vous pouvez en enlever et en rajouter à votre convenance, c’est globalement ce qu’on cherche à modifier une fois qu’on a fait les deux autres fichiers.

La raison de ce fichier, c’est que réécrire des variables dans des fichiers de configuration peut occasionner des erreurs de syntaxe bêtes. Si vous n’avez qu’un endroit où modifier ce qui change, cela réduit considérablement le risque d’erreurs.

# Project name
COMPOSE_PROJECT_NAME=syncrobot
COMPOSE_PROJECT_NAME_SHORT=sc
PRJ_NAME='syncrobot'
PHP_VERSION='8.2'
CONTAO_VERSION='4.13'
APP_SECRET='YOUR APP SECRET' # MODIFY THIS

# Database settings 
# MODIFY THIS
DB_HOST='my db host'
DB_DATABASE='my db'
DB_USER='my db user'
DB_PASSWORD='my db password'
DB_ROOT_PASSWORD='my root db password'
DATABASE_URL="mysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:3306/${DB_DATABASE}" #Yes, 3306, because inside docker network, the internal ports are useable, not the exposed ones

# Directories settings
WORKDIR_BASE='/var/www/html'
WORKDIR_INIT="${WORKDIR_BASE}/init"
WORKDIR_CONTAO="${WORKDIR_BASE}/${PRJ_NAME}"
WORKDIR_BUNDLE_END_SLASH="${WORKDIR_BUNDLE}/"

# Contao settings
BASE_URL=your.base.url
BUNDLE_VERSION="^1.0" # can be changed for any composer type version such as ^1.0
Variable Description
COMPOSER_PROJECT_NAME Nom du projet
COMPOSER_PROJECT_NAME_SHORT Version courte du nom du projet (utile pour des préfixes)
PRJ_NAME Nom du projet bis, peut être différent de celui ci-dessus
PHP_VERSION Version de PHP souhaitée
CONTAO_VERSION Version de Contao souhaitée
APP_SECRET Clé secrète utilisée par Contao
DB_HOST Hôte de la base de données
DB_DATABASE Nom de la base de données
DB_USER Utilisateur de la base de données
DB_PASSWORD Mot de passe de l’utilisateur ci-dessus
DB_ROOT_PASSWORD Mot de passe Root de la base de données
DATABASE_URL URL de connexion de la base de données
WORKDIR_BASE Répertoire de travail dans l’image
WORKDIR_INIT Répertoire d’initialisation dans l’image
WORKDIR_CONTAO Répertoire de Contao
BASE_URL URL où le projet sera accessible (pensez à le rendre accessible dans le fichier hosts de Windows si c’est en local)
BUNDLE_VERSION Version du bundle à importer

docker-composer.yml

Ce fichier sert à configurer les différentes images que nous allons associer dans notre environnement.

Les images sont disponibles sur le site Docker Hub, c’est sur ce site que se trouvent toutes les configurations des images disponibles.

Les particularités de notre fichier, c’est le stockage des fichiers Contao, dans l’image synchrobot-php, avec les volumes. Notez également le choix du port 8888, pour éviter le port 80, parfois utilisé en local par d’autres logiciels.

Niveau base de données, on utilise aussi les volumes pour sauvegarder notre base, et éviter qu’elle soit recréée à chaque démarrage de notre conteneur.

Le reste est assez classique, on utilise nos différentes variables d’environnement comme configurations, et on associe nos deux éléments dans le network synchrobot-db pour que les conteneurs puissent communiquer entre eux.

version: '3.8'
services:
  syncrobot-php:
    build:
      context: web
      args:
        WORKDIR_CONTAO: ${WORKDIR_CONTAO}
        PRJ_NAME: ${PRJ_NAME}
        CONTAO_VERSION: ${CONTAO_VERSION}
        PHP_VERSION: ${PHP_VERSION}
        APP_SECRET: ${APP_SECRET}
        BASE_URL: ${BASE_URL}
        BUNDLE_VERSION: ${BUNDLE_VERSION}
    environment:
      PRJ_NAME: ${PRJ_NAME}
      WORKDIR_CONTAO: ${WORKDIR_CONTAO}
      HTTP_HOST: ${BASE_URL}
    container_name: ${COMPOSE_PROJECT_NAME_SHORT}_php_base
    volumes:
      - ./files:${WORKDIR_CONTAO}/files
    networks:
      - syncrobot-db
    ports:
      - 8888:80


  syncrobot-db:
    image: mysql:5.7
    ports:
      - 3306:3306
    command: --default-authentication-plugin=mysql_native_password
    restart: always    
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} #mandatory
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
    container_name: ${COMPOSE_PROJECT_NAME_SHORT}_db_base
    volumes:
      - ./database:/var/lib/mysql
    networks:
      - syncrobot-db

networks:
  syncrobot-db:

Notez que comme la base de données est persistante, la création des utilisateurs MySQL ne se fera qu’à la création ! Donc si vous faites des modifications dans votre configuration, cela ne sera pris en compte qu’à la recréation de la base de données. Heureusement, il existe des astuces pour ça, comme passer par le fichier DockerFile directement, même si c’est moins pratique.

DockerFile

Ce fichier est le script qui se lance une fois le conteneur lancé et la configuration appliquée. Voilà notre fichier avec les détails de chaque ligne en commentaire.

# Récupération des variables d'environnement
ARG PHP_VERSION
FROM php:${PHP_VERSION}-apache
ARG CONTAO_VERSION
ARG WORKDIR_CONTAO
ARG APP_SECRET
ARG BASE_URL
ARG BUNDLE_VERSION

# On installe tout ce dont Contao a besoin
RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
        libicu-dev \
        zip \
        unzip \
        libzip-dev \
        libxml2-dev \
        libgmp-dev \
        re2c \
        libmhash-dev \
		file \
		git \
		iputils-ping \
		cron \
	&& docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-configure intl \
    && docker-php-ext-install intl \
    && docker-php-ext-configure pdo \
    && docker-php-ext-install pdo \
    && docker-php-ext-configure pdo_mysql \
    && docker-php-ext-install pdo_mysql\
    && docker-php-ext-configure gmp \
    && docker-php-ext-install gmp \
    && docker-php-ext-configure zip \
    && docker-php-ext-install zip

# On installe Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# On récupère une clef SSH qui a accès à notre compte Github, pour pouvoir travailler sur nos repos
ADD bot_github_prod /ssh/github_key
ADD bot_github_prod.pub /ssh/github_key.pub

# On créé le répertoire de Contao et on attribue les droits à l'utilisateur www-data
# N'utilisez jamais le root !
RUN echo WORKDIR_CONTAO : $WORKDIR_CONTAO
RUN mkdir -p $WORKDIR_CONTAO \
    # We give the www-data the same user UUID as ours
    && chown -R www-data:www-data /usr/local/bin/composer \
	&& chown -R www-data:www-data /ssh/ \
	&& chmod +x /usr/local/bin/composer \
    && chown -R www-data:www-data $WORKDIR_CONTAO \
	&& chown -R www-data:www-data /var/www

# On ajoute une autorisation d'accès dans le répertoire public de notre répertoire Contao
RUN sed -i -e "s@DocumentRoot .*@DocumentRoot $WORKDIR_CONTAO/public@" -e "s@#ServerName www.example.com@ServerName $BASE_URL@" /etc/apache2/sites-available/000-default.conf 

# On utilise désormais l'utilisateur www-data
USER www-data

# On se déplace dans le répertoire Contao puis on installe le CMS avec la version voulue
# Puis on autorise l'origine github.com dans les hôtes connus
RUN cd $WORKDIR_CONTAO \
 && composer create-project contao/managed-edition $WORKDIR_CONTAO $CONTAO_VERSION \
 && mkdir ~/.ssh \
 && echo "Host github.com\n  HostName github.com\n  IdentityFile /ssh/github_key" > ~/.ssh/config \
 && cat ~/.ssh/config \
 && ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts

# On modifie notre config Composer pour importer directement le bundle sur lequel on travaille
# Ce bundle est accessible uniquement grâce à la clef SSH importée plus haut
RUN cd $WORKDIR_CONTAO \
 && composer config repositories.bot '{"type": "vcs", "url": "git@github.com:Web-Ex-Machina/press-fsync-bot-bundle"}' \
 && composer require "webexmachina/press-fsync-bot-bundle:$BUNDLE_VERSION"

# Puis on télécharge le fichier Contao Manager et on donne sa propriété à www-data pour qu'on puisse l'exécuter
ADD --chown=www-data:www-data https://download.contao.org/contao-manager/stable/contao-manager.phar $WORKDIR_CONTAO/public/contao-manager.phar.php

# Enfin, on ajoute une autorisation de proxy
RUN echo "TRUSTED_PROXIES=REMOTE_ADDR\nHTTP_HOST=$BASE_URL\nSERVER_NAME=$BASE_URL\nSERVER_PORT=443\nAPP_SECRET=$APP_SECRET" > $WORKDIR_CONTAO/.env

Notez que ce ficher est générique et ne permet que d’installer un Contao basique avec un bundle spécifique. Vous pouvez le complexifier davantage en fonction de vos besoins.

Agir dans le container

Le principe d’un conteneur Docker est de fonctionner en circuit fermé et d’être relancé “de zéro” selon la configuration voulue. Cela veut donc dire que tout changement dans votre conteneur sera supprimé lors de son redémarrage. Pas hyper pratique pour développer, vous allez me dire.

Sauf que justement, c’est une logique qui pousse à la rigueur. Lorsque l’on développe, on travaille sur un dépôt git ou svn qui permet de versionner les répertoires de travail. On en avait parlé un peu dans cet article. Vu qu’on est censé commit — envoyer nos changements sur le dépôt distant — chaque soir, vous pouvez fermer le conteneur et récupérer votre travail via le dépôt GIT.

Par contre, lorsque vous faites tourner un conteneur sur votre poste, vous ne pouvez pas facilement éditer les fichiers à l’intérieur. Précisément parce que le conteneur fonctionne en circuit fermé, avec ses permissions sur les fichiers. Il existe bien évidemment des solutions.

Visual Studio Code

La solution la plus simple est d’utiliser l’IDE de Microsoft. Comme le WSL est géré par Windows, il est capable de se connecter directement à un conteneur Docker et peut agir à l’intérieur sans action additionnelle. Voici les différentes étapes de connexions.

Choisissez l'option de connexion
Connectez-vous au WSL
Puis choisissez "Attach to Running Container"
Choisissez votre conteneur PHP
Tada !

Dans notre cas, le projet est accessible à l’adresse localhost:8888. On retrouve tout ce qu’on attend de Contao, et notre dépôt de travail dans le dossier vendor/webexmachina. Tout ce que l’on fait via Visual Studio Code est transmis au conteneur sans action supplémentaire.

Autres IDE

Si votre IDE ne permet pas de modifier directement les fichiers dans les conteneurs, sachez qu’il est possible de copier des fichiers “locaux” vers votre conteneur Docker.

Dans le cas d’un projet Contao, ce qui nous intéressent sont les bundles dans le dossier vendor/webexmachina.

La première étape est de récupérer les fichiers en local, via un git clone.

Ensuite, il ne vous reste plus qu’à taper dans votre interpréteur de commande :

docker cp ./src/ sc_php_base:/usr/local/apache2/htdocs/smartgear/vendor/webexmachina/press-fsync-bot-bundle/

Cela fera une copie du dossier /src du dossier courant dans le dossier /usr/local/apache2/htdocs/smartgear/vendor/webexmachina/press-fsync-bot-bundle/ du conteneur sc_php_base (le conteneur qui contient le code de notre projet)

Vous pouvez donc modifier vos fichiers locaux puis les copier vers votre conteneur pour tester vos changements. Cette méthode est la plus simple à utiliser bien qu’il soit rébarbatif de devoir lancer la commande manuellement à chaque fois que l’on désire tester des changements.

Faites toujours attention à l’utilisateur en cours, surtout quand vous accédez au conteneur via ligne de commande. Par exemple, un cache:warmup en tant que root va complètement casser votre Contao car tous les fichiers seront créés avec des permissions root. Connectez-vous au docker avec l’utilisateur www-data, avec la ligne de commande suivante : docker exec -it -u www-data sc_php_base /bin/bash

Problématiques

Mettre en place un environnement Docker n’est pas aisé. Il y a à la fois beaucoup et peu de documentation disponible en ligne. Cela est dû au fait qu’il n’y ait pas de méthode absolue, ou d’environnement de travail unique.

Il y a beaucoup de choses à penser lorsque l’on met en place une infrastructure. On l’oublie quand on a des hébergements qui s’occupent de tout. La création de l’environnement Docker implique de mettre son nez dans tout ce dont vous avez besoin en production, ce qui n’est pas une mauvaise chose en soi, mais qui reste chronophage.

Ensuite, il y a toute la notion de persistance de la donnée. Par défaut, un conteneur ne garde rien. Donc c’est à vous de définir ce que vous voulez garder, en fonction de vos besoins du moment (via des volumes). Souvent, vous trouverez des configurations d’exemple dans les dépôts sur lesquels vous travaillez. Ils sont à répliquer et à adapter à votre configuration.

Exemple du dossier docker
Exemple du dossier docker

Il y a énormément de choses à apprendre sur Docker et davantage de possibilités. On a parlé ici de mettre en place un environnement de travail, mais Docker n’a pas que cet intérêt. Il est aussi utilisé pour réaliser des tests divers et variés sur des environnements de production spécifiques. De plus, on peut directement déployer en production des images Docker, ce qui facilite les mises en ligne ou les mises à jour d’applications.

Chez Web ex Machina, nous n’avons pas vraiment eu le besoin de cet outil jusqu’à récemment. Comme il est chronophage, il est naturellement plus adapté pour les gros projets, dans une optique de mises à jour régulières. Au final, nous commençons même à l’utiliser pour des projets plus simples. Avec la pratique, cela ne prend plus autant de temps, renforce nos méthodes de travail et nos procédures en interne, tout bénéf !

Dans la même catégorie