Déployer Hugo via Gitea et Drone-CI avec Caddy et MinIO

  1. Contexte et motivation
  2. MinIO
  3. Drone-CI
  4. Caddy
  5. Conclusion

Si le titre de cet article vous est familié : MERCI ! cela signifie que vous me lisez 😄 Dans le cas contraire, cet article fait suite à un autre article publié le mois dernier : Déployer Hugo via Gitea et Drone-CI. Vous aurez donc compris que je vais rendre la procédure encore plus complexe.

Contexte et motivation

Je suis très satisfait de ma stack de publication actuelle : j'écris mes articles "localement" sur mon Mac mini, quand je suis satisfait je commit mes modifications sur mon serveur Gitea, Drone-CI se déclenche, compile le site, et le copie sur le serveur web via SSH. Pour servir le site, j'utilise Caddy, qui doit alors disposer d'un accès au système de fichiers du serveur.

J'aimerais supprimer cette dépendance, pour m'éviter de monter un volume docker rien que pour faire ça. Il y a d'autres raisons plus techniques : cela pourrait me simplifier les sauvegardes, rendre possible la réplication des données, ce qui pourrait amèner à de la haute-disponibilité. Et puis, je suis très satisfait des performances de mon serveur MinIO mais qui, pour l'instant, reste sous-exploité. En outre, je pourrai supprimer le lien SSH entre la CI et le serveur Web, et l'utilisateur créé à cet unique effet.

Le but est donc que Drone-CI stocke le résultat de la compilation (c'est-à-dire, l'arborescence complète du site statique) dans MinIO, plutôt que de l'envoyer au serveur via SSH. Trois choses sont donc à modifier : MinIO pour créer un bucket destiné à mon site, Caddy pour lui dire comment accèder et servir ces données, et Drone-CI pour lui dire comment stocker les données dans le bucket.

Tout cela a l'air bien compliqué, je vous l'accorde, mais on peut voir les choses sous un autre angle :

  • Je ne rajoute finalement qu'une application au processus (MinIO)
  • C'est une application extrêmement simple, populaire, légère, rapide (en upload, je sature ma liaison gigabit, peu importe la taille des fichiers envoyés, ce qui n'est pas le cas avec SSH par exemple)
  • Je supprime un accès SSH et un utilisateur, donc - petit - gain de sécurité
  • Je découple Caddy et Drone-CI du stockage disque pour les coupler à un système de stockage distant, distribué et répliqué

Donc au final, ajouter une étape au processus me permet de simplifier la gestion de toute la stack, de mes serveurs, et de pérenniser mes données. Le tout avec des outils simples, puissants, légers et performants. Que demander de plus ?

MinIO

Avec un MinIO déjà opérationnel, il suffit de créer un nouvel utilisateur, un nouveau bucket (avec l'attribut public, sinon Caddy ne pourra pas y accéder), et donner les permissions readwrite au nouvel utilisateur sur ce bucket. C'est extrêmement simple via l'interface web.

J'ai nommé mon bucket richard-dern.fr.

Drone-CI

Dans Drone-CI, il faut créer deux nouveaux secrets, dans lesquels on va mettre l'accesskey* du nouvel utilisateur dans MinIO, et la secretkey* correspondante. J'ai tout simplement nommé ces secrets respectivement s3accesskey et s3secretkey.

Dans le fichier .drone.yml de mon projet, j'ai remplacé la partie concernant SCP par le plugin S3 :

name: upload
  image: plugins/s3
  settings:
    bucket: richard-dern.fr         # Remplacez par le nom de votre propre bucket
    source: public/**/*
    target: /
    strip_prefix: public/           # On supprime public/ sinon c'est chiant
    path_style: true                # Obligatoire pour MinIO, déprécié chez AWS
    access_key:
        from_secret: s3_access_key  # Le nom de votre nouvel utilisateur MinIO
    secret_key:
        from_secret: s3_secret_key  # La secret_key correspondante
    endpoint: http://10.0.2.1:9000  # Remplacez par l'URL de votre MinIO

Caddy

Caddy a nécessité un peu plus de recherche, mais j'ai fini par trouver une façon de faire, sans avoir recours au moindre plugin. Remplacez, dans les lignes suivantes (2 à 7), richard-dern.fr par le nom de votre bucket, et la dernière ligne par l'adresse de votre serveur MinIO.

www.richard-dern.fr {
    # Site statique, donc on bloque tout ce qui n'est pas GET
    # On évite ainsi les requêtes à l'API MinIO
    @method not method GET
    error @method "Method not allowed" 405

    @dirregex {
        path_regexp static ^(.*)/$
    }

    rewrite @dirregex /richard-dern.fr{re.static.1}/index.html
    rewrite * /richard-dern.fr{path}
    reverse_proxy http://10.0.2.1:9000
}

Le problème avec Caddy dans ce contexte, c'est qu'il ignore try_files et index, et que MinIO renvoit la liste des fichiers contenus dans le répertoire demandé en XML (API oblige). En outre, la directive reverse_proxy ne peut pas contenir de chemin. Pour résoudre ce problème, on utilise les lignes 7 et 8, ce qui nous permet au moins de ne pas être redirigés vers la page de connexion de MinIO.

Pour le problème des dossiers, on défini un matcher dans le jargon de Caddy, en l'ocurrence un path_regexp. On peut ainsi matcher toute URL qui se termine par un /. On créé ensuite la règle de réécriture à la ligne 6, pour ajouter /index.html à la fin. De fait, on peut conserver des URLs courtes, et, normalement, l'accès aux autres ressources n'est pas cassé…

Conclusion

Et voilà ! On peut démonter le volume utilisé jusq'à présent, et supprimer les fichiers du disque : Caddy n'utilisera plus que MinIO pour servir mon site. Le gain en performances me semble très intéressant, bien qu'il soit difficilement mesurable pour vous, chers visiteurs, compte tenu de mon upload limité. Mais de mon point de vue, tout me semble aller beaucoup plus vite, en particulier quand il y a des images dans un article.

Prochaine étape : haute-disponibilité ?

Plus d'informations