1
0
contenu/blog/2021/09/12/deployer-hugo-via-gitea-et-drone-ci/index.md

287 lines
13 KiB
Markdown
Raw Normal View History

2023-09-05 23:47:36 +02:00
---
title: Déployer Hugo via Gitea et Drone-CI
date: 2021-09-12
tags: [
"Gitea",
"Hugo",
"Drone-CI",
"SysAdmin",
"Docker",
"Auto-hébergement",
"Git"
]
---
## Pourquoi faire simple quand on peut faire compliqué ?
De prime-abord, on pourrait se dire : "si je veux un blog, je lance un WordPress
comme la moitié du web et je commence à publier en deux minutes". Mais quand on
est un vieux routard du web, on a des aspirations et des principes un peu plus
élitistes :
- WordPress ne génère pas de sites statiques (nativement)
- Le fait qu'il soit utilisé [par la moitié d'Internet](https://w3techs.com/technologies/overview/content_management) me révèle que c'est une cible facile pour des personnes ou des robots mal-intentionnés
- PHP est indispensable pour faire fonctionner WordPress, même si WordPress est configuré pour générer des pages pseudo-statiques
- WordPress est une usine à gaz, qui sort du HTML, du CSS et du Javascript pas optimisés
- Le code de WordPress est abominablement dégueulasse, impossible de faire quoique ce soit de propre, ni même de léger
- WordPress est impersonnel, ce que je trouve embêtant pour un blog (et encore plus pour un site corporate d'ailleurs) : il doit être le reflet de son auteur ; s'il ressemble à tous les autres, quel intérêt ? (et je vous le dis : ce n'est pas simplement parce que vous customisez un thème qu'on ne voit pas que vous utilisez WordPress...)
- WordPress a besoin d'une base de données
- L'écrasante majorité des extensions tierces un tant soit peu intéressantes sont payantes ou sur l'immonde modèle du premium
Un générateur de sites statiques comme Hugo me permet de :
- Séparer l'apparence, le contenu, et l'outil qui construit le site final (je peux utiliser autre chose qu'Hugo si j'en ai envie, moyennant quelques ajustement mineurs). Impossible de faire ça avec WordPress
- Publier un site 100% sécurisé : sans PHP, sans formulaires, sans Javascript, le maillon faible de la sécurité de mon site n'est pas mon site
- Avoir un suivi de mes modifications (grâce à Git)
- Simplifier les processus de sauvegarde et de restauration (en gros, avoir un plan de relance en béton armé en cas de défaillance technique - je peux même faire héberger temporairement mon site ailleurs sans avoir besoin d'aucune autre dépendance qu'un serveur web, impossible de faire ça avec WordPress en un minimum de temps)
- Être réellement libre du point de vue logiciel
L'utilisation de git dans ce processus est totalement facultative : il est tout
à fait possible de construire son site avec Hugo sans tout ce que je vais
présenter dans cet article. Néanmoins, cette procédure qui semble fastidieuse
de prime-abord est en réalité très puissante et très confortable au quotidien :
- Je créé ou modifie mes articles sous la forme de fichiers [Markdown](https://gohugo.io/content-management/formats/) - pratiquement du texte brut
- Je les publie sur [ma forge Gitea](https://git.athaliasoft.com/)
- Drone-CI fait le reste : construire le site en lançant l'exécutable Hugo et envoyer les fichiers HTML et CSS au serveur de production
Pour peu qu'on choisisse un thème existant plutôt qu'en créer un, on peut
**réellement** ne se préoccuper que du contenu de son site.
## Composant logiciels
Tout passe par des containers (docker, évidemment, mais l'utilisation de podman
est possible). Je présume donc que docker est installé et fonctionnel.
### Gitea
[Gitea](https://gitea.io/en-us/) est une _forge logicielle_ qui s'appuie sur le
système de gestion de code [Git](https://git-scm.com). J'ai choisi Gitea pour sa
sobriété, sa légèreté et sa simplicité, mais il existe d'autres forges
logicielles : [GitHub](https://github.com) qui appartient à Microsoft et qu'il
n'est pas possible d'auto-héberger, ou [Gitlab](https://about.gitlab.com) pour
ne citer que ces deux-là.
_docker-compose.yml_ :
```yaml {class=not-prose}
version: "3"
services:
gitea:
image: gitea/gitea:latest
restart: always
ports:
- "${HTTP_PORT}:3000"
- "${SSH_PORT}:22"
volumes:
- ${DATA_DIR}:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
```
Remplacez les variables en fonction de vos besoins :
- `HTTP_PORT` : le port sur lequel les requêtes HTTP arrivent sur votre serveur (pas le port HTTP du container)
- `SSH_PORT` : le port du serveur SSH du serveur (pas le port SSH du container)
- `DATA_DIR` : le chemin complet du dossier dans lequel stocker les données de Gitea, ou le volume si vous préférez utiliser un volume
### Hugo
[Hugo](https://gohugo.io) est un générateur de sites statiques. C'est ce que
j'utilise pour créer et maintenir mon blog. Il en existe d'autres, tels que
[Jekyll](https://jekyllrb.com). J'ai choisi Hugo pour sa légèreté.
L'essentiel du travail avec Hugo se fait sur votre machine locale. Il n'y a rien
à installer côté serveur. La construction du site se fera via Drone-CI, dans
lequel on va configurer un
[_pipeline_](https://docs.drone.io/pipeline/overview/) pour compiler les
ressources CSS (et éventuellement Javascript), et créer l'ensemble du site
statique dans un container docker. Tous ces fichiers seront ensuite envoyé au
serveur web via rsync, scp, ou ce que vous voulez.
Pour faire tout cela, il "suffit" d'ajouter un fichier _.drone.yml_ à la racine
de votre projet Hugo avec le contenu suivant :
```yaml {class=not-prose}
kind: pipeline
type: docker
name: default
steps:
- name: submodules
image: alpine/git
commands:
- git submodule update --init --recursive
- name: build
image: klakegg/hugo:ext-ci
commands:
- cd themes/blog_theme && npm i && cd -
- hugo --gc --minify --environment production
- name: deploy
image: appleboy/drone-scp
settings:
host: "10.0.2.1"
target: /mnt/volume1/shares/www/richard-dern.fr/www/
source: public/*
username:
from_secret: ssh_user
password:
from_secret: ssh_password
trigger:
branch:
- master
```
L'étape _submodules_ (entre les lignes 7 et 10) n'est nécessaire que si le thème
que vous utilisez est dans un _submodule_ git dans le répertoire _theme_ de
votre projet Hugo. Si le thème est directement dans l'arborescence de votre
projet, vous pouvez supprimer les lignes concernées.
Le thème que j'ai créé utilise
le framework [Tailwind CSS](https://tailwindcss.com), j'ai donc besoin de
compiler les ressources via `npm`, ce que je fais à la ligne 15, juste avant de
construire toutes les pages du site. Jusqu'à présent, c'est plutôt simple, non ?
Ensuite, l'étape la plus compliquée (_deploy_). Il faut créer des _secrets_ dans
Drone-CI (ici, `ssh_user` et `ssh_password`), mais nous verrons cela un peu plus
bas. Pensez simplement à modifier les valeurs de _host_ et _target_ : l'adresse
IP du serveur qui va héberge Hugo et le chemin où les fichiers générés par Hugo
seront envoyés.
Ceci dit, ici j'utilise `scp`, mais d'autres services sont disponibles.
Consultez la [liste des plugins Drone-CI](http://plugins.drone.io) pour utiliser
celui qui convient à votre hébergement.
Enfin, on a ajouté la directive
[_trigger_](https://docs.drone.io/pipeline/triggers/) pour que tout ce processus
de publication ne soit déclenché **que** lorsque la branche git _master_ est
modifiée, l'idée étant de protéger la branche _master_ en écriture, modifier le
contenu dans une nouvelle branche, fusionner avec _master_ une fois terminé,
mais là on part dans des considérations philosophiques devops :smile:
### Drone-CI
[Drone-CI](https://www.drone.io) est une application d'intégration continue :
c'est le genre d'applications qui s'intercalent entre la forge logicielle et les
serveurs de mise en production, une position privilégiée qui confère à ces
outils beaucoup de puissance et un intérêt considérable. Tests unitaires,
construction de ressources, publication automatique, les cas d'usage sont
nombreux.
La documentation de Drone-CI préconise de l'installer sur une machine physique
différente du serveur Gitea afin d'éviter les problèmes et de simplifier la
configuration. C'est l'option que j'ai choisi, mais en pratique, si vous savez
ce que vous faites, vous ne devriez pas avoir de soucis.
Il faut toutefois prendre en considération les ressources du (des) serveurs. Si
vous utilisez des Raspberry Pi par exemple, il vaut peut-être mieux lancer
Drone-CI sur un Pi dédié à cet usage.
Pour que Drone-CI puisse communiquer avec Gitea, il faut aller dans Gitea,
votre profil, puis dans l'onglet _Applications_. Là, créez une application
_OAuth_. Mettez ce que vous voulez comme nom ("drone", par exemple), et l'URL
de redirection, qui correspond à l'URL complète à laquelle vous accèderez à
Drone-CI, suffixée par _/login_. Dans mon cas, l'URL de redirection est :
> https://ci.athaliasoft.com/login
Validez, et Gitea vous communiquera un certain nombre d'informations à
renseigner dans le fichier _docker-compose.yml_ pour Drone-CI, notamment un
_Client ID_ et un _Client Secret_, à remplacer ci-dessous.
_docker-compose.yml_ :
```yaml {class=not-prose}
version: "3"
services:
drone:
image: drone/drone
restart: always
ports:
- "${PORT}:80"
environment:
- DRONE_GITEA_SERVER=${DRONE_GITEA_SERVER}
- DRONE_GITEA_CLIENT_ID=${DRONE_GITEA_CLIENT_ID}
- DRONE_GITEA_CLIENT_SECRET=${DRONE_GITEA_CLIENT_SECRET}
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_SERVER_HOST=${DRONE_SERVER_HOST}
- DRONE_SERVER_PROTO=https
volumes:
- ${VOLUME_ROOT}:/data
```
- `DRONE_GITEA_SERVER` correspond à l'URL de votre serveur Gitea
- `DRONE_RPC_SECRET` est une clé à générer via la commande `openssl rand -hex 16` (par exemple)
- `DRONE_SERVER_HOST` est le nom d'hôte correspondant à l'URL de redirection (donc, dans mon cas spécifique, _drone.git.athaliasoft.com_)
- `VOLUME_ROOT`, un volume docker ou un répertoire dans lequel seront stockées les données de Drone-CI
### Runners
En plus de Drone-CI, il faut configurer des
[_runners_](https://docs.drone.io/runner/overview/), c'est-à-dire un ou
plusieurs services qui vont exécuter les pipelines configurés. Comme je dispose
de plusieurs serveurs, j'ai installé un runner par serveur, ce qui permet
d'améliorer les performances générales de Drone-CI, mais il est tout à fait
possible (et viable) d'avoir un seul runner installé sur la même machine que
Drone-CI.
_docker-compose.yml_ :
```yaml {class=not-prose}
version: "3"
services:
runner:
image: drone/drone-runner-docker
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "3000:3000"
environment:
- DRONE_RPC_PROTO=http
- DRONE_RPC_HOST=drone
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_RUNNER_CAPACITY=${DRONE_RUNNER_CAPACITY}
- DRONE_RUNNER_NAME=${DRONE_RUNNER_NAME}
```
Pensez à modifier les variables suivantes pour convenir à votre environnement :
- `DRONE_RPC_SECRET`, la même clé que celle définie plus haut pour Drone-CI
- `DRONE_RUNNER_CAPACITY`, le nombre de pipelines que le runner peut exécuter en même temps (1 est suffisant pour des environnements modestes, 2 ou plus pour des environnements plus musclés)
- `DRONE_RUNNER_NAME`, un nom à attribuer au runner pour savoir quel runner a exécuté quoi et quand
## Performances
En voyant tout ça, on pourrait se dire que c'est consommateur de ressources. En
réalité, tout cela est très, très léger, et très performant. Il faut moins de 30
secondes pour déployer en production un nouvel article ; une durée qui serait
beaucoup - beaucoup - plus courte s'il n'y avait pas l'étape de compilation CSS
avec `npm`.
La possibilité de tirer partie d'un nombre indéterminé de runners est un gage de
scalabilité, même si les performances sont déjà élevées avec un seul serveur,
grâce à la sobriété des applications mises en oeuvre.
J'aime ce qui est rapide et performant, même si cela nécessite un peu de temps
pour être mis en oeuvre. Le processus expliqué ici respecte mon cahier des
charges.
## Conclusion
J'ai présenté ici le déploiement automatisé d'un site statique créé avec Hugo,
puisant dans un serveur Gitea, en construisant le site via Drone-CI avant de
copier les fichiers sur le serveur de production. C'est **un** cas d'usage parmi
d'autres : je me sers également de cette configuration pour créer
automatiquement des releases pour certaines de mes librairies, par exemple.
Le simple fait de parcourir [la liste des plugins](http://plugins.drone.io/) de
Drone-CI donne un aperçu de ce qu'il est possible de faire _out-of-the-box_,
mais en réalité, il n'y a pas vraiment de limite dans la mesure où on peut
facilement créer ses propres plugins, puisque ce ne sont que des containers
docker !