FirstApp, pour connaitre l'IP de mon conteneur docker
Pour être sûr que tout fonctionne comme espéré, je vais créer un petit projet en PHP qui aura pour but d'afficher l'IP locale du conteneur docker. Et pour que cela colle le plus à la réalité de ce qu'est PHP aujourd'hui, je vais suivre la PSR-4 en utilisant composer
.
{
"name": "yoann/first_app",
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"authors": [
{
"name": "Yoann Chocteau",
"email": "X"
}
],
"require": {}
}
Concernant mon fichier index.php, il fera simplement appel à une classe FirstApp qui retournera l'IP Locale
<pre>
<?php
// public/index.php
use App\Service\FirstApp;
include '../vendor/autoload.php';
echo "First App \n";
echo FirstApp::getLocalIP();
?>
</pre>
Et concernant le service en question, il retourne simplement une variable d'environnement nommée LOCAL_IP
. Nous verrons plus tard comment cette variable est définie.
<?php
// src/Service/FirstApp.php
namespace App\Service;
class FirstApp
{
public static function getLocalIP(): string
{
return 'IP : '.$_SERVER['LOCAL_IP'];
}
}
Étape suivante: créer l'image docker. Une image très basique dont le rôle sera de faire tourner php-fpm
# Dockerfile
FROM php:8.1-fpm-alpine
WORKDIR /srv/app
# Je copie tout ce dont j'ai besoin dans mon image...
COPY public /srv/app/public
COPY src /srv/app/src
COPY vendor /srv/app/vendor
COPY composer.json /srv/app/composer.json
COPY docker-entrypoint.sh .
# ... Et je donne les droits nécessaires à la lecture et à l'éxecution
RUN chmod -R ugo+rx public
RUN chmod -R ugo+rx src
RUN chmod -R ugo+rx vendor
# PHP-FPM expose sur le port 9000. Il est important de le préciser ici afin que Caddy puisse communiquer avec mon conteneur créé à partir de cette image.
EXPOSE 9000
# ENTRYPOINT est executé à la fin de la création de mon conteneur. Juste avant CMD. On voit ça juste après.
ENTRYPOINT [ "./docker-entrypoint.sh" ]
# La commande principale, php-fpm
CMD php-fpm
#!/bin/sh
# docker-entrypoint.sh
# Nous le verrons plus tard, mais Caddy va devoir partager un volume avec mon conteneur PHP.
# Lorsque mon conteneur est recréé à partir d'une nouvelle version de mon image, la commande suivante permet de mettre à jour le volume.
# Petit warning cependant, si vous faites une modification dans votre index.php et que vous avez scale votre service, vous pouvez vous retrouvez avec une indisponibilité le temps du déploiement
cp -r /srv/app/public/* /first_app_volume
# La fameuse variable d'environnement LOCAL_IP que j'utilise dans mon projet. Elle contient simplement le résultat d'une commande qui me permet de récupérer l'IP du hostname
export LOCAL_IP=$(hostname -i)
exec "$@"
Pour finir, je construis l'image et je la pousse sur mon container registry.
docker build -t registry.gitlab.com/XXXXX/XXXXXX/first_app .
docker build registry.gitlab.com/XXXXX/XXXXXX/first_app
C'est tout pour le moment concernant mon projet PHP, nous reviendrons dessus plus tard pour voir comment le déployer avec Docker Swarm.
Caddy, pour mettre en ligne simplement FirstApp
Si vous avez passé du temps à jouer avec des confs Apache ou Nginx, vous allez voir que Caddy a une approche beaucoup plus simple.
# Caddyfile
# Dans ce premier bloc, nous définissons la configuration globale de Caddy qui sera commune à tous nos domaines.
{
# Définir un email est recommandé pour générer les certificats
email VOTRE_ADRESSE_MAIL
# Ici, je vous recommande pour vos tests d'utiliser le CA de staging. Le rate limiter est beaucoup plus intéressant
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
# Et j'ajoute debug qui m'aidera beaucoup en cas d'errreur
debug
}
# Je définie mon domaine
first-app.chocteau.dev {
# L'index.php ainsi que les assets seront disponible sur le volume /first_app
root * /first_app
# Je configure php_fastcgi afin d'utiliser php-fpm de mon service first_app
php_fastcgi first_app:9000 {
# Mais pour que cela fonctionne, je vais devoir reconfigurer un certain nombre de variable d'environnement
# afin de remplacer par exemple pour DOCUMENT_ROOT /first_app par /srv/app/public qui correspond au répertoire où est présent le code
env DOCUMENT_ROOT /srv/app/public
env SCRIPT_NAME /index.php
env SCRIPT_FILENAME /srv/app/public/index.php
}
# Et pour permettre le téléchargement de mes fichiers JS et CSS, j'ajoute file_server
file_server
}
Le Dockerfile, lui, est extrêmement simple. Je me base sur l'image de Caddy et je lui ajoute le Caddyfile créé plus haut.
# Dockerfile
FROM caddy:2-alpine AS app_caddy
WORKDIR /srv/app
COPY Caddyfile /etc/caddy/Caddyfile
Et pour finir, je build et je push !
docker build -t registry.gitlab.com/XXXXXX/XXXXXX/caddy .
docker push registry.gitlab.com/XXXXXX/XXXXXX/caddy
Avant de passer à la partie suivante, je voulais juste revenir sur l'utilisation de l'environnement de staging de LetsEncrypt. Nous n'avons pas exactement les mêmes limitations qu'en prod. Typiquement, la limitation d'échec de validation est de 60 par heure... alors que normalement, la limite est de 5 échecs par compte, par nom de domaine et par heure. Et 5, je vous promets, ça arrive très vite ! Avec docker swarm, en cas d'échec de démarrage d'un service, il retente. Alors la limitation à 5, il faut être au taquet pour ne pas se retrouver bloqué.
L'inconvénient est que vous devez définir le certificat de staging sur un navigateur dédié à vos tests. Et il est bien précisé dans la documentation que vous ne devez pas utiliser ce même navigateur pour parcourir le web, car de fait, vous avez mis en place une faille de sécurité.
L'approche que je préfère est celle qui consiste à désactiver https le temps de la mise en place. Pour ce faire, dans la configuration générale de Caddy, ajoutez simplement l'option suivante :
{
....
auto_https off
}
Et le dernier point sur lequel je voulais revenir, c'était la mise en place des variables d'environnements lorsque je fais appel à php-fpm. Pour first-app.chocteau.dev
, j'ai fais un montage de volume sur /first-app
. Mon SCRIPT_FILENAME est donc défini comme étant /first-app/index.php
. Et si je ne modifie pas cette variable d'environnement, j'obtiendrai l'erreur File Not Found
de la part de PHP-FPM.
Nous venons de voir à quoi vont ressembler mes images caddy et first_app, voyons maintenant comment déployer tout cela !