Créer et recréer des pipelines de CI/CD…#
L’intégration et le déploiement continus (CI/CD), c’est avant tout un concept devenu incontournable pour créer des images conteneurisées, déployer votre code d’infrastructure ou encore vérifier les vulnérabilités de vos outils.
Si vous avez déjà maintenu différents types de pipeline plus ou moins complexes, vous savez à quel point le code de ces chaînes automatisées peut être dupliqué dans les différents dépôts de code, qu’ils soient applicatifs ou d’infrastructure.
Pire encore, comment s’assurer que les chaînes CI/CD suivent le standard adopté dans toute l’entreprise en utilisant des étapes de vérifications syntaxiques, de scan de vulnérabilités ou de vérification de la couverture de code ? Pas si évident !
Et pourtant, depuis sa version 17, GitLab a introduit un nouveau concept disponible dans toutes les éditions : les composants CI/CD. Un paradigme qui apporte enfin la réutilisation, le versioning et le partage natif des jobs entre projets GitLab.
J’ai utilisé et j’utilise toujours ce concept que je trouve extrêmement crucial surtout pour ériger un standard et s’assurer de la mise en place d’un socle commun accessible à tous.
Dans la suite de cet article, je vous propose de faire un tour d’horizon de ce concept, en poussant jusqu’à la création d’une suite de composants sur OpenTofu, le fork de Terraform.
Un composant ? Pour quoi faire ?#
Un composant à la sauce GitLab, c’est une portion de code en YAML, à travers un dépôt de code Git, qui peut être appelée à travers une chaîne CI/CD dans le fichier .gitlab-ci.yml.
Voici un exemple :
Fichier format.yml
spec:
inputs:
stage:
default: lint
[...]
---
tofu-fmt:
stage: $[[ inputs.stage ]]
script: tofu fmt -check -recursive -write=false -diff
[...]
On rentrera dans les détails un peu plus tard, notamment sur la structure mais ce composant permet d’appeler un job tofu-fmt directement dans une pipeline de CI/CD.
L’avantage ? Éviter la duplication de code mais surtout versionner et mettre à disposition des autres équipes de votre entreprise ou de la communauté, des composants clés en main.
Bien évidemment toutes les étapes des pipelines ne sont pas forcément faites pour être utilisées sous forme de composant. L’objectif étant d’identifier ceux que vous voulez utiliser le plus, notamment en fonction d’un thème ou d’un outil précis afin d’élaborer une bibliothèque standardisée.
Par exemple, on pourrait avoir des composants regroupés sous OpenTofu, Terraform, Docker, Podman ou bien alors une suite de composants pour l’infrastructure as code, pour les vérifications de sécurité, ou la conteneurisation, etc.
Je suis assez adepte de décomposer le plus possible pour faire des composants évolutifs mais surtout maintenables, n’oubliez jamais qu’ils devront être mis à jour, une fois leur mise en place terminée.
Une structure pour les gouverner tous !#
Comme dit plus haut, un composant est contenu dans un dépôt de code qui doit répondre à certaines exigences côté arborescence.
Exemple ici avec le repo Git opentofu :
├── templates/
│ └── format.yml
├── LICENSE.md
├── README.md
└── .gitlab-ci.yml
On retrouve :
Un
README.mdpour expliquer en long et en large les différents composants, avec pourquoi pas les variables que l’on peut configurer, comment les appeler avec des exemples, etc;Un fichier
LICENSE.mdpour assigner une licence à ce composant, notamment pour les composants publics. Les licences MIT ou Apache 2.0 sont souvent privilégiées;Le
.gitlab-ci.ymlqui peut exécuter des tests pour valider l’utilisation du composant, jouer une suite de tests mais surtout créer une release en fonction d’un tag pour publier une nouvelle version;Un dossier
templatesdans lequel on peut ranger les composants sous forme de fichier.yml. Il est possible de créer des sous-dossiers mais ces derniers doivent contenir un fichiertemplate.ymlpour être exploité correctement en tant que composant.
Enfin, le nom du repo ainsi que son arborescence n’est pas à négliger, notamment pour la gestion des droits et éviter les confusions !
Publier pour mieux partager#
Autre étape importante, publier vos composants. Bien que non obligatoire, cette dernière est primordiale pour les rendre accessibles à votre organisation ou les rendre publics.
Dans une logique DevOps, c’est grâce à l’ajout d’un job de release au sein du fichier .gitlab-ci.yml du dépôt de code en question qui doit permettre de publier l’ensemble au sein du Catalogue CI/CD de GitLab.
Ce catalogue, comme son nom l’indique, permet de répertorier l’ensemble des composants disponibles dans votre instance GitLab en fonction des permissions des utilisateurs.
Attention toutefois, des étapes supplémentaires doivent être complétées avec le rôle Owner pour autoriser votre dépôt de code Git à publier dans le catalogue global.
Pour publier au sein du catalogue de GitLab, voici un exemple de publication automatique qui peut être fait avec ce bout de code :
.gitlab-ci.yml
stages:
- release
create-release:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:v0.24.0
script: echo "Creating release $CI_COMMIT_TAG"
release:
tag_name: $CI_COMMIT_TAG
description: "Release $CI_COMMIT_TAG of components in $CI_PROJECT_PATH"
rules:
- if: $CI_PIPELINE_SOURCE != "push"
when: never
- if: $CI_COMMIT_TAG
Cela aura pour but de générer une release en fonction d’un tag défini au sein du repo.
Le versioning est une étape clé, GitLab recommande de suivre le semantic versioning. Néanmoins, il reste possible d’appeler un composant de différentes manières :
- À travers le sha d’un commit (exemple : 1f4d90e308bf4cf9abe17060fdd59bbd71f720bf);
- Un tag (exemple : 1.1.0)
- Un nom de branche (exemple : main)
- Et enfin, le mot clé
~latestpermettant de récupérer la version la plus récente d’un composant.
Le sha est conseillé, bien que peu lisible, la bonne option reste le tag mais exige un versioning propre en respectant l’immuabilité.
Place à l’utilisation#
L’utilisation de votre composant s’effectue par une syntaxe dédiée au sein d’un fichier YAML, le plus souvent .gitlab-ci.yml :
include:
- component: ${CI_SERVER_FQDN}/cicd-catalog/opentofu/[email protected]
inputs:
image_tag: "v1.10.6"
À travers le bloc include, sous forme de liste, il est possible d’énumérer les composants avec leurs versions @1.1.0. Comme vous pouvez le constater, le dossier templates n’est pas à préciser dans le chemin du composant, uniquement le nom du fichier YAML ou dossier contenant ce dernier.
Le bloc inputs référence les variables définies dans la spec du composant. Dans cet exemple, image_tag permet de fixer la version de l’image pour OpenTofu.
Quelques bonnes pratiques#
Pour clôturer la partie essentiellement théorique, voici quelques principes pour rendre vos composants robustes et adaptables :
Améliorez votre composant par itération : évitez de créer 40
inputspour rendre votre composant totalement configurable, concentrez-vous sur la valeur de celui-ci avec une poignée de variables. Quitte à l’améliorer au fil du temps en fonction des demandes.Gérez la compatibilité de vos versions : votre composant va évoluer dans le temps, il est donc nécessaire d’avoir un changelog exhaustif de version en version sur les futures dépréciations ou ajouts de fonctionnalités;
Validez votre composant : ajoutez des jobs de test dans le
.gitlab-ci.ymldu projet regroupant vos composants pour garantir que le template fonctionne dans différentes configurations, notamment la partie script lors de la mise à jour d’une image;Privilégiez les variables officielles GitLab : évitez de coder en dur des URLs ou chemins. Utilisez par exemple des variables prédéfinies au sein de GitLab CI/CD, voici quelques exemples :
- $CI_SERVER_FQDN : Le nom de domaine de l’instance GitLab;
- $CI_API_V4_URL : L’URL racine de l’API de GitLab;
- $CI_PROJECT_DIR : Le répertoire où est cloné votre projet lors de l’exécution de la CI/CD.
Créez une documentation exhaustive : détaillez les commandes, variables attendues, comportements optionnels dans le
README.md. N’hésitez pas à ajouter différents cas d’usage pour faciliter l’intégration de votre composant avec un paramétrage avancé.
L’heure de vérité#
C’est le moment de créer nos composants OpenTofu !
Pour faire les choses simplement, je vous propose de créer un composant par job GitLab :
- format: vérifie l’indentation du code avec la commande
fmt; - security: exécute une analyse de sécurité avec
checkov; - lint: analyse les bonnes pratiques avec
tflint; - plan: exécute un
tofu planen stockant le plan pour plus tard sans oublier un mécanisme de cache pour le dossier.terraformet le fichier.terraform.lock.hcl; - apply: lance un
tofu applyavec le plan en paramètre.
Pas mal pour un début, non ?
Vous pouvez récupérer le code directement à travers mon repo GitLab, ce qui vous donnera une idée du travail à réaliser :
Comme vous pouvez le constater, le dossier templates est assez fourni avec autant de fichiers YAML que de composants décrits plus haut.
En guise d’exemple, je vous propose de parcourir le composant plan qui est plutôt complet. Les variables, à travers le bloc spec permettent de personnaliser le job en fonction des besoins :
spec:
inputs:
job_name:
default: plan
description: "Name of the job"
stage:
default: plan
description: "Pipeline stage where the job will run"
image:
default: ghcr.io/opentofu/opentofu
description: "Container image to use for OpenTofu"
version:
default: "1.10.6"
description: "OpenTofu version to use"
working_directory:
default: "."
description: "Directory containing OpenTofu files"
plan_file:
default: "tfplan"
description: "Name of the plan file to generate"
[...]
Personnellement, je trouve important de donner la possibilité à l’utilisateur de nommer le job à travers une variable job_name dans le but d’éviter les conflits avec d’autres composants ou jobs existants, mais surtout d’éviter d’utiliser le mot clé extends pour le renommer.
Attention à ne pas oublier les --- pour séparer le code du ou des job(s) des variables définies.
Côté job, le template reste assez lisible, inutile d’en faire trop… On souhaite permettre à l’utilisateur de générer un fichier contenant le plan des ressources à créer, mettre à jour ou supprimer à la suite de la commande tofu plan avec en option, quelques paramètres : backend_config, var_file et var.
"$[[ inputs.job_name ]]":
stage: $[[ inputs.stage ]]
image:
name: $[[ inputs.image ]]:$[[ inputs.version ]]
entrypoint: [""]
script:
- cd $[[ inputs.working_directory ]]
- |
INIT_ARGS=""
if [[ -n "$[[ inputs.backend_config ]]" ]]; then
IFS=',' read -ra CONFIGS <<< "$[[ inputs.backend_config ]]"
for config in "${CONFIGS[@]}"; do
INIT_ARGS="$INIT_ARGS -backend-config=$config"
done
fi
[...]
Les variables sont identifiées et remplacées grâce à la syntaxe $[[ inputs.version ]].
Si jamais vous désirez tester ces composants, voici un exemple de fichier .gitlab-ci.yml qui peut en inspirer plus d’un :
include:
# Format
- component: $CI_SERVER_FQDN/filador-public/cicd-library/opentofu/[email protected]
inputs:
stage: analyze
working_directory: "./infra"
# Security
- component: $CI_SERVER_FQDN/filador-public/cicd-library/opentofu/[email protected]
inputs:
stage: analyze
working_directory: "./infra"
# Lint
- component: $CI_SERVER_FQDN/filador-public/cicd-library/opentofu/[email protected]
inputs:
stage: analyze
working_directory: "./infra"
# Plan
- component: $CI_SERVER_FQDN/filador-public/cicd-library/opentofu/[email protected]
inputs:
working_directory: "./infra"
# Apply
- component: $CI_SERVER_FQDN/filador-public/cicd-library/opentofu/[email protected]
inputs:
working_directory: "./infra"
stages:
- analyze
- plan
- apply
plan:
needs:
- format
- security
- lint
apply:
needs:
- plan
Le bloc inputs permet de surcharger les valeurs par défaut. Dans mon cas, le code OpenTofu se trouve dans un dossier infra et j’ai regroupé les étapes format, security et lint dans le stage analyze.
Vous l’aurez compris, c’est comme une sorte de puzzle où on importe les composants qui font du sens par rapport à la CI/CD que l’on crée, l’aspect modulaire est un atout essentiel de cette approche.
Dernière chose un peu personnelle, je préfère gérer les dépendances avec le mot clé needs à l’extérieur des composants pour ne pas perdre en lisibilité et comprendre les dépendances très facilement en lisant ce fichier.
Un univers de composants#
Réinventer indéfiniment la roue n’est peut-être pas votre meilleure option !
Et en effet, sur GitLab.com, la communauté propose déjà un riche catalogue de composants. Libre à vous de les utiliser, de contribuer, de les forker ou de les adapter à vos besoins.
Le gain de temps est considérable, sans oublier que ces derniers sont améliorés et éprouvés par une large communauté avec un tas d’options.
Bien sûr, l’exemple ci-dessus n’est pas aussi touffu que le composant OpenTofu maintenu par GitLab mais vous avez compris le principe.
Alors, utile ?#
Dès lors que l’on commence à utiliser ce concept, il est difficile de s’en passer !
Que ce soit ceux fournis par la communauté ou vos propres composants, il y en a pour tous les goûts.
Avec cette approche, on est clairement dans du Platform Engineering, en mettant à disposition un catalogue de composants standardisés, prêts à l’emploi tout en répondant aux exigences de qualité voire sécurité de l’entreprise.
À vous de jouer maintenant !




