Introduction à Gitlab CI

2018-08-31 - Louis-Philippe Véronneau

Voici les notes de l'atelier d'introduction à Gitlab CI que je donne le 23 septembre dans le cadre de la Semaine québécoise de l'informatique libres.

Pourquoi faire de l'intégration continue?

Il y a plusieurs bonnes raisons pour commencer à faire de l'intégration continue (CI). En voici quelques unes:

  1. Tester son code. Souvent, l'intégration continue est une bonne manière de commencer à tester son code. Les tests automatisés permettent de trouver des erreurs d'inattention et forcent à vérifier si le code que l'on écrit fonctionne réellement.

  2. Tester son code sur des environnements radicalement différents. Avoir des tests que l'on peut faire rouler localement sur sa machine est une bonne chose, mais utiliser un CI permet de tester son code sur des environnements sur lesquels on ne travaille pas.

  3. Tester son code automatiquement. Utiliser un CI lié à une plateforme d'hébergement git permet de s'assurer que les nouveaux commits réussissent les tests avant d'être ajoutés à la branche master.

  4. Déployer son code automatiquement. Il est possible d'aller une étape plus loin et d'utiliser le CI pour publier du code sur une archive comme PyPi ou NPM automatiquement si tous les tests passent. On parle alors de déploiement continu (CD).

Introductions aux fonctionnalités de Gitlab CI

Aperçu

Pour utiliser Gitlab CI, il faut tout d'abord avoir un projet sur une instance Gitlab. Quand on ajoute un commit git au dépôt, si un fichier .gitlab-ci.yml est présent dans le dépôt, Gitlab suit ces instructions et roule un script dans le runner, une machine externe qui sert à faire des tests.

Sur la plupart des instances Gitlab, il existe des runners partagés que tout le monde peut utiliser. Il est également possible de monter un runner privé pour son propre projet. Cela peut être intéressant si on a des tests qui demandent beaucoup de ressources.

Le fichier .gitlab-ci.yml

Pour commencer à utiliser Gitlab CI, il faut tout d'abord créer un fichier .gitlab-ci.yml.

Voici un exemple très simple de fichier:

---
image: debian:stable

test12345:
  script:
  - echo 'deb http://deb.debian.org/debian buster main contrib non-free' >> /etc/apt/sources.list
  - apt-get update
  - apt-get install -y -t buster rolldice
...

La première partie du fichier est le paramètre image. C'est l'image docker qui va être utilisée par défaut dans les tests que l'on effectue. Cette image est récupérée du Docker Hub. Pour utiliser une image, on spécifie le tag complet de l'image. Ainsi, l'image pour la version stable de Debian est debian:stable.

La seconde partie du fichier est le test que l'on roule. Il est possible d'avoir plusieurs tests l'un à la suite de l'autre. Le test dans notre exemple est nommé test12345, mais il aurait été possible de le nommer d'une autre manière. apt:rolldice aurait également été un nom valide.

Chaque test doit comporter un script. Ce script est un ensemble de commandes shell de type bash. L'ensemble des commandes que l'on souhaite effectuer s'y retrouvent étapes par étapes.

Il est possible d'avoir plusieurs tests l'un à la suite de l'autre:

---
image: debian:stable

apt:rolldice:stable:
  script:
  - apt-get install -y rolldice

apt:rolldice:buster:
  script:
  - echo 'deb http://deb.debian.org/debian buster main contrib non-free' >> /etc/apt/sources.list
  - apt-get update
  - apt-get install -y -t buster rolldice
...

Il est également possible de changer l'image par défaut utilisée dans un test:

---
image: debian:stable

apt:rolldice:stable:
  script:
  - apt-get install -y rolldice

apt:rolldice:buster:
  image: debian:buster
  script:
  - apt-get install -y rolldice
...

Les fichiers présents dans votre dépôt git sont accessibles par vos scripts comme si vous travailliez directement dans ce dossier:

> foobar.txt

Hello World

> .gitlab-ci.yml

---
image: debian:stable

test:hello:
  script:
  - cat foobar.txt
  - cat foobar.txt | grep "Hello World"
...

Le paramètre service

Pour certains tests plus complexes, il est parfois nécessaire d'avoir un service externe différent de notre image docker principale, par exemple une base de données.

Gitlab CI permet de rouler un service externe accessible par nos tests. Ces services sont également des images Docker. Par voici un exemple simple d'utilisation d'une base de données:

> mytest.sql

CREATE TABLE `table1` (randomvar VARCHAR(30) NOT NULL);
CREATE TABLE `table2` (randomvar VARCHAR(30) NOT NULL);
CREATE TABLE `table3` (randomvar VARCHAR(30) NOT NULL);
CREATE TABLE `table4` (randomvar VARCHAR(30) NOT NULL);

> .gitlab-ci.yml

---
image: debian:stable

services:
  - mariadb:10.1

variables:
  MYSQL_DATABASE: mytest
  MYSQL_ALLOW_EMPTY_PASSWORD: 1

test:mariadb:
  script:
  - apt-get update && apt-get -y install mariadb-client-10.1
  - mariadb -u root -h mariadb -D mytest < mytest.sql
  - mariadb -u root -h mariadb -e "SELECT * FROM information_schema.columns WHERE table_schema = 'mytest'";
...

N'importe quelle image Docker peut être utilisée comme service. Si vous avez des besoins particuliers, il est nécessaire de créer une image Docker par vous même.

Artifacts

Si vous souhaitez utiliser Gitlab CI pour compiler du code ou créer des fichiers que vous souhaitez conserver par la suite, il est nécessaire d'utiliser le paramètre artifacts. Sa fonction est de spécifier un fichier ou un dossier qui va être enregistré par Gitlab et que vous allez pouvoir récupérer par la suite.

> input.md

# Hello World

This is a markdown test. It should be rendered to HTML properly using the great
[pandoc](https://pandoc.org/MANUAL.html).

> .gitlab-ci.yml

---
image: debian:stable

build:html:
  script:
  - apt-get update && apt-get -y install pandoc
  - pandoc -o output.html input.md
  artifacts:
    paths:
    - output.html
...

Tests à plusieurs étapes

Par défaut, les tests dans Gitlab CI fonctionnent tous simultanément. Cela est pratique pour des tests de type unit test. Pour des tests plus élaborés, on peut parfois vouloir fonctionner par étapes et créer des dépendances entre nos tests.

Le paramètre stage permet de spécifier des groupes dans lesquels on peut placer nos différentes tâches. Ces groupes peuvent par la suite être ordonnés, pour que les tests roulent dans un ordre précis.

On peut également utiliser le paramètre dependencies pour créer un lien de dépendance entre différentes tâches. Les artifacts créés dans une tâches sont automatiquement passés à une seconde tâche si elle en dépend.

> hello.c

#include 

int
main (void)
{
  printf ("Hello, world!\n");
  return 0;
}

> .gitlab-ci.yml

---
stages:
  - build
  - test

compile:debian:
  image: debian:stable
  stage: build
  script:
  - apt-get update && apt install -y build-essential
  - gcc -Wall hello.c -o hello
  artifacts:
    paths:
    - hello

compile:alpine:
  image: alpine
  stage: build
  script:
  - apk add --update build-base
  - gcc -Wall hello.c -o hello
  artifacts:
    paths:
    - hello

test:debian:
  image: debian:stable
  stage: test
  dependencies:
  - compile:debian
  script:
  - ./hello

test:alpine:
  image: alpine
  stage: test
  dependencies:
  - compile:alpine
  script:
  - ./hello
...

Variables secrètes

Si vous souhaitez utiliser Gitlab pour publier votre projet sur une plateforme comme PyPi ou NPM, il est nécessaire de stocker vos informations d'authentification quelque part.

Il n'est bien évidemment pas recommandé d'ajouter vos mots de passes directement dans votre fichier .gitlab-ci.yml ou dans un autre fichier dans votre dépôt git pour des raisons de sécurité: n'importe qui pourrait lire ces fichiers et compromettre vos comptes.

Gitlab permet de stocker des variables secrètes. Pour ce faire, il faut aller dans l'onglet Settings > CI/CD > Variables de votre projet.

Une fois que vous avez créé une variable secrète, il est possible d'y référer comme une variable normale dans votre fichier .gitlab-ci.yml.

---
image: debian:stable

test:secret-var:
  script:
  - apt-get update && apt-get -y install wget
  - wget https://agendadulibre.qc.ca/events/$SECRET
...

Compiler et héberger un site web statique à l'aide de Gitlab CI et Gitlab Pages

Un cas de figure intéressant pour Gitlab CI est la compilation d'un site web statique. Une fois compilé, ce site web peut par la suite être hébergé directement sur Gitlab grâce au projet Gitlab Pages. Cela peut être utile pour héberger la documentation d'un projet.

Voici un exemple de projet utilisant Gitlab CI et Gitlab Pages. Ce projet utilise Sphinx avec le thème Read the Docs. Le site web compilé est disponible en ligne ici.

Une fois que l'on a ajouté un fichier .gitlab-ci.yml qui compile notre site statique, il faut aller dans l'onglet Settings > Pages de notre projet git pour mettre en ligne notre projet. Gitlab offre également la possibilité d'utiliser un domaine externe.

Plus d'informations

La documentation complète du fichier .gitlab-ci.yml est disponible en ligne.

Atelier collaboratif d'utilisation de Gitlab CI

Trouvez un projet qui vous intéresse et tentez de créer une série de tests ầ l'aide de Gitlab CI.

Instructions pour installer Gitlab CI + Docker sur une machine privée (avancé)

On souhaite installer Docker et Gitlab CI sur un serveur Debian Stretch. Ces étapes nécessitent une certaine connaissance de la ligne de commande.

Docker

L'executor Docker est le plus simple et le plus flexible. C'est également celui qui est mis en place sur gitlab.com.

On commence donc par installer Docker sur notre machine. En temps normal, on installerait Docker à partir du dépôt officiel Debian. Malheureusement, Docker n'est pas packagé dans Debian alors il est nécessaire de l'installer à partir du dépôt offert par Docker.

On commence par ajouter un fichier source à apt:

> /etc/apt/sources.list.d/docker.sources

Types: deb
URIs: https://download.docker.com/linux/debian/
Suites: stretch
Architectures: amd64
Components: main
Signed-By: /usr/share/keyrings/docker-archive-keyring.gpg

On complète l'installation en téléchargeant la clef GPG et en installant les packets1:

$ apt install apt-transport-https
$ curl https://download.docker.com/linux/debian/gpg | gpg --dearmor > /usr/share/keyrings/docker-archive-keyring.gpg
$ apt update
$ apt install docker-ce

Docker a la fâcheuse habitude de démarrer avant que l'interface réseau ne soit en ligne. Cela fait en sorte que quand notre machine redémarre, notre CI n'a plus accès à internet, ce qui fait avorter nos tests.

Pour remédier à ce problème, on créé un fichier cron qui redémarre Docker 30 secondes après que notre machine ait démarrée:

$ sudo echo '@reboot root /bin/sleep 30 && /bin/systemctl restart docker' > /etc/cron.d/docker-reboot

On souhaite également éviter que notre serveur se remplisse des instances de test que l'on fait rouler. On ajoute donc un autre fichier cron pour purger Docker périodiquement:

$ sudo echo '0 3 * * * root /usr/bin/docker system prune -a -f > /dev/null 2>&1' > /etc/cron.d/docker-prune

Et voilà! Docker est prêt à être utilisé dans notre installation future de Gitlab CI!

Gitlab CI

Encore une fois, plutôt que d'utiliser le dépôt Gitlab offert par Debian pour installer Gitlab CI, on choisi plutôt le dépôt offert par Gitlab.

Gitlab est encore très jeune dans Debian et il y de nombreuses mises à jour de sécurité. Peut-être dans le futur les paquets Debian vont être plus fiables, mais ce n'est pas le cas pour l'instant.

On commence par ajouter un fichier source à apt:

> /etc/apt/sources.list.d/gitlab.sources

Types: deb
URIs: https://packages.gitlab.com/runner/gitlab-runner/debian/
Suites: stretch
Architectures: amd64
Components: main
Signed-By: /usr/share/keyrings/gitlab-archive-keyring.gpg

On complète l'installation en téléchargeant la clef GPG et en installant les packets:

$ curl https://packages.gitlab.com/gpg.key | gpg --dearmor > /usr/share/keyrings/gitlab-archive-keyring.gpg
$ apt update
$ apt install gitlab-runner

Une fois gitlab-runner installé, on peut utiliser l'outil en ligne de commande pour créer un nouveau runner:

$ sudo gitlab-runner register
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
> https://url-de-l-instance-gitlab
Please enter the gitlab-ci token for this runner:
> le-token-de-votre-projet                 
Please enter the gitlab-ci description for this runner:
> La description du runner
Please enter the gitlab-ci tags for this runner (comma separated):
> nom-du-runner-gitlab-ci, docker

Pour obtenir le token nécessaire à la création du runner, il faut aller dans l'onglet Settings > CI/CD > Runners dans votre projet git. Le token est spécifié dans la section Setup a specific Runner manually.

Et voilà! Si tout fonctionne correctement, le runner devrait s'afficher dans la l'onglet Settings > CI/CD > Runners de votre projet.

Pro Tip

Si vous utilisez votre propre runner Gitlab CI dans un environnement en production, il est important de s'assurer que ce dernier fonctionne pour s'éviter de fâcheux ennuis.

Pour s'assurer que notre runner est fonctionnel, on peut créer un projet git dont le seul but est de s'assurer que le CI fonctionne. Ce dernier n'a qu'à contenir un fichier .gitlab-ci.yml de base.

Par la suite, vous pouvez faire rouler le test à tous les jours ou à toutes les heures en créant un nouvel item dans dans l'onglet CI/CD > Schedules de votre projet.

Voici un exemple de fichier gitlab-ci.yml que l'on peut utiliser:

---
image: debian

test-ci:
  script:
  - apt-get update && apt-get -y install rolldice
  tags:
    - nom-du-runner-gitlab-ci
...

Il est bien important de spécifier le nom du runner sur lequel vous souhaitez que le test roule dans la section tags pour éviter de faire le test sur une autre machine que la vôtre.


  1. Cette méthode est bien plus sécuritaire qu'utiliser apt-key add car elle elle autorise la clef GPG externe seulement pour la source que l'on souhaite ajouter et non pour toutes les autres sources. Plus de détails sur cette méthode ici


gitlab-cidebianhowto