Journée X/Stra-Dev Intégration continue

21 novembre 2017


L'intégration continue avec GitLab

image.png

Session pratique


Source du projet

Matthieu Boileau - matthieu.boileau@math.unistra.fr

Contenu sous licence CC BY-SA 4.0

GitLab CI

GitLab CI propose une chaîne complète d'intégration continue intégrée à son système de forge logicielle.

Les avantages :

  • la simplicité de mise en oeuvre
  • la robustesse d'une solution intégrée

Le gitlab runner

Introduction

  • gitlab-runner est un démon installé sur une machine destinée à exécuter les tâches d'intégration continue.
  • Pour chaque runner enregistré, gitlab-runner écoute l'instance de gitlab correspondante dans l'attente d'une tâche
  • Cette tâche est déclenchée par des évènements (push, merge, etc.) sur le projet avec lequel il est lié
  • Ce lien est créé à l'enregistrement du runner par un token associé au projet.

Doc officielle

Runners : shared vs specific

  • shared : n'importe quel projet peut s'en servir. Seul l'administrateur du GitLab peut enregistrer des runners partagés.
  • specific : seul le créateur du runner peut s'en servir dans d'autres projets

Attention : les runners partagés sont pratiques mais peuvent présenter des problèmes de sécurité.

Runners : locked vs unlocked runners

  • locked : le runner ne peut être utilisé que pour le projet avec lequel il est lié
  • unlocked : le créateur du runner peut l'utiliser dans ses autres projets

Installer gitlab-runner

La procédure dépend du système d'exploitation.

Récupérer le matériel du TP

Deux solutions :

  • Option 1 : si vous avez un compte sur https://git.unistra.fr, faire un fork du projet
  • Option 2 : importer le projet dans une autre instance GitLab (par exemple GitLab.com).

Option 1 : fork du TP

Faire un fork du projet sur votre compte GitLab Unistra en allant sur la page d'accueil du projet ou en suivant ce lien.

Option 2 : Import du projet

  1. Menu New Project
  2. Onglet Import project
  3. Import project from
  4. Dans le champ Git repository URL, entrez https://git.unistra.fr/xstra-dev/tp-gitlab-ci
  5. Create Project

Clone du TP

  • Pour paramétrer les commandes de ce notebook, positionner les variables d'environnement suivantes :
In [ ]:
export USERNAME="xstra-dev"  # votre login sur git.unistra.fr
export TPBASEDIR=$HOME       # le répertoire où vous souhaitez installer le TP
export TPDIR=$TPBASEDIR/tp-gitlab-ci
  • Cloner localement votre projet
In [ ]:
cd $TPBASEDIR
git clone git@git.unistra.fr:$USERNAME/tp-gitlab-ci.git

Enregistrer un nouveau runner

Si vous ne souhaitez pas installer et configurer gitlab-runner, rdv Exo1.

Repérer l'URL et le token d'enregistrement

Engistrer un runner consiste à créer un nouveau runner associé à un projet. Si vous n'êtes pas administrateur du GitLab, il s'agit forcément d'un runner spécifique.

Allez chercher l'URL et le token du projet dans

Settings > CI / CD > Runners settings

Enregistrement en mode non interactif

On positionne des variables d'environnement pour gitlab-runner

In [ ]:
export CI_SERVER_URL=https://git.unistra.fr/  # Ne pas changer pour cette session pratique
export RUNNER_NAME=a_runner_for_$USERNAME
export REGISTRATION_TOKEN=xxx  # Remplacez par la valeur indiquée sur la page de paramètres du runner
export REGISTER_NON_INTERACTIVE=true  # nécessaire pour le mode script

Si vous êtes sous MacOS, vous pouvez maintenant enregistrer votre runner en décommentant la ligne suivante :

In [ ]:
#gitlab-runner register --executor shell

Si vous êtes sous Linux, vous pouvez copier les lignes de variables d'environnement dans un terminal et exécuter :

sudo gitlab-runner register --executor shell

Enregistrement en mode interactif

Ou bien lancez la commande en mode interactif et compléter le prompt en suivant la vidéo ci-dessous :

gitlab-runner register       # sous MacOS
sudo gitlab-runner register  # sous Linux

Cette procédure est décrite pas à pas dans la documentation officielle.

Editer les paramètres du runner

  • Retournez sur la page de paramètrage des runners
  • Repérez votre nouveau runner
  • Editez-le et ajouter les tags tp-gitlab-ci, shell s'ils ne sont pas déjà présents

Votre runner est maintenant prêt à travailler. Nous allons le tester en lui soumettant une tâche GitLab CI déclenchée par un git push sur votre projet.

Exo1 : helloworld en bash

Mettre en place une action d'intégration continue

Allez dans le répertoire racine du clone local de votre projet et basculez sur la branche exo1

In [ ]:
cd $TPDIR
In [ ]:
git pull
git stash
git checkout exo1
git checkout exo1-start .gitlab-ci.yml

Repérer le fichier .gitlab-ci.yml

In [ ]:
ls -al

L'éditer avec l'éditeur de votre choix

In [ ]:
cat .gitlab-ci.yml

Le fichier .gitlab-ci.yml (format YAML) décrit l'intégralité des tâches d'intégration continue avec GitLab CI. Dans cet exemple :

  • helloworld indique le nom du job d'intégration continue
  • tags permet de sélectionner des runners sur la base de mot-clés
  • scripts correspond aux lignes de commande bash que vous souhaitez exécuter

Si vous souhaitez utiliser un runner partagé

Dans la rubrique :

Settings > CI / CD > Runners settings

Notez la présence de plusieurs runners partagés dont clu1-tp-gitlab-ci qui portent les tags shell et docker mais pas tp-gitlab-ci. Pour cibler ce runner, retirer le tag tp-gitlab-ci du fichier .gitlab-ci.yml

Déclenchez une action d'intégration continue

Modifiez la ligne de script, par exemple:

In [ ]:
echo '    - echo "bonjour, monde"' >> .gitlab-ci.yml
cat .gitlab-ci.yml

Enregistrez avec git

In [ ]:
git add .gitlab-ci.yml
git commit -m "Update .gitlab-ci.yml"

Poussez vos modifications

In [ ]:
git push

Sur GitLab, observez l'exécution de votre job dans la rubrique de votre projet :

CI / CD > Pipelines

Provoquez une erreur

Dans .gitlab-ci.yml, introduisez une erreur dans une des lignes du script et observer l'effet dans CI / CD > Pipelines après avoir poussé les modifications.

Note : les fichiers peuvent s'éditer directement dans GitLab.

Exo2 : helloworld en C

Description

On écrit le programme helloworld en C qui doit afficher hello, world.

Mettre en place une action de build

Dans votre clone local, basculez sur la branche exo2

In [ ]:
cd $TPDIR
git stash  # si vous avez des modifications non enregistrées dans exo1
git checkout exo2
git rm .gitlab-ci.yml  # Clean previous commits

Listez le contenu du répertoire

In [ ]:
ls -al

Vérifiez que le progamme compile

In [ ]:
make

On crée le fichier .gitlab-ci.yml avec une étape (stage) de build. Il doit contenir :

build_hello:
  stage: build
  tags:
    - shell
  script:
    - make
In [ ]:
cat > .gitlab-ci.yml <<- EOM
build_hello:
  stage: build
  tags:
    - shell
  script:
    - make
EOM

On vérifie le contenu

In [ ]:
cat .gitlab-ci.yml

On enregistre et on pousse.

In [ ]:
git add .gitlab-ci.yml
git commit -m "Add .gitlab-ci.yml with a build stage"
In [ ]:
git push

Sur GitLab, observez l'exécution de votre job dans la rubrique de votre projet :

CI / CD > Pipelines

Ajouter une action de test

On souhaite ajouter une étape de test réalisée avec make test. Tester l'exécution du test (il ne doit pas renvoyer d'erreur) :

In [ ]:
make test

Il faut ajouter le contenu suivant :

test_hello:
  stage: test
  tags:
    - shell
  script:
    - make test
In [ ]:
cat >> .gitlab-ci.yml <<- EOM

test_hello:
  stage: test
  tags:
    - shell
  script:
    - make test
EOM
In [ ]:
cat .gitlab-ci.yml

On enregistre, on pousse et on vérifie l'exécution de la chaîne d'intégration continue dans CI / CD > Pipelines

In [ ]:
git add .gitlab-ci.yml
git commit -m "Add a test stage to .gitlab-ci.yml"
In [ ]:
git push

Provoquez des erreurs

Modifier les fichiers pour provoquer des erreurs. Notez que le test n'est pas exécuté si le build échoue.

Exo3 : Docker pour gérer les dépendances

On veut mettre en place d'intégration continue d'un programme en python rosenbrock.py qui calcule la fonction de Rosenbrock. Pour que cette fonction soit efficace, nous allons la compiler avec pythran, un traducteur de python vers C++ qui permet d'accélérer le code python. Cet exercice a pour but d'illustrer la gestion des dépendances de compilation avec les conteneurs Docker dans une chaîne GitLab CI.

Pour le reproduire, il est nécessaire de :

  1. créer un compte sur DockerHub
  2. que Docker soit installé sur la machine qui héberge le runner (c'est le cas pour le runner partagé de ce TP).
  3. configurer un nouveau runner en reprenant la procédure avec les paramètres suivants :
    • tags : tp-gitlab-ci, docker-exec
    • executor: docker

Le dépôt d'images est gratuit sur DockerHub à condition que les images soient publiques. Pour héberger des images privées, il existe la solution du GitLab container registry, un service qui doit être activé par l'administrateur de l'instance de GitLab.

Dans votre clone local, basculez sur exo3

In [ ]:
cd $TPDIR
git stash  # si vous avez des modifications non enregistrées dans exo2
git checkout exo3

Entête et étape préliminaire du fichier .gitlab-ci.yml

stages:
  - build
  - test
  - release

variables:
  DOCKERHUB_USERNAME: boileaum
  CONTAINER_TEST_IMAGE: $DOCKERHUB_USERNAME/rosen:$CI_COMMIT_REF_NAME
  CONTAINER_RELEASE_IMAGE: $DOCKERHUB_USERNAME/rosen:latest
[...]

Remplacez boileaum par votre username sur DockerHub.

Etape de build :

[...]
b:docker:
  stage: build
  tags:
    - shell, docker
  script:
    - echo $DOCKERHUB_PASSWD | docker login -u $DOCKERHUB_USERNAME --password-stdin
    - docker build --pull -t $CONTAINER_TEST_IMAGE -f ./docker/Dockerfile-rosen .
    - docker push $CONTAINER_TEST_IMAGE
[...]
  1. La première ligne de script permet de s'authentifier sur DockerHub. Sur GitLab dans Settings > CI / CD > Secret variables, créez une variable DOCKERHUB_PASSWD qui contient le mot de passe de votre compte DockerHub.
  2. on construit l'image de test à partir du fichier ./docker/Dockerfile-rosen
  3. on pousse cette image sur DockerHub

Note : l'image construite avec ./docker/Dockerfile-rosen est basée sur l'image construite avec ./docker/Dockerfile-pythran et qui contient les dépendances nécessaires.

Une première étape de test

[...]
  t:rosen-py36:
    stage: test
    image: $CONTAINER_TEST_IMAGE
    tags:
      - tp-gitlab-ci
      - docker-exec
    script:
      - make clean && make
      - pytest -v
[...]

Avec le mot clé image, on indique à GitLab CI qu'on veut instancier un conteneur Docker. Les lignes de scripts vont alors être exécutées à l'intérieur de ce conteneur.

Une deuxième étape de test

[...]
t:rosen-py27:
  stage: test
  image: $CONTAINER_TEST_IMAGE
  tags:
    - tp-gitlab-ci
    - docker-exec
  script:
    - source /home/euler/py27/bin/activate 
    - make clean && make
    - pytest -v
[...]

Cette fois-ci, on veut tester l'exécution (et avant la construction) avec python 2.7. Pour ce faire, on utilise virtualenv tel qu'il est a été installé dans l'image boileaum/pythran:latest construite avec le fichier ./docker/Dockerfile-pythran.

Etape de livraison

[...]
r:docker:
  stage: release
  tags:
    - shell
    - docker
  script:
    - echo $DOCKERHUB_PASSWD | docker login -u $DOCKERHUB_USERNAME --password-stdin
    - docker pull $CONTAINER_TEST_IMAGE
    - docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE
    - docker push $CONTAINER_RELEASE_IMAGE
  only:
    - exo3

On ne fait que tirer l'image de test validée, la renommer, la pousser vers DockerHub. Notez le mot-clé only qui indique que cette étape ne sera exécutée que pour la branche git exo3. On évite ainsi de livrer des images produites par des branches de développement.

Pipeline complet

Comme le montre ce schéma, l'exécution de différents jobs d'une même étape peut être réalisée en parallèle par différents runners.

Plus d'information sur l'utilisation de docker avec GitLab CI dans la doc officielle.

Conclusion

Ce qu'on a vu

  • comment configurer un runner shell
  • comment cibler différents runners
  • comment mettre en place une chaîne d'intégration continue
  • comment tirer/construire/pousser des images Docker

Ce qu'on n'a pas vu

Beaucoup de réponses sont dans la doc officielle qui est très complète !