Déploiement automatique du blog CRI avec gitlab-ci
Notre fonctionnement avec git
Dans notre fonctionnement de gitlab, il nous est interdit de push sur master. Le processus traditionnel de travail consiste donc à travailler sur sa branche, ouvrir une merge request, et une fois celle ci finie, de merge sur master.
Cette utilisation classique de gitlab nous permet donc d’avoir un déploiment automatique du blog avec une version de prod, qui correspond à la branche master, et une version de dev, pour toutes les autres branches.
Gitlab-ci
Pour les pressés
stages:
- site-builder
- site-publisher
site-builder:
stage: site-builder
image: jekyll/jekyll
script:
- jekyll build
cache:
paths:
- /usr/local/bundle
artifacts:
expire_in: 2 days
paths:
- _site
site-publisher:release:
stage: site-publisher
image: docker:stable
services:
- docker:dind
only:
- master
dependencies:
- site-builder
script:
- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY
- docker run --rm -e AWS_SECRET_ACCESS_KEY=${cri_blog_secret} -e AWS_ACCESS_KEY_ID=${cri_blog_access} -e cri_blog_bucket=${cri_blog_bucket} -v `pwd`/_site:/srv/site ${cri_image_archlinux} aws s3 --endpoint-url ${endpoint_url} cp --acl public-read --recursive /srv/site $cri_blog_bucket
site-publisher:dev:
stage: site-publisher
image: docker:stable
services:
- docker:dind
except:
- master
dependencies:
- site-builder
script:
- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY
- docker run --rm -e AWS_SECRET_ACCESS_KEY=${cri_blog_dev_secret} -e AWS_ACCESS_KEY_ID=${cri_blog_dev_access} -e cri_blog_bucket=${cri_blog_dev_bucket} -v `pwd`/_site:/srv/site ${cri_image_archlinux} aws s3 --endpoint-url ${endpoint_url} cp --acl public-read --recursive /srv/site $cri_blog_bucket
Voilà à quoi ressemble notre fichier de configuration gitlab-ci pour gérer le blog.
Rentrons dans les détails de notre .gitlab-ci.yml
:
Stages
Un stage (“étape” en français) dans gitlab-ci correspond à un regroupement de un ou plusieurs jobs. Le stages sont successifs là où les jobs peuvent être en parallèle ( lien vers la documentation de gitlab sur les stages)
stages:
- site-builder
- site-publisher
Dans le fonctionnement du blog, on écrit des fichiers en markdown
(principalement), qui sont ensuite traduit par
Jekyll
en HTML/CSS. Il devient donc logique d’avoir une étape qui s’occupe de cette
génération du code du site statique par jekyll (site-builder
), et une qui
publie le site généré (site-publisher
).
Phase de build
Intéressons-nous à la phase de build :
site-builder:
stage: site-builder
image: jekyll/jekyll
script:
- jekyll build
cache:
paths:
- /usr/local/bundle
artifacts:
expire_in: 2 days
paths:
- _site
On créer un job site-builder
qui fait parti du stage site-builder
(original), et qui utilise l’image docker jekyll/jekyll
, image officielle de
Jekyll. Le script est très simple, et c’est normal car il n’y a rien à faire
de particulier.
On pense quand même à définir un cache du bundle ruby pour éviter de télécharger à chaque CI l’intégralité des dépendances nécéssaires (soyons sympa avec les mecs qui nous mettent des ressources à disposition).
L’artifact _site
est le dossier dans lequel Jekyll met le résultat final de
son build – le webroot. On va donc le transmettre à l’étape d’après, qui va
publier le site.
Publication
Il existe deux jobs pour la publication, l’un pour le dev et l’autre pour la prod. Intéressons nous à celui de la prod pour commencer :
site-publisher:release:
stage: site-publisher
image: docker:stable
services:
- docker:dind
only:
- master
dependencies:
- site-builder
script:
- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY
- docker run --rm -e AWS_SECRET_ACCESS_KEY=${cri_blog_secret} -e AWS_ACCESS_KEY_ID=${cri_blog_access} -e cri_blog_bucket=${cri_blog_bucket} -v `pwd`/_site:/srv/site ${cri_image_archlinux} aws s3 --endpoint-url ${endpoint_url} cp --acl public-read --recursive /srv/site $cri_blog_bucket
Comme on publie le site pour la prod ou pour le dev, le nom du job porte la
cible (site-publisher:release
). On utilise ici une image docker:stable
car
pour publish le site sur le S3 nous avons besoin du tool awscli
. Plutôt que
d’utiliser une image publique qui doit certainement exister, nous préférons ici
utiliser notre image perso qui fait office de couteau suisse. A cause de
restrictions particulières d’accès au registry gitlab lorsque les workers et le
gitlab sont configuré dans Kubernetes, il est plus facile de lancer une image
classique, se connecter au registry et lancer notre image custom via du
docker-in-docker.
Une API S3 est une API RESTful (qui implémente le CRUD). On manipule des fichiers via les méthodes HTTP. Le site étant complètement statique, une telle API est adaptée car il n’y a pas de script à éxecuter, de plus, la nature des implémentations fournie de très bonnes performances et une redondance solide. Le nom S3 vient d' Amazon Simple Storage Service, les créateurs de S3. Les API S3 sont proposée par Amazon, mais il est également possible d’avoir son API S3 hébergée chez soi (avec Ceph, minio, …) {: .notice–info}
L’utilisation de docker-in-docker requiert la définition du service
docker:dind
, nécéssaire pour nous permettre de lancer notre conteneur en
étant nous même dans un conteneur.
On précise le only: master
pour déployer la prod uniquement sur la branche
master, bien évidemment.
On précise également la dépendance envers notre étape précédente pour pouvoir récupérer l’artifact précédemment créé.
Le script est relativement simple bien que la deuxième ligne soit
particulièrement longue. Tout d’abord on se connecte à notre registry, pour
pouvoir utiliser notre image qui possède awscli
. Ensuite on lance ladite
image, en précisant plusieurs choses:
- les variables d’environnement, issue de la config gitlab, sont transmises au conteneur pour qu’il puisse s’identifier auprès du S3 ;
- le
--rm
, pour des raisons évidentes ; - le
-v
pour monter le site généré dans le conteneur ; - la commande à executer.
La commande est un simple aws cp
avec tous les arguments nécéssaires à son
bon fonctionnement :
- on précise l’endpoint car on n’utilise pas AWS S3, mais notre propre S3, via Ceph ;
- les acl pour préciser que les fichiers du site seront accessibles en lecture seule par n’importe qui, ce qui semble pertinent si on veut que le blog ne renvoie pas que des HTTP 403 ;
- le mode
--recursive
, pour lui dire de copier l’intégralité du dossier et son contenu pour des raisons évidentes ; - la destination, contenue dans une variable.
Publication du blog de dev
La différence est négligeable entre la publication de prod et celle de dev. En
pratique, on change juste l’endpoint S3 pour préciser le bucket de dev et non
pas celui de prod, et on remplace le only: master
en except: master
.