Plus d’un an après mon article sur Hugo avec gitea, drone, minio et traefik, et beaucoup de modifications, je me suis dit qu’il serait temps de vous partager les évolutions.
Je reste cependant sur la forge gitea + drone, tout simplement parce que je l’aime bien ce petit duo.
Pourquoi changer ?!
Déjà je n’ai pas trouvé d’autre vrai utilité à mon S3 local, donc je maintenais minio que pour mon blog, je trouvais la solution un peu lourde pour un pauvre petit blog, malgré que très intéressant à mettre en place.
Ensuite je voulais sortir mon blog de mon serveur qui est chez moi dans mon garage, pour surtout 2 raisons. La première est que j’ai eu une longue période avec une connexion très instable, ce qui le rendait souvent indisponible. Ensuite ma machine sert également de seedbox, mais surtout de labs, donc je teste beaucoup de choses dessus, je casse souvent des trucs également et ayant de moins en moins de temps pour ces bidouilles, je peux laisser mon lab en carafe pendant plusieurs semaines.
J’ai donc décidé de passer chez scaleway, sur un petit stardust, ce qui est largement suffisant. Sur lequel j’ai installé un nginx avec une configuration un peu spécial. Et c’est tout, c’est ultra light, et ça déploie en 30 secondes.
Workflow de déploiement
J’ai décidé de partir sur un workflow très simple, je déploie en production que la branche main, et dès que je crée un nouvel article, je crée une branche pour celui ci. Celle-ci sera déployée sur mon serveur, dans un répertoire spécifique qui sera accessible via https://.blog.xataz.net.
Puisque quasiment tout ce que je fais est également à but pédagogique, j’ai décidé d’utiliser des concepts de droneio, et notamment les promotes.
Donc pour résumer :
- Création d’une branche pour mon article
- Push de mon article
- La pipeline build se lance, build mon blog, test la validité html, les fautes d’orthographe et upload un tarball de mon blog sur mon gitea
- La pipeline de deploiement se lance, me crée un environnement de review, et me notifie si c’est ok
- Je check que la mise en page est ok
- Je promote mon build sur drone
- Une autre pipeline se lance sur cette branche, supprime mon environnement de review, et merge sur la main
- Lancement d’une autre pipeline qui fait mon déploiement en production
Sachant que l’étape 6 et 7 peuvent être remplacé par un merge manuel, ou via gitea.
Configuration
Je pars du principe que vous avez déjà un gitea ainsi qu’un drone qui tourne, il existe des tonnes de ressources sur le sujet, je ne vois pas l’utilité d’en ajouter un. Je ne détaille pas également l’installation de nginx et la configuration de acme.sh, idem on trouve des ressources sur le sujet.
Configuration nginx
Pour permettre d’avoir un blog par branche, sans devoir reconfigurer mon nginx à chaque fois, j’ai créé 2 vhosts, un pour la prod, et un pour les reviews :
prod.conf :
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name xataz.net blog.xataz.net www.xataz.net;
return 301 https://$host$request_uri;
}
server {
listen 443 http2 ssl default_server;
listen [::]:443 http2 ssl default_server;
server_name xataz.net blog.xataz.net www.xataz.net;
ssl_certificate /etc/nginx/ssl/certs/xataz.net.pem;
ssl_certificate_key /etc/nginx/ssl/certs/xataz.net.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
ssl_dhparam /etc/nginx/ssl/certs/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/certs/xataz.net.pem;
add_header Strict-Transport-Security "max-age=63072000" always;
location / {
root /opt/www/blog/main;
index index.html index.htm;
}
}
Pour la prod rien de complexe pour ceux qui connaissent un peu nginx, mon blog est simplement stocké dans /opt/www/blog/main.
reviews.conf :
server {
listen 80;
server_name *.blog.xataz.net;
return 301 https://$host$request_uri;
}
server {
listen 443 http2 ssl;
listen [::]:443 http2 ssl;
server_name ~^(.+)\.blog\.xataz\.net$;
set $file_path $1;
ssl_certificate /etc/nginx/ssl/certs/blog.dev.xataz.net.pem;
ssl_certificate_key /etc/nginx/ssl/certs/blog.dev.xataz.net.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
ssl_dhparam /etc/nginx/ssl/certs/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/certs/blog.dev.xataz.net.pem;
add_header Strict-Transport-Security "max-age=63072000" always;
location / {
root /opt/www/blog/$file_path;
index index.html index.htm;
}
}
Là, c’est un peu plus spécifique :
server_name ~^(.+)\.blog\.xataz\.net$;
: Regex pour récupérer le bon domaine,(.+)
permets d’en récupérer le sous-domaine et le mettre dans une variable$1
set $file_path $1;
: Pour que se soit plus propre, je crée une variable$file_path
avec la valeur de$1
root /opt/www/blog/$file_path;
: Qui permets d’indiquer le bon répertoire
Par exemple pour cet article, j’ai créé une branche blog-cicd, qui sera copié dans /opt/www/blog/blog-cicd
, et qui sera accessible via https://blog-cicd.blog.xataz.net.
Les pipelines
Pour permettre tout ceci, j’ai créé 3 pipelines différentes. Toutes dans le fichier .drone.yml.
C’est un concept un peu particulier, mais pour ceux qui viennent de gitlab, les pipelines sont un peu comme les stages, et les steps, sont plutôt les jobs. Une pipeline peut être dépendante d’une autre, mais un step peut aussi être dépendant d’un autre. Cela permets d’avoir une gestion très fine de l’ordonnancement de notre workflow.
Nous n’avons cependant pas la possibilité d’avoir des jobs manuels sous drone. Pour pallier ceci, nous pouvons utiliser les promotes, qui permettent d’exécuter une autre pipeline par exemple.
Pour ceci, il me faut créer quelques secrets dans drone :
- hosts : Le noms des hôtes sur lesquels je déploie
- Private_Key : La clé ssh pour le déploiement
- discord_webhook_id : Je m’envoie des notifications une fois déployer, ceci est l’id du webhook discord
- discord_webhook_token : et son token
- gitea_token : Token qui me permet la création de package
- gitea_url : l’url de gitea
Pipeline de build
kind: pipeline
type: docker
name: build
steps:
- name: build
image: plugins/hugo
settings:
hugo_version: 0.102.1
validate: false
- name: html5validator
image: painless/html5validator
commands:
- html5validator --root public/
failure: ignore
depends_on:
- build
- name: languagetool
image: alpine
commands:
- apk add --no-cache openjdk8-jre git
- git fetch origin main
- wget https://languagetool.org/download/LanguageTool-5.6.zip
- unzip -q LanguageTool-5.6.zip
- for file in $(git diff --name-only origin/main | grep ".md"); do sed -e '/---/,/---/d' -e '/\`\`\`/,/\`\`\`/d' $file | java -jar LanguageTool-5.6/languagetool-commandline.jar -d WHITESPACE_RULE -l fr -c utf-8 -; done
failure: ignore
depends_on:
- build
- name: Push package to gitea
image: alpine
environment:
DRONE_TO_GITEA_TOKEN:
from_secret: gitea_token
GITEA_URL:
from_secret: gitea_url
commands:
- apk update && apk add curl
- tar czvf blog.tar.gz public/
- 'curl -XPUT -H "Authorization: token $DRONE_TO_GITEA_TOKEN" --upload-file blog.tar.gz $GITEA_URL/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${CI_COMMIT_SHA}/blog.tar.gz'
depends_on:
- build
- html5validator
- languagetool
trigger:
branch:
exclude:
- main
- master
event:
exclude:
- promote
include:
- push
En gros j’ai 4 jobs
- build : J’utilise le plugin hugo de drone, pas beaucoup de paramètre, et ça fonctionne directement.
- html5validator : Pour valider mon html
- languagetool : Pour valider mon orthographe
- Push package to gitea : Pour pousser une archive de mon blog sur gitea
Cette pipeline ne sera exécuté que sur les branches non principale, car ça ne sert à rien de rebuild quelques choses de testé, en plus cela peut altérer le livrable entre la review et la production.
Pour la version de mon blog, je me base simplement sur le commit.
Voici ce que ça donne :
Pipeline de déploiement
kind: pipeline
type: docker
name: deploy
steps:
- name: Download package from gitea
image: alpine
environment:
DRONE_TO_GITEA_TOKEN:
from_secret: gitea_token
GITEA_URL:
from_secret: gitea_url
commands:
- apk update && apk add curl
- 'curl -XGET -H "Authorization: token $DRONE_TO_GITEA_TOKEN" $GITEA_URL/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${CI_COMMIT_SHA}/blog.tar.gz > blog.tar.gz'
- tar xzvf blog.tar.gz
- name: deploy ${DRONE_BRANCH,,}
image: drillster/drone-rsync
settings:
hosts:
from_secret: hosts
username: root
key:
from_secret: Private_Key
port: 22
target: /opt/www/${DRONE_REPO_NAME}/${DRONE_BRANCH,,}
source: public/
prescript:
- mkdir -p /opt/www/${DRONE_REPO_NAME}/${DRONE_BRANCH,,}
script:
- chown -R www-data:www-data /opt/www/${DRONE_REPO_NAME}/${DRONE_BRANCH,,}
depends_on:
- Download package
- name: Notify Success
image: appleboy/drone-discord
settings:
webhook_id:
from_secret: discord_webhook_id
webhook_token:
from_secret: discord_webhook_token
message: "Deployment on ${DRONE_BRANCH,,} done (https://${DRONE_BRANCH,,}.blog.xataz.net)"
username: cicd
when:
status:
- success
depends_on:
- deploy ${DRONE_BRANCH,,}
- name: Notify Failure
image: appleboy/drone-discord
settings:
webhook_id:
from_secret: discord_webhook_id
webhook_token:
from_secret: discord_webhook_token
message: "Deployment on ${DRONE_BRANCH,,} failed (https://${DRONE_BRANCH,,}.blog.xataz.net)"
username: cicd
when:
status:
- failure
depends_on:
- deploy ${DRONE_BRANCH,,}
- Download package
trigger:
event:
- push
- Download package from gitea : Récupères et extrait mon archive depuis gitea
- Deploy ${DRONE_BRANCH,,} : Qui permets de copier les fichiers, via le plugin drillster/drone-rsync
- Notify Success : Qui m’envoie un message sur discord si le déploiement est ok
- Notify Failure : Idem, mais si c’est en erreur
Cette pipeline sera exécuté à chaque push, et permettra de déployer mon blog tout simplement.
Voici ce que ça donne :
Pipeline de promote
kind: pipeline
type: docker
name: Promote
steps:
- name: Merge to master
image: alpine
commands:
- apk update
- apk add git
- git branch -a
- git fetch origin main
- git checkout main
- git branch -a
- git merge ${DRONE_BRANCH}
- git branch -d ${DRONE_BRANCH}
- git push origin main
- git push -d origin ${DRONE_BRANCH}
- name: Delete environment
image: appleboy/drone-ssh
settings:
host:
from_secret: hosts
username: root
key:
from_secret: Private_Key
port: 22
script:
- rm -rf /opt/www/${DRONE_REPO_NAME}/${DRONE_BRANCH,,}
trigger:
branch:
exclude:
- master
- main
event:
- promote
target:
- production
Cette pipeline va simplement mergé ma branche en cours, vers la branche main, cela aura comme conséquence de lancer le déploiement sur la production. Le 2ème job permets de supprimer mon environnement de review.
Voici ce que ça donne :
C’est fini
Et voilà, une fois tout ceci fait, mon article est en ligne. C’est de cette manière que celui-ci vous est partagé.
Alors bien sûr, ce n’est pas parfait, loin de là, mais ça fonctionne, et ça correspond parfaitement à mon besoin. Nous pourrions l’améliorer, avec par exemple un linter markdown,