Commencer simplement avec GitLabCI

jphi_baconnais

Jean-Phi Baconnais

Posted on April 24, 2020

Commencer simplement avec GitLabCI

Tout le monde connait GitLab, et beaucoup de monde connait son outil d'intégration et de déploiement continus : GitLabCI 🦊.

Avec l'arrivée d'une application ne possédant aucun outil de CI/CD dans notre équipe, nous avons pu manipuler GitLabCI. C'est d'ailleurs le sujet d'une présentation sur laquelle nous avons travaillé avec un collègue (Jean-Baptiste Martin pour ne pas le citer 🙂).

C'est cette approche que je vais vous présenter dans cet article.

Sommaire

1 - Création d'un pipeline
2 - Notre premier job
3 - Le registry
4 - Les environnements
5 - GitLab Pages
6 - Kubernetes
The (happy) end

🛠 Création d'un pipeline

Pour créer un pipeline GitLabCI, c'est tout simple, il suffit de créer un fichier .gitlab-ci.yml à la racine de votre projet.

Les pipelines sont disponibles via le menu "CI/CD" > "Pipelines".

Notre premier pipeline

Un pipeline est un élément assez simple, ayant un status : in progress, pending, passed et (je ne vous le souhaite pas) failed.

En consultant un pipeline, nous pouvons voir sa structuration, il est constitué de 1 ou plusieurs jobs, organisés dans différentes colonnes. Tout cela se configure dans le fameux fichier .gitlab-ci.yml.

Je vous entend déjà raler en voyant l'extension yml, mais rassurez vous, GitLab a mis un outil CI Lint qui vous permet de valider vos pipelines. Utile pour éviter de voir vos pipelines planter car le yaml est invalide ! Il est disponible dans la page des pipelines :

Structuration du fichier .gitlab-ci.yml

Le fichier .gitlab-ci.yml commence généralement par les définitions de nos "colonnes". Il s'agit de stage. Un pipeline peut contenir autant de stage qu'il nous semble utile. Les différents traitements se feront de manière séquentielle.
Dans cet exemple il y a 5 étapes dans la construction de notre projet :

stages:
  - build_and_push
  - deploy_tis
  - deploy_re7
  - notif_prepa_liv_prod
  - deploy_prod
Enter fullscreen mode Exit fullscreen mode
  • La première étape permet de builder une image docker de notre projet et le pusher sur un repository.
  • La seconde deploie notre composant sur un environnement de fabrication.
  • La troisième sur un environnement de recette.
  • la quatrième envoie une notification dans slack qu'une livraison en production est en prévision.
  • La dernière déploie notre application en production.

Voila graphiquement à quoi cela ressemble :

Pipeline

L'enchainement des différents stages se fait automatiquement si tous les jobs se font sans erreur. Dans l'exemple, il a été (heureusement) mis en place un système d'exécution manuelle. Notre application n'ayant très peu de tests automatisé, il est préférable de faire quelques tests avant d'envoyer une nouvelle version en production 🤭. Il est donc possible de spécifier qu'un stage est à exécuter uniquement après une action manuelle : when: manual
Ce qui visuellement dans notre pipeline est repérable grâce à la flèche noire en haut du stage :

Comment s'exécute le pipeline ?

Sur chaque étape, il est possible de mentionner la branche qui va enclencher notre pipeline.

 only:
    - dev
    - master
Enter fullscreen mode Exit fullscreen mode

Dans cet exemple, dès qu'un push est effectué sur la branche dev et master, le pipeline définit va s'exécuter.

Afin d'optimiser le temps de traitement de notre pipeline, il est possible (voire conseillé) de paralléliser les traitements. Pour cela plusieurs jobs peuvent être associés à un stage. Dans cet exemple, tous les jobs de build de composants sont réalisés en parallèle.

Au niveau du fichier .gitlab-ci.yml cela se traduit de cette façon :

  • 1 stage
stages:
       - build
Enter fullscreen mode Exit fullscreen mode
  • chaque job reçoit la configuration suivante :
📦 front :
       stage: build

📦 api_go :
       stage: build

📦 node :
       stage: build
Enter fullscreen mode Exit fullscreen mode

NB : Un pipeline peut également être programmé via la partie "Schedule". Accessible via le menu, un pipeline peut être programmé.

🛠 Les jobs

Notre premier job

Un job est ce qui va permettre de réaliser une ou plusieurs tâches. Le job le plus simple doit être composé d'une action dans une partie script :

job:
  script : 
     - echo "Voici mon premier job"
Enter fullscreen mode Exit fullscreen mode

Il existe d'autres mots clés qui vont nous permettre de faire des actions plus complexes qu'un simple echo 😁

L'attribut stage

Tout d'abord un stage peut lui être attribué, permettant ainsi de classifier nos jobs comme montré dans le paragraphe ci-dessus. Si on définit 2 stages tels que :

stages:
  - first_stage
  - second_stage
Enter fullscreen mode Exit fullscreen mode

Nous pourrions avoir plusieurs jobs définis tels que :

job1:
  stage: first_stage
  script : 
     - echo "Voici mon premier job"
job2:
  stage: second_stage
  script : 
     - echo "Voici mon second job"
Enter fullscreen mode Exit fullscreen mode

et nous pourrions avoir plusieurs jobs associés au même stage :

job1:
  stage: first_stage
  script : 
     - echo "Voici mon premier job"
job2:
  stage: second_stage
  script : 
     - echo "Voici mon second job"
job3:
  stage: first_stage
  script : 
     - echo "Voici mon troisième job exécuté lors du premier stage"
Enter fullscreen mode Exit fullscreen mode

L'attribut image

Il y a ensuite le mot clé image. Il permet d'effectuer une action à l'aide d'image docker. Il peut être positionné pour l'ensemble des jobs dans la première ligne du fichier .gitlab-ci.yml, ou pour chaque job.

Commun à tous les jobs :

image: alpine

job1:
  stage: first_stage
  script : 
     - echo "Voici mon premier job"
job2:
  stage: second_stage
  script : 
     - echo "Voici mon second job"

Enter fullscreen mode Exit fullscreen mode

ou pour chaque job :

job1:
  image: alpine
  stage: first_stage
  script : 
     - echo "Voici mon premier job"
job2:
  image: alpine
  stage: second_stage
  script : 
     - echo "Voici mon second job"

Enter fullscreen mode Exit fullscreen mode

Si on a notre pipeline qui contient la plupart de job qui utilisent une image, il vaut mieux spécifier l'image comme globale à notre pipeline, puis à surcharger les jobs avec des images spécifiques :

image: alpine

job1: # utilisera l'image de base alpine
  script : 
     - echo "Voici mon premier job"
job2:
  image: seconde_image_docker # ce job spécifie l'image qu'il a besoin pour effectuer une action spécifique.
  stage: second_stage
  script : 
     - echo "Voici mon second job"

Enter fullscreen mode Exit fullscreen mode

En choisissant l'image, on peut imaginer par exemple lancer des commandes maven pour pouvoir exécuter les tests et poursuivre le pipeline si tous les tests sont passés.

test:
  stage: test
  script:
    - mvn clean verify
Enter fullscreen mode Exit fullscreen mode

L'attribut only

L'attribut only est simple à comprendre, il permet d'éxécuter votre job seulement sur une branche ou une typologie de branche. Par exemple un déclenchement de job sur la branche master s'écrit comme ceci :

  only:
    - master
Enter fullscreen mode Exit fullscreen mode

On peut également définir une liste de branches :

  only:
    - dev
    - master
Enter fullscreen mode Exit fullscreen mode

ou un pattern de branches :

  only:
    - features*
Enter fullscreen mode Exit fullscreen mode

L'attribut when

L'attribut when permet de conditionné l'exécution d'un job. Cinq valeurs sont disponibles et permettent de conditionner l'exécution d'un job. Elles sont assez parlantes : on_success, on_failure, always, manual et la dernière arrivée manual.

Pour plus de détail sur les différents états, se référer à la doc GitLab https://docs.GitLab.com/ee/ci/yaml/#when

La première utilisation que j'ai utilisée, c'est celle spécifiée précédemment, c'est le manual. En effet, avec cette description, ce job ne s'exécutera qu'après intervention manuelle d'un utilisateur :

job1: 
  image: alpine
  stage: second_stage
  script : 
     - echo "Voici mon second job"
  when: manual
Enter fullscreen mode Exit fullscreen mode

L'attribut artifact

Un artifact est un moyen de récupérer des fichiers générés par un job. Cela peut servir à récupérer un rapport de test par exemple. Afin de ne pas trop surcharger les infrastructures et ne garder indéfiniement des fichiers, la notion d'expiration est possible.

expire_in: 1 week
Enter fullscreen mode Exit fullscreen mode

Par exemple, pour ce site réalisé avec l'outil gohugo, j'exécute dans un job la commande hugo qui permet de générer le site statique. Tous les fichiers vont dans le répertoire public :

  artifacts:
    paths:
    - public

Enter fullscreen mode Exit fullscreen mode

Sous l'interface du pipeline il est possible de récupérer les sources :

Artefact

L'attribut variables

Dans un pipeline il est possible de créer des variables propres à un job :

  variables:
    DATA: "{'text':'une donnée json que vous voulez utilisez plus tard'}"
Enter fullscreen mode Exit fullscreen mode

Cette variable pourra être simplement utilisée dans la partie script :

script:  
    - curl -X POST -H "${CONTENT}"  --data "${DATA}" "${WEBHOOK_LIVRAISON}"
Enter fullscreen mode Exit fullscreen mode

Pour intégrer et réutiliser une variable dans plusieurs jobs, c'est exactement la même chose mais à l'extérieur d'un job 😉.

Il existe encore un niveau au dessus en terme de variable, notamment utilisé pour stocker les mots de passe qui ne doivent pas se trouver dans votre code source ! Dans le menu "Settings" > "CI / CD" plusieurs options sont disponibles, dont les variables.

Dans cet exemple on voit deux variables créées et masquées qui sont utilisables dans le pipeline comme une variable créée dans le fichier .gitlab-ci.yml.

Registry images

GitLab met également des variables prédéfinies, comme l'identifiant du pipeline CI_PIPELINE_ID, le user CI_REGISTRY_USER, le message du commit utilisé CI_COMMIT_MESSAGE, des informations sur l'utilisateur exécutant le pipeline, etc.

Plus de détail sur cette page : https://docs.gitlab.com/ee/ci/variables/predefined_variables.html

L'attribut tags

L'attribut tag permet de sélectionner un Runner, un quoi ? 🤔

Rapidement un runner est ce qui permet d'interpréter les fichiers .gitlab-ci.yml. Si vous utilisez l'instance publique GitLab.com, des runners sont configurés et sont ceux utilisés par défaut, ce sont les Shared runner, les runners partagés avec tout le monde. Si on est administrateur de son instance GitLab privée, on peut s'amuser à créer des Specified Runner, c'est-à-dire des runners spécifiés à un projet ou un groupe de projet. Cela permet de partager les ressources machines en fonction des projets ou groupement de projet.

Le registry

Le container registry de GitLab permet de stocker des images dans votre projet, que ce soit via GitLabCI ou même manuellement en ligne de commande ou par d'autres outils. Simple à comprendre et à utiliser !

C'est disponible dans le menu "Packages" > "Container Registry".

Il permet même de stocker plusieurs niveaux. Par exemple dans ce projet je stocke 4 images pour lesquelles je peux déployer plusieurs tags.

Registry images

Registry images

Vos images seront accessibles facilement avec un

docker pull registry.gitlab.com/jeanphi-baconnais/arbregen/front:master_133030344
Enter fullscreen mode Exit fullscreen mode

On peut ainsi imaginer un job qui build l'image docker de votre composant via :

🐳  build_and_deploy: 
  stage: build
  image: docker:stable
  services:
    - docker:dind
  script:
    - docker login -u GitLabci -p "$CI_SECRET"  registry.GitLab.com
    - docker build -t <your image name> .
    - docker push <your image name>:latest
Enter fullscreen mode Exit fullscreen mode

Cette tache est encore plus facile avec l'utilisation de l'outil Kaniko (https://github.com/GoogleContainerTools/kaniko) de Google :

🐳 build_push_image_docker:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE
Enter fullscreen mode Exit fullscreen mode

Kaniko s'occupe de builder l'image à partir d'un dockerfile et de le pusher dans le repository renseigné.

Les environnements

La partie environnement n'est pas un élément que j'ai approfondi à fond. Cela permet de déclarer des environnements, et de les voir dans la partie environnement disponible dans le menu de GitLab. Un environnement peut être nommé, une url peut y être affectée et cela alimenté automatiquement le menu environnement.

Environnements

En configurant un cluster kubernetes, il est possible de consulter directement les métriques, avoir accès à un terminal ou alors rejouer votre stage.

Environnements

GitLab Pages

GitLab met à disposition un outil bien pratique, GitLab Pages, qui permet d'héberger gratuitement un site basé sur votre repository. C'est exactement avec cet outil que j'ai pu avoir mon site perso http://jeanphi-baconnais.gitlab.io.

Lors de la création de votre projet, il est possible de partir de templates, et ceux préfixés par "Pages" permettent de générer un projet possédant directement la configuration nécessaire pour vous permettre d'avoir un site disponible.

Le nom du projet doit contenir votre user.gitlab.io. Contrainte imposée par GitLab.

Create project

Pour mon site, je me base sur le framework GoHugo (cf https://gohugo.io/).

Le fichier .gitlab-ci.yml généré est le suivant :

image: registry.GitLab.com/pages/hugo:latest

pages:
  script:
  - hugo
  artifacts:
    paths:
    - public
  only:
    refs:
    - master

Enter fullscreen mode Exit fullscreen mode

La commande hugo permettant de builder le site est exécutée et produit le résultat dans l'artifact "public", le site buildé est mis à disposition et votre site est opérationnel 😎.

Si votre site n'est pas disponible, vous pouvez vérifier la configuration dans le menu "Settings" > "Pages".

L'url de votre page doit être affichée telle que :

Conf Pages

Kubernetes

Il est possible d'intégrer (facilement) des cluster kubernetes à GitLab. Après avoir créé son compte sur Google Kubernetes Engine ou Amazon, GitLab va permettre via le menu "Operations" > "Kubernetes" de créer ou déclarer un cluster Kubernetes sur l'une des deux plateformes. Mon exemple concerne l'infrastructure Google.

Kubernetes

Après avoir un cluster, plusieurs outils sont intégrables via l'onglet "Applications", comme Helm, Ingress, Prometheus, etc.

Kubernetes
Kubernetes

On peut même y voir l'état de santé de notre cluster.

Kubernetes

Pour déployer une application via GitLabCI, c'est très simple, cela se passe toujours dans le .gitlab-ci.yml et via l'image google/cloud-sdk, l'outil kubectl est disponible et permet donc de déclarer des déploiements, services en ligne de commande ou bien via un fichier yaml. C'est également possible avec Helm si vous avez bien activé l'application auparavant.

🐋 deploy_gcp :
  stage: deploy
  image: google/cloud-sdk
  script:
    - kubectl apply -f k8s/hello-world.yaml
Enter fullscreen mode Exit fullscreen mode

The (happy) end

L'approche GitLabCI est très simple à prendre en main, par contre, il faut l'avouer qu'au départ, c'était assez sportif de construire nos pipelines.
Dès qu'un nouveau job était en construction, il plante souvent ... J'ajoute une ligne dans mon job, je git add, git commit, git push, regarde mon pipeline tourner, puis planter. Je modifiais le pipeline, git add, commit, push, regardait mon pipeline tourner, puis planter, etc.
Même avec l'utilisation du Web IDE, ça facilitait les opérations git, mais les statistiques de nos pipelines n'étaient pas dingues. Sur le premier projet de nos tests avec GitLabCI, 40% de tests passés avec succès !

Les GitLab Runner permettent à GitLab de constuire vos pipelines, et bien l'outil GitLab-Runner peut être installé sur vos machines : https://docs.gitlab.com/runner/install/

Cela permet de gagner un temps fou, et d'améliorer vos statistiques de pipeline si votre chef préféré vous espionne 😅

Si vous avez un pipeline constitué de plusieurs jobs, il est même possible d'exécuter un seul job. Par exemple si vous avez un job 'job1' déclaré dans votre .gitlab-ci.yml, vous pourrez l'exécuter avec les process de votre machine via un

gitlab-runner exec docker job1

.

Je ne l'ai pas encore dit, mais vos runner peuvent utiliser plusieurs types d'executor : docker (le seul que j'ai utilisé), shell, sh, virtual box et même kubernetes (que je découvre en écrivant cet article). Je vous laisse lire la documentation si vous voulez approfondir ce point : https://docs.gitlab.com/runner/executors/.

Avec toutes ces notions, nous avons pu automatiser nos pipelines de CI/CD pour automatiser les builds d'applications, les installer sur des machines distantes, informer via Slack des livraisons effectuées avec le commit, bref nous faire gagner en temps et en confort 😎 Même l'intégration sur un cluster Kubernetes est simple et rapide à mettre en place.

La documentation de GitLab est très bien faite et est en constante évolution : https://docs.gitlab.com/ee/ci/yaml/.

Merci de votre lecture et bons pipelines !

💖 💪 🙅 🚩
jphi_baconnais
Jean-Phi Baconnais

Posted on April 24, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related