Pour divers projets, il m'arrive régulièrement de devoir mettre en place un serveur PHP/Apache + MySQL, et c'est souvent la galère pour développer (il faut se connecter en SSH sur le conteneur Apache ou SQL, il faut créer et gérer un sshfs pour développer ou synchroniser via un script ou encore un dépôt git), de plus l'image PHP-Apache de base ne dispose pas de toutes les librairies et/ou modules qui m’intéressent.
J'ai donc créé une image “Apache2 + PHP” personalisée:
il est important de noter que cette image n'est pas directement exploitable en production,elle ne prends pad en charge le SSL/HTTPS; nous utiliserons un reverse proxy (voir ci-dessous) pour résoudre cette problématique.
Nous reviendrons plus tard sur le dossier/volume /var/www/html
car c'est lui qui hébergera notre application, et permet donc de dissocié la partie “serveur APACHE/PHP” de l'application/web site.
Cette image même si elle est exploitable directement, demande un peu d'attention (notamment car elle ne supporte que le HTTP) et peut demander d'autres composants (MariaDB, PHPMyAdmin, reverse proxy, etc.).
Docker recommande d'utiliser un compose file pour créer des services et conteneurs, c'est exactement ce que je propose de faire plus bas.
Voici le DockerFile
qui permet de “build” l'image, il attends un paramètre FROM_REF
qui permet de choisir quelle image de référence l'on désire utiliser (PHP 8.3, PHP 8.4, bookworm ou autre…)
# Dockerfile for PHP Web app # # Specific PHP Version (bookworm & apache) # # Source & infos : # https://hub.docker.com/_/php # ARG FROM_REF FROM ${FROM_REF} # Update to latest package and install requierements RUN apt-get update && apt-get dist-upgrade -y; \ apt-get install -y --no-install-recommends \ libgd-dev \ libwebp-dev \ libpng-dev \ libjpeg-dev \ libwebp-dev \ libxpm-dev \ libfreetype-dev \ zlib1g-dev \ libzip-dev; \ apt-get autoremove; \ apt-get autoclean; \ rm -rf /var/lib/apt/lists/*; # Add Apache Modules: RUN a2enmod rewrite # Use the default production configuration RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" # Add pdo & mysql extention RUN docker-php-ext-configure gd --with-freetype --with-jpeg; \ docker-php-ext-install pdo_mysql mysqli zip opcache gd
Il n'y a rien d'exotique:
On peut build l'image via la commande suivante:
frater@vulkan: ~$ docker build --rm --build-arg FROM_REF="php:8.3.23-apache-bookworm" -t saintfrater/php8-3-23-custom:$(date +%Y%m%d-%H%M) -t saintfrater/php8-3-23-custom:latest . Sending build context to Docker daemon 56.83kB Step 1/6 : ARG FROM_REF Step 2/6 : FROM ${FROM_REF} ---> fa6c27da5746 Step 3/6 : RUN apt-get update && apt-get dist-upgrade -y; apt-get install -y --no-install-recommends libgd-dev libwebp-dev libpng-dev libjpeg-dev libwebp-dev libxpm-dev libfreetype-dev zlib1g-dev libzip-dev; apt-get autoremove; apt-get autoclean;rm -rf /var/lib/apt/lists/*; ---> Using cache ---> 9096e3a0dfc4 Step 4/6 : RUN a2enmod rewrite ---> Using cache ---> eb2007e04345 Step 5/6 : RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" ---> Using cache ---> 66c9fc9fc2f4 Step 6/6 : RUN docker-php-ext-configure gd --with-freetype --with-jpeg; docker-php-ext-install pdo_mysql mysqli zip opcache gd ---> Using cache ---> 89c70d388760 Successfully built 89c70d388760 Successfully tagged saintfrater/php8-3-23-custom:20250630-0300 Successfully tagged saintfrater/php8-3-23-custom:latest
Comme annoncé précédemment, nous allons utiliser un compose.yaml file pour créer notre service.
services: # Web Server webserver: image: saintfrater/php8-3-23-custom:latest container_name: proj01-webserver restart: unless-stopped ports: - 80:80 tty: true stdin_open: true volumes: - public:/var/www/html environment: - TZ=Europe/Brussels networks: - server volumes: public: networks: server: external: true
Ce compose file crée un conteneur proj01-webserver
et un volume public
sur votre host docker, il permet de joindre le conteneur via l'url http://host/
.
vous pouvez utiliser la commande suivante pour créer l'application:
frater@vulkan:~$ docker compose -f compose.yaml -p proj_vulkan up
Cette commande va créer:
proj01-webserver
(création du conteneur ou redémarrage si le conteneur existe déjà)proj_vulkan_public
(si le volume existe déjà il ne sera pas modifié)Veuillez noter que si un autre conteneur (ou même le host) utilise déjà le port HTTP/80, le compose ne fonctionnera pas, il faudra modifier le fichier “compose.yaml” pour utiliser un autre port libre
je l'ai dis avant, mais il est rare que nous n'ayons pas besoin de “composants” supplémentaires (database, etc.) dans une application.
Avec la phylosophie de Docker, c'est extremement facile.
Nous allons ajouter à notre compose.yaml
ces services un par un; a commencer par un serveur MariaDB; afin que notre application puisse communiquer avec une base de données:
services: # Web Server webserver: image: saintfrater/php8-3-23-custom:latest container_name: proj01-webserver restart: unless-stopped ports: - 80:80 tty: true stdin_open: true volumes: - public:/var/www/html environment: - TZ=Europe/Brussels depends_on: - db networks: - server - backend # MariaDB Server db: image: mariadb:latest container_name: proj01-db restart: unless-stopped environment: # update with YOUR requiered db informations - MARIADB_DATABASE=__YOUR_DATABASE_NAME__ # So you don't have to use root, but you can if you like - MARIADB_USER=__YOUR_APPLICATION_USER__ # You can use whatever password you like - MARIADB_PASSWORD=__YOUR_APPLICATION_DB_PASSWORD__ # Password for root access - MARIADB_ROOT_HOST=localhost - MARIADB_ROOT_PASSWORD=__YOUR_DB_ROOT_PASSWORD__ volumes: - db:/var/lib/mysql networks: - backend volumes: db: public: networks: server: external: true backend: driver: bridge
Ces paramètres sont utilisés par l'image MariaDB lors de l'initialisation des la base de données du conteneur (lorsque le volume db
est vide) pour créer l'instance du SQL;
Une fois la base de données créée, vous pouvez supprimer l'information de votre compose file.
Le Host du root est localhost
afin de restreindre la connexion “root” depuis un autre conteneur (il ne sera pas possible à PHPMyAdmin de se connecter en root), si vous désirez utiliser cette possibilité, il faudra remplacer localhost
par %
.
Veuillez noter que j'ai délibérement utiliser 2 réseaux :
server
(externe) qui est partagé par d'autres services sur le host, et permet la communication entre “front-end”.backend
(interne) et est lié à mon environnement décrit dans le compose file.
ce choix permet de dissocié le front-end server
et le back-end backend
qui sera plutôt réservé à la communication entre conteneurs “internes” à mon application.
J'utilise ce design pour limiter les risques et contrôler plus facilement les interactions entre services.
Il faudra également adapter les paramètres d’environnement selon les besoins de votre application:
# MariaDB application's credentials MARIADB_DATABASE=__YOUR_DATABASE_NAME__ MARIADB_USER=__YOUR_APPLICATION_USER__ MARIADB_PASSWORD=__YOUR_APPLICATION_DB_PASSWORD__ # MariaDB root informations MARIADB_ROOT_HOST=localhost MARIADB_ROOT_PASSWORD=__YOUR_DB_ROOT_PASSWORD__
Comme mentionné au début, le développement demande également un accès facile à la DB, j'ai donc ajouté à mon stack un phpmyadmin
.
Comme il est impossible (sauf a utiliser un reverse proxy) de monter 2 serveurs différents sur le même port 80, nous devons connecter le port 80 du apache sur le port 80 du host docker, et le port 80 du PHPMyAdmin sur le port 8080 du host docker…
On peut donc accéder à l'application via l'url http://host/
et à l'interface phpMyAdmin via l'url http://host:8080/
.
services: # Web Server webserver: image: saintfrater/php8-3-23-custom:latest container_name: proj01-webserver restart: unless-stopped ports: - 80:80 tty: true stdin_open: true volumes: - public:/var/www/html environment: - TZ=Europe/Brussels depends_on: - db networks: - server - backend # MariaDB Server db: image: mariadb:latest container_name: proj01-db restart: unless-stopped environment: # update with YOUR requiered db informations - MARIADB_DATABASE=__YOUR_DATABASE_NAME__ # So you don't have to use root, but you can if you like - MARIADB_USER=__YOUR_APPLICATION_USER__ # You can use whatever password you like - MARIADB_PASSWORD=__YOUR_APPLICATION_DB_PASSWORD__ # Password for root access - MARIADB_ROOT_HOST=localhost - MARIADB_ROOT_PASSWORD=__YOUR_DB_ROOT_PASSWORD__ volumes: - db:/var/lib/mysql networks: - backend # phpMyAdmin phpmyadmin: image: phpmyadmin container_name: proj01-phpmyadmin restart: unless-stopped ports: - 8080:80 environment: - TZ=Europe/Brussels - # define the SQL host name & connection port - PMA_HOST=proj01-db - PMA_PORT=3306 # credentials for connection - PMA_USER=__YOUR_APPLICATION_USER__ or root - PMA_PASSWORD=__YOUR_APPLICATION_DB_PASSWORD__ or __YOUR_DB_ROOT_PASSWORD__ depends_on: - db networks: - server - backend volumes: db: public: networks: server: external: true backend: driver: bridge
Bien prendre en comptes les remarques exprimées pour le service MariaDB
Attention que si vous précisez les informations de login (PMA_USER et PMA_PASSWORD) dans votre environnement, PHPMyAdmin se connectera immédiatement à la DB sans demander d'authentification; exposant ainsi votre base de données (ou l'ensemble de votre serveur SQL si vous avez utiliser le compte root
) a toute personne qui utilisera l'URL(http://host:8080/) pour se connecter.
Afin de ne pas s'enerver avec SSH, la syncho ou un git, j'ai décidé d'ajouter a mon stack, un IDE (Eclipse Theia IDE).
Theia est un environnement de développement intégré (IDE) open source développé principalement par Eclipse Foundation. Il est conçu pour être extensible, modulaire et utilisable en mode navigateur.
Et c'est là que la notion de volume /var/www/html
prends tout son sens, en effet, il sera plus facile de “monter” le volume 2 fois, une fois pour le web serveur et une fois pour l'IDE et d'éditer les fichiers “en directe”.
l'un des avantages du reverse proxy est qu'il permet de lier plusieurs services sur un meme port, c'est le role du Reverse Proxy de diriger les requetes externe vers le bon serveur (en général un différencie un service via son url), il peut également appliquer des parametres SSL de façon indépendante du web serveur, et donc le developpement peut se concentrer uniquement sur le code…