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:
- Un serveur de build que nous nommerons buildsrv:
- Sous linux
- Accessible via SSH
- Disposant des outils make et git
- Une forge git hébergeant les sources de notre application. Nous utiliserons ici GitHub mais toute autre forge capable d’appeler des webhooks lors de la réception d’évènements push sur ses dépôts fera aussi très bien l’affaire.
- Une instance de Ctfreak avec un compte administrateur (la Free Edition suffira). Nous utiliserons ici https://demo.ctfreak.com
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 la partie Intégration Continue:
- Compiler les sources (via la commande
make
) à chaque fois qu’un développeur push sur la branche dev. - Recevoir une notification en cas d’échec.
- Compiler les sources (via la commande
- Pour la partie Déploiement Continue:
- Compiler les sources et déployer (via la commande
make deploy
) à chaque fois qu’un développeur push sur la branche master. - Recevoir une notification en cas de succès ou d’échec.
- Compiler les sources et déployer (via la commande
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:
- Celle-ci va appeler un webhook défini dans Ctfreak.
- Ctfreak va alors se connecter via SSH au serveur de build pour exécuter la commande
make
adéquate, puis envoyer une notification.
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 Credential → New SSH Credential:
Puis ajoutez le serveur en lui même via Nodes → Internal Nodes → New node:
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 Projects → New Project
Validez pour créer le projet.
Ajout d’un notifieur
Allez dans Projects → CI/CD → Notifiers → New notifier
Choisissez Discord comme type de notifieur (par exemple).
Précisez l’URL du webhook discord à appeler et validez.
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 Projects → CI/CD → New task
Choisissez Bash Script comme type de tâche.
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 Projects → CI/CD → Buid myawesomeapp / dev → Duplicate.
En y apportant les modifications suivantes:
- Nous souhaitons recevoir une notification en fin de traitement dans tous les cas.
- Le contenu du script est mis à jour pour déployer à partir de la branche master.
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 Projects → CI/CD → Buid myawesomeapp / dev → Incoming webhooks → New 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).
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.
Au niveau de la configuration de votre dépôt GitHub, ajoutez le webhook que nous venons de créer en précisant bien:
- Son URL et son secret
- Que c’est le format JSON qui est attendu
- Que le webhook ne doit être appelé QUE pour les évènements push
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):
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:
- Vous ne serez pas contraint par les limites de ce dernier (version de git pas la plus récente, non prise en charge de certaines spécificités de votre build, limite du nombre de build journalier souscrit atteinte, …).
- Si votre serveur de build est indisponible, vous pourrez encore effectuer un déploiement en urgence à partir d’un poste de développeur (croyez-moi, ça peut servir).