Page d'accueil Mes articles

Création de l'image de mon projet PHP et de mon serveur Caddy

Création d'une application PHP dont le but sera de fournir à l'utilisateur l'IP du conteneur Docker. Un serveur Caddy sera chargé d'exposer cette application sur le web

Publié en October 2022.

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 !

Cet article vous a été utile ? Faites le connaître sur les réseaux sociaux Twitter twitter LinkedIn linkedin

Un commentaire ?

codeur
Ou connectez-vous avec GitHub