accueil   blog   bin   contact

Génération d'un site statique avec Gitlab CI-CD

Étapes de la génération d'un site statique

Les sites statiques sont générés par des programmes, assez simples, comme Hugo, Pelican ou, dans le cas qui nous concerne, ssg5. Tout l'intérêt est de dissocier l'étape de génération des pages de leur mise à disposition par un serveur web.

Pour effectuer cette étape, il est nécessaire de disposer d'un ordinateur possédant l'environnement adéquat, dépendant du générateur choisi. Il faut ensuite rendre le résultat disponible aux internautes. Le choix fait ici est d'utiliser un hébergement mutualisé d'un grand nom européen, mais d'autres options peuvent être retenues.

Il est possible d'utiliser l'ordinateur du blogueur pour faire ces deux opérations, mais cela n'est pas le choix qui a été fait ici. En effet, si plusieurs contributeurs veulent rédiger des articles, ou si un rédacteur unique utilise plusieurs ordinateurs, comment s'assurer que toutes les personnes et toutes les machines ont la dernière version du code ?

Suivi de version par git et Gitlab

Dans une optique de suivi des modifications apportées au présent site web et d'uniformisation des versions du site existantes, un gestionnaire de suivi de version est utilisé, c'est git. Un article futur traitera de ce logiciel très utile aux personnes manipulant du code, mais nous ne saurions que recommander la série de vidéos de Grafikart sur le sujet.

Pour rendre le code disponible au plus grand nombre, le choix a été fait de déposer le dépôt git sur une plateforme publique : framagit. C'est une instance Gitlab hébergée par Framasoft dans le cadre de la campagne Dégooglisons Internet. Il est en revanche difficile de conseiller d'héberger son code sur cette plateforme, vu qu'elle sera probablement prochainement abandonnée par l'association.

Les trois grands avantages de ce choix sont les suivants :

Compilation et déploiement d'un site web statique par un runner

Fichier gitlab-ci.yml

Un runner est un serveur qui est capable d'exécuter une succession de travaux dans un environnement préalablement choisi. La configuration des opérations, appelées jobs, prend place dans un fichier .gitlab-ci.yml placé à la racine du dépôt. Celui du présent site peut être trouvé sur framagit. La version sur laquelle s'appuie cet article correspond au commit 378b2bd0.

L'environnement retenu ici est celui de la dernière version de la distribution Alpine Linux, qui présente un immense avantage sur les autres environnements : sa petite taille. En effet, à chaque fois que le runner se lance, il récupère l'image Docker choisie, et plus celle-ci est petite, mieux c'est. Celle choisie ici a une taille de l'ordre de 5 Mo, bien plus faible que celle des grandes distributions GNU/Linux comme Arch, Ubuntu ou Debian. À ce titre, on pourra se référer à la série d'articles d'Enix sur le sujet. Ainsi, on indique au début du fichier :

image: alpine:latest

Il est ensuite possible de définir des jobs, qui se construisent comme un script shell. Ici, il y a deux jobs, en lien avec deux étapes du processus, dites stages. Ces deux étapes sont build et deploy qui correspondent aux étapes de génération et de déploiement du site sur son hébergement. On a alors les deux étapes :

stages:
  - build
  - deploy

Job de génération du site statique

Le premier job reprend les étapes de construction du site, avec les appels aux programmes brg, ssg5 et smc. Quelques lignes du fichier .gitlab-ci.yml sont néanmoins remarquables. Les premières concernent l'internationalisation d'Alpine Linux. En effet, par défaut, cette distribution n'embarque pas les différentes locales dont peut avoir besoin un utilisateur non anglophone. Ici, nous avons besoin de la locale fr_FR.UTF-8. Pour cela, on installe le package musl-locales et on exporte les variables permettant de définir cette nouvelle locale comme celle à utiliser :

apk add --no-cache musl-locales
export MUSL_LOCPATH=/usr/share/i18n/locales/musl
export LANG=fr_FR.UTF-8

Ensuite, le site est généré à l'aide des scripts détaillés dans les articles précédents.

mkdir -p dst fin
./bin/brg src/blog 'https://www.dioptre.fr' 'https://www.dioptre.fr/blog'
./bin/ssg5 src dst "www.dioptre.fr" "https://www.dioptre.fr"
./bin/smc dst fin

Aussi, ce job crée un artifact. Un artifact est une archive contenant un certain nombre de répertoires ou fichiers dont l'utilisateur pense avoir besoin pour la suite de l'exécution du processus. Ici, les deux répertoires conservés à la fin de l'exécution du job sont dst et fin, correspondant respectivement aux répertoires de création du site et celui qui sera finalement téléversé sur l'hébergement mutualisé. On lui attribue une durée de vie ; il est inutile de le garder trop longtemps :

artifacts:
  paths:
  - dst
  - fin
  expire_in: 1 day

Déploiement du site sur un hébergement mutualisé

Cet artifact est ensuite utilisé dans le job suivant, le contenu du répertoire fin étant prêt à être téléversé sur l'hébergement du site. Il faut alors indiquer que ce job dépend du présent et de son artifact :

dependencies:
  - build_site

Il est également précisé que cette étape de déploiement ne doit être effectuée que si la branche est master. Cela est utile dans le cas où une autre branche serait créée, comme une branche de développement. En effet, il n'est pas conseillé de placer en production un site qui peut ne pas être stable. Ainsi, le développement particulier se fera sur une branche spécifique, qui ne sera jamais visible par l'internaute.

only:
  - master

Le déploiement du site se fait via rsync avec une connexion ssh à l'aide d'une clé RSA. Il est nécessaire de l'ajouter aux fichiers de configuration du runner pour qu'il puisse se connecter au serveur distant. Or, cette clé est un fichier sensible qu'il faut protéger. Il est possible de l'ajouter aux variables du processus dans l'interface en ligne de Gitlab, pour que son contenu soit inaccessible aux personnes extérieures. On ajoute également les informations qui permettent d'identifier le serveur distant à ~/.ssh/known_hosts. Les différents fichiers étant sensibles, on ajuste leurs permissions.

apk add --no-cache openssh-client
eval $(ssh-agent -s)
echo "$SFTPKEY" | tr -d '\r' | ssh-add -
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "$SSHKNOWNHOSTS" >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts

Enfin, le runner assure la synchronisation du répertoire fin avec celui du répertoire distant à l'aide de l'utilitaire rsync. Le fichier .files, créé par ssg5 n'est pas nécessaire au bon fonctionnement du site, il est donc ignoré.

rsync -aczv --no-t --rsh='ssh' --delete-excluded --exclude='.files' fin/ "$DEPLOYDST"

Notons que le premier job peut être redémarré une fois en cas d'erreur, le second deux fois. Cela est particulièrement pertinent dans le cas du second job, pour lequel la connexion ssh peut échouer sans raison apparente. Pour que la nouvelle version du site soit effectivement déployée, il est utile que ce job puisse être relancé un peu plus tard.

Dépendances des programmes et performances

Avoir un temps d’exécution court était un des prérequis lors de la construction de ce processus de génération et de déploiement. En effet, cela permet de s’assurer rapidement qu’il n’y a pas eu d’erreur lors de l’exécution et que le site est bien déployé.

Cela justifie en particulier le choix de s’appuyer uniquement sur des scripts compatibles POSIX. Le shell embarqué par défaut dans Alpine Linux est ash. C’est un shell très rapide et minimaliste, compatible POSIX. Si un autre shell est requis pour l’exécution du processus, il faudra le télécharger et l’installer, ce qui prendra du temps. Aussi, le fait de s’appuyer sur un ensemble de script permet de limiter de nombre de dépendances et donc le nombre de logiciels à installer.

Dans l'état actuel des choses, l'exécution du processus ainsi exposé prend moins de 30 secondes. Cela est jugé satisfaisant.

Pour accélérer le processus, il serait possible :

Documentation du fichier .gitlab-ci.yml

On pourra se reporter à la très bonne documentation (en anglais) fournie par Gitlab au sujet de la construction du fichier .gitlab-ci.yml pour plus d'informations. Il est également intéressant de lire la page dédiée au processus d'intégration continue et aux artifacts.