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

logo gitlab

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.

Cyril `zarak` Duval
Cyril `zarak` Duval
Étudiant

Étudiant à EPITA de la promo 2020. Membre du CRI de janvier 2018 à janvier 2020. Root CRI + Assistant (YAKA / ACU) à EPITA.

Suivant