Organiser son pipeline Gitlab CI avec les templates

Organiser son pipeline Gitlab CI avec les templates


Lors de ma mission chez Maisons du Monde en tant que SRE, j'ai découvert une nouvelle façon d'organiser le code du pipeline GitLab.

Lorsqu'il y a plusieurs services à maintenir et à déployer, le code des pipelines des différents services est très souvent copié-collé. Ce n'est pas DRY (Don't Repeat Yourself).

Il est donc recommandé dans ce cas de mutualiser le code. Il existe plusieurs façons de procéder, mais nous nous attarderons dans cet article sur la façon suivante.

Un pipeline Gitlab CI

Prenons cet exemple de pipeline Gitlab CI. Il se lance au moment d'une merge-request et lors d'un merge dans la branche par défaut.

Le pipeline construit les dépendances, lance les tests et construit l'application.

workflow: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' image: golang:1.18 stages: - install - test - build install: stage: install script: - go mod download test: stage: test script: - mkdir report - go test -json -cover -coverprofile report/coverage.out > report/unittest.json artifacts: paths: - report dependencies: - install build: stage: build script: - mkdir -p build - go build -o build/awesome-app dependencies: - test artifacts: paths: - build/

Le concept du template off-the-shelf

Je pourrais mettre ce pipeline complet en tant que template et le réutiliser partout. Cependant, si une équipe veut gérer son pipeline différement, ou qu'elle veut ajouter des outils supplémentaires tel que gosec, elle ne pourra pas le faire simplement.

Le principe est de créer des templates pour chaque job. Ainsi l'équipe responsable du pipeline est libre de les utiliser ou non. Elle peut également en décider l'ordre. C'est le concept du template off-the-shelf (cela peut être traduit littéralement par "patron sur étagère" ou "livre sur étagère").

Créons un dépôt git avec tous les templates : gitlab-ci-library

L'organisation des dossiers est libre, mais il faut veiller à sa cohérence. Ce dépôt git va grandir au fur et à mesure des ajouts des templates pour les différents besoins.

code
  |- go
    |- install.yaml
    |- test.yaml
    |- build.yaml

Le dossier de niveau 1 sera le thème. Ensuite je spécifie le langage et enfin chaque fichier va contenir le template.

Répartissons le code des différents jobs dans chacun des fichiers correspondants.

# code/go/install.yaml .template:code:go:install: variables: GO_VERSION: 1.18 image: golang:$GO_VERSION script: - go mod download
# code/go/test.yaml .template:code:go:test: variables: REPORT_PATH: report GO_VERSION: 1.18 image: golang:$GO_VERSION script: - mkdir -p ${REPORT_PATH} - go test -json -cover -coverprofile ${REPORT_PATH}/coverage.out > ${REPORT_PATH}/unittest.json artifacts: paths: - ${REPORT_PATH}
# code/go/build.yaml .template:code:go:build: variables: BUILD_PATH: build GO_VERSION: 1.18 image: golang:$GO_VERSION script: - mkdir -p ${BUILD_PATH} - go build -o ${BUILD_PATH} artifacts: paths: - ${BUILD_PATH}

La gestion des dépendences entre jobs est laissée à l'équipe de développement des services.

En plus de mutualiser en un seul endroit le code des jobs des pipelines, il est possible de donner des options pour les rendre configurables. Par exemple, le dossier où sera stocké le rapport peut être changé sans impacter la fonctionnalité.

Tous les templates dont nous avons besoin pour transformer le pipeline seront inclus via le mot clef include. Avec l'option ref, les templates sont versionnés. Un outil tel que renovate pourra faire des MR de mise à jour de la version.

Ci-dessous le nouveau pipeline avec l'inclusion des templates.

include: - project: gitlab-ci-library ref: 1.0.0 file: - '/code/go/install.yaml' - '/code/go/test.yaml' - '/code/go/build.yaml' workflow: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' stages: - install - test - build install: stage: install extends: .template:code:go:install test: stage: test extends: .template:code:go:test dependencies: - install build: stage: build extends: .template:code:go:build dependencies: - test

Ce nouveau pipeline est plus lisible. Le développeur s'affranchit de la complexité des jobs. Il se concentre sur l'ordre et les fonctionnalités du pipeline. Il pourra ajouter un job de code lint plus tard par exemple.

Dans une autre équipe qui gère un autre service, le pipeline pourrait ressembler à ça par exemple :

include: - project: gitlab-ci-library ref: 1.0.0 file: - '/code/go/install.yaml' - '/code/go/test.yaml' workflow: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' variables: GO_VERSION: 1.17 stages: - download deps - testing download_deps: stage: download deps extends: .template:code:go:install test_app: stage: testing extends: .template:code:go:test dependencies: - download deps

Le nom des stages est différent. La version de go est différente. Et pourtant, les fonctionnalités des templates restent les mêmes.

Conclusion

Cette organisation du code permet de mutualiser le code à un seul endroit. Le développeur choisi et configure les templates qui vont lui permettre de créer son pipeline. Il en garde ainsi la pleine maîtrise car il connait les besoins de son application.

Du côté SRE, cette organisation permet de répondre aux besoins de toutes les applications sans avoir à répéter le code. Chaque template est générique dans son fonctionnement, mais pleinement configurable.

Ressources

Auteur(s)

Thierry T.

Thierry T.

Super Data Boy

Voir le profil

Vous souhaitez en savoir plus sur le sujet ?
Organisons un échange !

Notre équipe d'experts répond à toutes vos questions.

Nous contacter

Découvrez nos autres contenus dans le même thème

Delta Lake avec Apache Spark

Delta Lake avec Apache Spark

Il existe différent format de fichier pour stocker la donnée : parquet, avro, csv. Connaissez-vous le format Delta Lake ? Découvrons les fonctionnalités de ce format.

Comment tester son script Apache Spark avec Pytest ?

Tester son script Apache Spark avec pytest

Dans le domaine de la data, la qualité de la donnée est primordiale. Pour s'en assurer, plusieurs moyens existent, et nous allons nous attarder dans cet article sur l'un d'entre eux : tester unitairement avec Pytest.

Un long couloir fait à partir de données

Démarrer avec Apache Spark étape par étape

Le domaine de la data est présent dans le quotidien de chacun : la majorité de nos actions peut être traduite en données. Le volume croissant de ces données exploitables a un nom : "Big Data". Dans cet article, nous verrons comment exploiter ce "Big data" à l'aide du framework Apache Spark.