Mise en place d'un pipeline CI/CD self-hosted

22 mars 2023
ci/cd ctfreak devops self-hosted

Pour rappel, un pipeline CI/CD (ou pipeline d’intégration et de déploiement continus) est une série d’actions à effectuer en vue de distribuer de manière cohérente et fiable une nouvelle version d’un logiciel.

Dans cet article, nous allons voir comment mettre en place un pipeline CI/CD minimaliste (lancer automatiquement les builds et déploiements lors des événements push sur un dépôt Git) et auto-hébergé que vous pourrez par la suite adapter selon vos besoins.

Prérequis

Il nous faut:

Les sources

Les sources de notre application peuvent se résumer à ce Makefile:

build:
	@echo "Build my awesome app"
deploy: build
	@echo "Deploy my awesome app"

Vous pouvez les retrouver dans ce dépôt git, avec la branche master correspondant à la dernière version déployée et la branche dev à la version en cours de développement.

Objectifs

Passons en revue les objectifs à atteindre par la mise en place de notre pipeline CI/CD:

Pour ce faire Ctfreak va jouer le rôle de passerelle entre la forge git et le serveur de build.

C’est à dire qu’à chaque push reçu par la forge:

Configuration du serveur de build

Connectez-vous à votre serveur de build et lancez les commandes suivantes:

ctfreakdemo@buildsrv:~$ git clone https://github.com/jypsoftware/myawesomeapp.git myawesomeapp.master
> Cloning into 'myawesomeapp.master'...
> remote: Enumerating objects: 15, done.
> remote: Counting objects: 100% (15/15), done.
> remote: Compressing objects: 100% (13/13), done.
> remote: Total 15 (delta 2), reused 6 (delta 1), pack-reused 0
> Receiving objects: 100% (15/15), 14.38 KiB | 3.59 MiB/s, done.
> Resolving deltas: 100% (2/2), done.

ctfreakdemo@buildsrv:~$ git clone https://github.com/jypsoftware/myawesomeapp.git myawesomeapp.dev
> Cloning into 'myawesomeapp.dev'...
> remote: Enumerating objects: 15, done.
> remote: Counting objects: 100% (15/15), done.
> remote: Compressing objects: 100% (13/13), done.
> remote: Total 15 (delta 2), reused 6 (delta 1), pack-reused 0
> Receiving objects: 100% (15/15), 14.38 KiB | 7.19 MiB/s, done.
> Resolving deltas: 100% (2/2), done.

ctfreakdemo@buildsrv:~$ cd myawesomeapp.dev

ctfreakdemo@buildsrv:~/myawesomeapp.dev$ git checkout dev
> Branch 'dev' set up to track remote branch 'dev' from 'origin'.
> Switched to a new branch 'dev'

Comme vous pouvez le constater, nous avons cloné le repo git 2 fois (1 par branche), ce qui nous permettra d’éviter d’éventuels conflits si les builds des branches master et dev devaient s’exécuter de manière concurrente.

Configuration de Ctfreak

Connectez vous à votre instance Ctfreak avec un compte administrateur.

Ajout du serveur de build

Commencez par ajouter la clef SSH permettant de se connecter au serveur de build via SSH CredentialNew SSH Credential:

Ajout de la clef SSH

Puis ajoutez le serveur en lui même via NodesInternal NodesNew node:

Ajout du serveur

Vérifiez que la clef SSH Key for buildsrv est bien sélectionnée comme Credential (Ctfreak s’en servira pour se connecter au serveur), puis validez pour créer le node buildsrv.

Création du projet CI/CD

Nous allons créer un projet dédié pour rassembler toutes les tâches en rapport avec notre pipeline CI/CD.

Allez dans ProjectsNew Project

Création du projet

Validez pour créer le projet.

Ajout d’un notifieur

Allez dans ProjectsCI/CDNotifiersNew notifier

Sélection du type de notifieur

Choisissez Discord comme type de notifieur (par exemple).

Edition du notifieur

Précisez l’URL du webhook discord à appeler et validez.

Liste des notifieurs

Une notification de test peut être envoyée en cliquant sur le bouton d’envoi (celui en forme d’avion en papier).

Création de la tâche de build

Allez dans ProjectsCI/CDNew task

Sélection du type de tâche

Choisissez Bash Script comme type de tâche.

Tâche d’intégration continue

Comme vous l’aurez deviné, cette tâche va permettre de compiler la branche dev sur le serveur de build et envoyer une notification en cas d’échec.

La stratégie d’exécution multiple (multiple execution policy): chaînage intelligent (smart chaining) choisie ici va permettre d’éviter de lancer des builds superflus.

Prenons un exemple: vous avez un build qui dure une demi-heure et 3 pushs sont envoyés sur le dépôt git à 5 minutes d’intervalle.

Au premier push, le build est lancé immédiatement.

Au 2ème push, un 2ème build est mis en attente vu que le premier n’est pas encore terminé.

Au 3ème push, on ne prévoit pas de faire de 3ème build car le 2ème est toujours en attente.

Création de la tâche de déploiement

Pour gagner du temps, créez cette tâche à partir de celle de build via ProjectsCI/CDBuid myawesomeapp / devDuplicate.

Tâche de deploiement continue

En y apportant les modifications suivantes:

Ajout des webhooks

Maintenant que nos tâches de build et de déploiement sont créées, nous allons ajouter pour chacune d’elle un webhook entrant qui pourra être appelé par notre forge git.

Commençons avec la tâche de déploiement en allant dans ProjectsCI/CDBuid myawesomeapp / devIncoming webhooksNew webhook.

Sélection du type de webhook

Choisissez GitHub comme type de webhook.

Lorsque GitHub appelera notre webhook suite à un push, il le fera quelque soit la branche concernée or notre tâche ne doit être exécuté que lorsque ce push concerne la branche dev.

Pour résoudre ce problème, nous ajoutons une condition d’exécution qui s’appliquera au payload JSON envoyé par GitHub. La tâche ne sera exécutée que si la condition est remplie (c-a-d le payload fait bien référence à la branche dev).

Création du webhook dev 1

Définissez au besoin un secret qui permettra à Ctfreak de valider que les appels au webhook proviennent bien de GitHub et validez.

Une fois le webhook créé, nous récupérons son URL pour le paramétrage de GitHub.

Création du webhook dev 2

Au niveau de la configuration de votre dépôt GitHub, ajoutez le webhook que nous venons de créer en précisant bien:

Paramétrage du webhook dev dans github

Procédez de la même façon (création du webhook, référencement dans GitHub) pour la tâche de déploiement (pensez à bien faire référence à la branche master dans la condition d’exécution):

Création du webhook master

Test du pipeline

Maintenant que tout est en place, voyons ce que provoque un git push sur la branche dev:

Suite à l’appel du webhook GitPushDev la tâche de build a bien été exécutée.

En cliquant sur l’icône en forme d’œil, nous avons les logs de l’exécution.

Même test avec un git push sur la branche master:

Ici c’est le webhook GitPushMaster qui a été appelé comme prévu.

Et le petit plus ici, c’est la réception d’une notification Discord pour confirmer que le déploiement s’est bien passé.

Conclusion

Vous voilà avec un pipeline CI/CD opérationnel et suffisamment souple pour prendre en charge les workflows de build les plus complexes.

En optant pour cette solution self-hosted et en plaçant le cœur du processus de build dans un script shell plutôt que dans un logiciel d’intégration continue comme Jenkins, GitHub Actions, …, vous bénéficierez de plusieurs avantages: