Il y a deux principales manières de construire sa chaine de CICD.
La première consiste à faire usage d’un seul et même produit pour traiter toutes les étapes de sa chaine. On trouve de plus en plus de « plateforme » capable à la fois de prendre en compte le CI et le CD (pour ceux qui ne sauraient pas faire la différence, une explication est disponible ici).
Cliquez sur l'image pour l'agrandir.
C’est le cas par exemple de GitLab dont je propose une installation sous Kubernetes dans cet article.
Jenkins, dont vous trouverez un tutoriel ici, peut servir à tout également. Bien qu’il soit pensé à l’origine pour du CD, son extensibilité via ses nombreux plugins peut lui permettre de couvrir le CI.
Le cloud connait également ses suites complètes, comme Azure DevOps, qui permet de couvrir tout le spectre possible du CI/CD.
L’avantage de ne retenir qu’un outil pour tout faire est le niveau d’intégration proposée et la simplicité de maintenance des chaines construites. On spécialise ses équipes sur le logiciel retenu et on capitalise sur le travail de chacun.
Les inconvénients sont la dépendance induite à la solution et la difficulté de se retrouver parfois à configurer des choses compliquées pour réaliser des actions simples. Certaines taches peuvent être finalement complexes, car il n’est pas toujours évident de pouvoir tout faire, de manière basique et efficace quand on cherche à tout intégrer dans un seul produit.
La plupart de ses solutions doivent parfois faire des concessions pour traiter un pan de la chaine en comparaison d’outils spécialisés.
C’est pourquoi la seconde manière consiste à lier différents outils ensemble, chacun étant optimisé dans une ou plusieurs tâches de la chaîne. On peut ainsi retenir des applicatifs extrêmement efficaces dans un domaine précis, comme le GitOPs avec ArgoCD dont vous trouverez une présentation dans cet article.
Cliquez sur l'image pour l'agrandir.
L’avantage de cette méthode est d’être plus modulable, plus adaptable et customisable tout en pouvant être plus efficace sur certaines parties de la chaine. L’inconvénient demeure la complexité que cela peut entrainer. Il devient nécessaire de se former sur plusieurs outils et surtout de maintenir un lien entre eux. L’upgrade d’un applicatif peut dégrader la chaine et avoir un impact négatif sur tout le reste.
De mon côté, mon expérience et surtout les collègues avec qui je travaille m’ont plutôt orienté sur cette seconde méthode. En effet, les inconvénients cités précédemment sur la segmentation de la chaine à travers divers outils peuvent être amoindris lorsque les outils choisis disposent nativement de moyens pour discuter entre eux. Dans ce cas, et en théorie, maintenir une bonne communication entre eux est relativement simple à faire si on s’efforce de les faire évoluer en parallèle tout en respectant les matrices de compatibilités.
C’est le but de ce tutoriel, où après avoir vu le déploiement de Jenkins puis de Gitlab sous Kubernetes, nous allons pouvoir les faire communiquer ensemble. Plutôt que d’utiliser l’un ou l’autre pour concevoir toute une chaine, on va plutôt capitaliser sur leurs points forts respectifs pour déclencher une pipeline jenkins sur un évènement git.
Cela permettra d’établir les premières briques de mon objectif final d’illustrer un cas d’utilisation plus complet et concret autour d’outillage CI/CD.
La lecture de cet article nécessite à minima d’être à l’aise dans le fonctionnement de Jenkins et GitLab et idéalement d’avoir jeté un œil à mon article traitant de Jenkins, puis à celui traitant de Gitlab. Car je vais m’appuyer sur les installations qui en découlent pour poursuivre l’exemple du jour.
Pour démarrer, on crée un compte de services dédié à la communication de Jenkins avec GitLab. Il est possible d’utiliser un compte local à GitLab, mais, comme dans le tutoriel associé, nous avions mis en place l’intégration de l’annuaire AD via LDAP, il est plus optimisé de travailler avec un compte Active Directory. Pour davantage de sécurité, je vous conseille un compte de service dédié, et non un compte générique. Par exemple: svc-jenkins-togitlab (libre à chacun d’adopter la nomenclature de son choix).
Cliquez sur l'image pour l'agrandir.
L’important est de pouvoir rapidement identifier l’usage du compte (le champ description dans l’AD est bien utile…mais trop peu souvent utilisé…).
Nous nous connectons ensuite avec ce compte sur GitLab. À partir de son profil, on crée un token d’accès. Celui-ci sera propre au compte de service et le token généré sera utilisé au niveau de jenkins.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Passé cette étape, il n’y a normalement plus à utiliser ce compte directement.
Nous basculons sur Jenkins. Il faut se connecter avec les droits d’administration.
La première chose à faire est d’installer le plugin GitLab.
On passe par le menu Administrer Jenkins, puis Plugins pour récupérer ce dernier.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Son installation va permettre de tirer parti du token généré précédemment.
Pour cela on se rend dans la section .Credentials de Jenkins.
Cliquez sur l'image pour l'agrandir.
On choisit ensuite le domaine Global (credential valable sur toute la plateforme Jenkins), puis on crée un nouveau Credentials de type GitLab API Token.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
On laisse la porté à Global, on renseigne le token récupéré depuis GitLab en n’oubliant pas de choisir un ID et une description suffisamment compréhensible.
Cliquez sur l'image pour l'agrandir.
Pour valider le bon fonctionnement, on reste dans la section administration de Jenkins mais on bascule sur l’option System.
Cliquez sur l'image pour l'agrandir.
Grâce à l’installation du plugin gitlab, une section dédiée est disponible.
Il suffit de créer une nouvelle connexion.
En guise de nom, j’utilise l’URL de gitlab (gitlab.coolcopr.priv) mais chacun peut choisir la valeur de son choix.
On indique ensuite l’url complète (incluant https) du serveur gitlab. Attention, si votre serveur est protégé par un certificat personnalisé issu d’une CA interne, il faut que cette dernière soit connue de Jenkins. N’hésitez pas à suivre mon tutoriel sur l’installation de Jenkins pour en apprendre davantage sur ce point.
Puis vient le choix du credential. Normalement celui créé précédemment doit apparaitre.
Il suffit de le choisir, puis de cliquer sur tester pour valider la configuration, puis de sauvegarder l’ensemble.
Cliquez sur l'image pour l'agrandir.
Arriver à ce stade, Jenkins est en mesure de solliciter Gitlab via la clef API du compte de service AD dédiée, tout cela grâce au plugin Gitlab déployé dans Jenkins.
Mais ce n’est pas terminé. Nous allons chercher à mettre en œuvre deux types d’interactions entre Jenkins et GitLab.
La première consiste, comme indiqué au début de l’article, à lancer une chaîne Jenkins en réponse à un push dans un repo GitLab.
La seconde va être d’héberger la définition de la pipeline, soit le jenkinsfile, directement dans git. Au lieu que le code de la pipeline soit explicitement inscrit dans la GUI, celui-ci sera rédigé dans un fichier, lui-même hébergé dans Gitlab. Ainsi à chaque lancement de la pipeline, le code de cette dernière sera d’abord récupéré dans Git avant d’être exécuté.
Cela permet de centraliser ses définitions de pipeline et de permettre leur suivi en équipe, ainsi que leur versionning.
Pour arriver à cette seconde interaction, il va falloir revenir dans la gestion des credentials de Jenkins.
Toujours dans le domain Global, on crée cette fois-ci un credential de type Nom d’utilisateur et mot de passe.
Cliquez sur l'image pour l'agrandir.
On utilise le compte de service AD (sans préciser le domaine) ainsi que son mot de passe.
Désormais toutes les conditions sont remplies pour se lancer dans l’exemple final illustré dans le schéma ci-dessous.
Cliquez sur l'image pour l'agrandir.
On commence par créer le repo dédié à l’hébergement des jenkinsfile. Attention, ceci n’est pas le repo associé à notre futur application dont on souhaite un traitement CI/CD, il s’agit simplement du référentiel à venir de tout le code des pipelines qu’on pourrait être amené à construire.
Je vais reprendre le code de mon article précédent sur Jenkins.
pipeline {
agent any
stages {
stage('Test Kubernetes Access') {
steps {
sh 'kubectl get pods -A'
}
}
}
}
Il était à l’origine écrit explicitement dans l’interface de Jenkins, nous allons le recopier dans un fichier demo.jenkinsfile.
C’est un exemple basique qui se contente de lister les pods du cluster Kubernetes où Jenkins s’exécute.
On crée ensuite le repo chargé de contenir ce fichier. Je me connecte avec mon compte AD personnel sur gitlab, puis je commence par créer un groupe (au sens gitlab) nommé CICD. C’est plus simple pour moi d’organiser tous ce qui touche au CICD (à l’opposé des applications) dans un groupe dédié.
Cliquez sur l'image pour l'agrandir.
Je vais d’ailleurs donner un droit d’accès à ce groupe au compte de service svc-jenkins-togitlab créé en début d’article. Il pourra ainsi accéder à tous les repos au sein du groupe.
Cliquez sur l'image pour l'agrandir.
Dans ce même groupe, on déclare un repo jenkinsfile que je m’empresse de cloner sur mon poste de travail.
Cliquez sur l'image pour l'agrandir.
Attention, utilisant TortoiseGIT avec un gitlab sous K8S, je dois modifier mon url SSH pour tenir compte du port spécifique que j’ai retenu pour exposer GitLab Shell. Tout cela est expliqué dans mon article dédié sur l’installation de GitLab sous Kubernetes.
Cliquez sur l'image pour l'agrandir.
Il ne me reste plus qu’à déposer le fichier demo.jenkinsfile dans le dossier et à effectuer un « push » pour le rendre disponible dans le répo.
Puisque nous sommes déjà sur GitLab, passons maintenant au repo de l’application démo spécifique (celle que nous voulons intégrer dans une chaîne de CICD et pour laquelle nous désirons déclencher une pipeline Jenkins après chaque modification).
On créer donc un dossier apps dans lequel on créé le repo demo-check-code (le nom n’est pas anodin pour les articles à venir)
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
On donne les droits d’accès au compte de service svc-jenkins-togitlab
Je valide le bon fonctionnement du repo en réalisant un clone de ce dernier sur mon poste.
On poursuit avec Jenkins.
On créer un nouvel item de type Pipeline.
Cliquez sur l'image pour l'agrandir.
Je décide de l’appeler security-code-check.
Dans la configuration de la pipeline on va indiquer un petit descriptif.
On bascule ensuite dans la section gitlab connection pour retenir la connexion que l’on a créée dans les prérequis (celle qui utilise le token).
Cliquez sur l'image pour l'agrandir.
On continue à descendre pour cocher la case: Build when a change is pushed to GitLab. GitLab webhook URL:....
En prenant bien soin de noter l’URL indiquée.
On souhaite que la pipeline se déclenche sur un évènement de type >push.
Cliquez sur l'image pour l'agrandir.
Un peu plus bas on déroule le menu Avancé.
On ne filtre sur aucune branche.
Puis on clique sur generate pour obtenir un secret token, là aussi il est important de le retenir comme pour l’URL, nous allons en avoir besoin pour la suite.
Cliquez sur l'image pour l'agrandir.
Cette partie de la configuration couvre le cas de la première interaction Jenkins/GitLab, celle du déclenchement de la pipeline sur un push dans le repo de l'app hébergé dans gitlab.
Il faut poursuivre la configuration pour traiter la seconde interaction.
Dans la section pipeline, on sélectionne Pipeline script from SCM.
C’est de cette manière qu’on indique à Jenkins de récupérer le code de la pipeline depuis un repo.
En l’occurrence on choisit un repo de type GIT.
On indique l’URL du repo Git associé au premier repo GitLab que nous avons créé pour stocker les fichiers jenkinsfile.
Attention, on utilise un lien type https et non ssh.
Juste en dessous, on choisit les identifiants pour se connecter à ce répo.
En l’occurrence, le credential de type login et mot de passe créé précédemment avec les identifiants du compte de service AD dédié à la communication entre jenkins et gitlab.
Juste en dessous, on indique qu’on se base sur la branche main.
Cliquez sur l'image pour l'agrandir.
En tant que navigateur de la base de code, on choisi gitlab.
On répète l’URL https du repo contenant les jenkinsfiles.
Du côté version, il faut s’aligner sur la version de gitlab. Au moment de la rédaction de cet article, j’utilise la release 17.1.
Enfin au niveau de l’option Script Path, on indique le nom du jenkinsfile à utiliser, soit le fichier poussé dans le repo dédié: demo.jenkinsfile.
Cliquez sur l'image pour l'agrandir.
Si vous décidez d’héberger tous vos jenkinsfiles dans un même repo, vous pourrez toujours reproduire cette configuration en changement uniquement le nom du fichier à appeler.
Si vous exploitez des sous-dossiers au sein du repo, il faudra mettre le chemin complet. Sinon, vous pouvez aussi décider d’utiliser des repos dédiés, dans ce cas, il faudra mettre vos paramètres en adéquation avec vos repos.On peut terminer la configuration en cliquant sur save.
Il faut maintenant revenir à gitlab.
Avant de paramétrer le webhook, il faut déjà procéder à un changement de configuration globale.
Pour ça il faut utiliser un compte avec pouvoir.
On se positionne dans la section admin, settings, puis Network.
On déplie la section Outbound requests pour cocher la case Allow Request to the local Network.
On peut éventuellement filtrer les IPs et les domaines autorisés pour les webhook.
Cliquez sur l'image pour l'agrandir.
On n’oublie pas de sauvegarder.
On peut ensuite reprendre là où on s’était arrêté sur GitLab, en utilisant notre compte standard pour le répo « apps », que j’ai personnellement nommé demo-check-code.
Dans les paramètres de ce repo, on choisit l’option webhook.
Cliquez sur l'image pour l'agrandir.
On crée un nouveau webhook.
Cliquez sur l'image pour l'agrandir.
Celui-ci prend en URL l’URL retournée précédemment par jenkins lors de la création de la pipeline.
Cliquez sur l'image pour l'agrandir.
De même pour le secret qui doit prendre la valeur du token qu’on n’a généré précédemment au niveau de la pipeline.
On indique qu’on souhaite déclencher le webhook sur une activité de push pour toutes les branches du repos.
Cliquez sur l'image pour l'agrandir.
Si vous avez suivi mon tutoriel sur l’installation de GitLab, vous devriez pouvoir cocher la case Enable SSL verification. Si vous utilisez une CA personnalisée pour sécuriser l’URL de votre serveur Jenkins et que cette dernière n’est pas reconnue par GitLab, vous devez décocher la case correspondante.
Cliquez sur l'image pour l'agrandir.
Tout le paramétrage est maintenant terminé.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Pour tester l’ensemble, il suffit de pousser un fichier dans le repo de l’apps de demo.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Si tout se déroule comme prévu, le pipeline devrait se déclencher automatiquement en affichant le nom de la personne qui a effectué le push dans le repo.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Cette double intégration entre Jenkins et Gitlab permet à la fois de déclencher une pipeline sur un évènement de push dans le repo, mais également de récupérer dynamiquement le code de la pipeline à chaque exécution.
Nous n’avons rien utilisé d’exotique, en nous basant uniquement sur les fonctionnalités d’intégrations natives des deux produits. Bien entendu, il faudra garder une cohérence entre la version de gitlab, le plugin gitlab de jenkins et jenkins lui-même. Il ne faut pas oublier les informations d’authentification qui lient les solutions entre elles, en considérant les changements de mot de passe et les expirations de token.
Pour se simplifier la maintenance, il est toujours bon de conserver une documentation à jour et un schéma d’interaction. Ce n’est pas une configuration qu’on est amené à refaire tous les jours. On peut vite oublier la manière dont elle s’articule. Quand quelque chose ne fonctionne plus, il faut être en mesure de se souvenir de la logique mise en place.
Les briques sont désormais posées pour entamer une automatisation plus avancée autour d’un cas concret d’usage. J’aborderais ceci dans un futur article.