Blog | WeScale

Installer Jenkins sur votre instance AWS en 3 étapes clés

Rédigé par Sophnel Merzier | 12/04/2023

Jenkins est l'un des outils de CI/CD (Intégration et déploiement continu) les plus populaires et les plus utilisés dans l'industrie. Il permet aux développeurs d'automatiser les processus de test ou de déploiement des logiciels, ce qui permet de réduire les erreurs et de libérer du temps pour les tâches plus importantes.

Dans cet article destiné aux personnes souhaitant commencer sur Jenkins, nous verrons un cas d'étude sur son utilisation avec la création de notre premier pipeline CI, et ses possibilités si vous souhaitez aller plus loin.

Installation et configuration de Jenkins

Cet article utilise un environnement de test qui ne doit jamais être utilisé en production. En effet, Jenkins et votre application devront être installés sur des serveurs distincts.

Pour notre exemple, nous allons installer Jenkins sur une instance AWS, nous pourrons y accéder via l’IP publique de l’instance créée.

Suivant votre distribution, je vous conseille d’aller voir le tutoriel officiel sur https://www.jenkins.io/doc/book/installing/linux/.

Après l’installation, si vous avez bien le port 8080 autorisé dans le groupe de sécurité sur AWS, vous pouvez accéder à la page d’accueil en allant à http://<IP_PUBLIQUE>:8080

Pour connaître le mot de passe, connectez-vous sur le serveur AWS, ensuite affichez le contenu du fichier qui est indiqué comme ci-dessous. Il s’agit d’une sécurité supplémentaire apportée par Jenkins lors de son installation.

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

Laissez le choix par défaut qui est d’installer les plugins suggérés et attendez la fin. Puis, créez un nouvel utilisateur pour continuer :

Laissez par défaut l’adresse proposée pour accéder de l'extérieur.

Après les configurations, la page d’accueil ressemble à ceci : 

Nous allons maintenant créer notre premier pipeline CI/CD.

Notre première application

Pour mieux comprendre l’utilité de Jenkins, nous allons imaginer le scénario suivant.

Vous êtes DevOps dans l’entreprise WeWe, ils ont une application en Node.js qui sera simulée ici avec l’exemple du projet « HelloWorld » du dépôt d’IBM https://github.com/IBM/node-hello-world.

On vous a chargé d'ajouter une fonctionnalité qui sera accessible à travers un bouton.

Votre but sera de créer un pipeline Jenkins qui automatise le processus de build de la fonctionnalité et de test du bouton sans intervention humaine.

Notre application étant sur le même port et serveur que Jenkins, nous allons changer son port pour pouvoir y accéder.

Nous allons forke le dépôt précédent pour pouvoir le modifier à notre guise de la manière suivante. Dans ce fork, créer un nouveau fichier intitulé button.js en y mettant ce code :

Script de test du bouton dans notre pipeline

const puppeteer = require('puppeteer');
const assert = require('assert');
describe('Button Test', async function() {
   let browser;
   let page;
   before(async function() {
       browser = await puppeteer.launch({
           headless: true,
           args: ['--no-sandbox', '--disable-setuid-sandbox'],
       });
       page = await browser.newPage();
       await page.goto('http://localhost:7000/');
   });
   it('Should returned "Button Clicked!"', async function() {
       await page.click('input[type="submit"]');
       await new Promise(resolve => page.on('load', resolve));
       const message = await page.evaluate(() => document.querySelector('body').textContent);
       assert.equal(message, 'Button Clicked!');
   });
   after(async function() {
       await browser.close();
   });

});

Il sera appelé dans notre pipeline pour tester si le bouton fonctionne correctement après l’ajout de cette fonctionnalité.

C’est un script de test utilisant le framework de test de navigateur "Puppeteer" et le module de test "assert" de Node.js. Il teste une application web qui affiche un bouton "Click Me" et renvoie le message "Button Clicked!" lorsque ce bouton est cliqué.

L’application

'use strict';

const express = require('express')
const app = express();
const port = 7000;
const host = '0.0.0.0';
app.get('/', (req, res) => {
res.send('<html><body><h1>Bonjour Jenkins</h1><form action="/submit" method="post"><input type="submit" value="Click Me"></form></body></html>');
})
app.post('/submit', (req, res) => {
res.send('Button Clicked!');
});
app.listen(port, host);
console.log('Running on http://${host}:${port}');

Nous modifions le fichier app.js avec l’ajout du bouton, et surtout nous changeons le port pour 7000, car Jenkins écoute déjà sur le 8080.

Ce code crée une application web qui renvoie une page HTML, un bouton qui envoie une requête POST à une autre URL, qui va également renvoyer sa réponse en retour. 

C’est utilisé pour créer des interactions simples avec l'utilisateur via une interface web.

En se connectant au serveur sur le port que nous avons choisi précédemment qui est le 7000, vous pouvez voir à quoi ressemble notre application fictive. Notre but est ici d’automatiser la phase de build et test.

Notre premier pipeline Jenkins

Notre application étant maintenant configurée, nous passons à notre pipeline Jenkins:

Créer un nouveau pipeline en sélectionnant « New item » à gauche

Nous souhaitons créer un pipeline que nous appellerons « Wescale_Jenkins ».

pipeline {
   agent any

   stages {
       stage('Clone') {
           steps {
               git branch: 'main', url: '<FORK_DU_HELLO_WORLD_IBM>'
           }
       }
       stage('Build') {
           steps {
               sh 'npm install chromium'
               sh 'npm install chromedriver'
               sh 'npm install selenium-webdriver'
               sh 'npm install  puppeteer'
               sh 'npm install mocha'
               sh 'npm install'
           }
       }
       stage('Test') {
           steps {
               sh 'npm start &'
               sh 'npx mocha button.js'        
           }
       }
   }
}

Nous utilisons ce code dans la partie pipeline script, remplacez le lien du `git clone` avec votre lien du fork GitHub, avec les modifications faites précédemment.

À noter : Jenkins utilise son propre DSL (Domain Specific Language) en se basant sur le langage de script Groovy pour définir la configuration. Il permet de spécifier les étapes de build, de test et de déploiement d'une application, ainsi que les dépendances et les outils nécessaires à chaque étape.

Ce code automatise un processus de développement logiciel en utilisant des étapes définies dans les « stages » : Nous commençons par une première étape, intitulée « Clone », qui utilise la commande git pour cloner un dépôt de code à partir d'une URL spécifiée. Puis la deuxième étape,  « Build », utilise des commandes npm pour installer des dépendances nécessaires pour l'application, telles que chromium, chromedriver, selenium-webdriver, puppeteer, mocha. La troisième et dernière étape, « Test », utilise des commandes npm pour lancer l'application et exécuter les tests en utilisant mocha. 

Pour aller plus loin dans l’optimisation, nous pouvons mettre toute la partie « Build » dans un script qui sera appelé pour installer toutes les dépendances. Cette partie peut aussi être améliorée en mettant les applications comme dépendance du projet node dans le fichier package.json.

Les résultats de ces tests sont ensuite utilisés pour déterminer si l'application est prête à être déployée ou non.

Un dernier stage peut être ajouté comme le “Deploy” pour ajouter du CD (continuous delivery/deployment) dans le pipeline :

Cette étape devrait ressembler à ceci, vous pouvez sauvegarder et passer à la suite.

Pour finir, cliquez sur  « Build Now » et cliquez sur le numéro du build quand il se termine pour accéder à son résultat et à ses logs.

Dans la section « Console Output », nous pouvons voir que notre test a été un succès. Le pipeline a construit l’application, l’a lancée puis a testé la nouvelle fonctionnalité.

Note : Un stage « Deploy » est souvent ajouté pour automatiquement déployer notre nouveau livrable.

Pour les plus curieux

Dans les parties précédentes, nous avons vu un cas d'étude pour débuter, mais Jenkins peut aller beaucoup plus loin pour les utilisateurs expérimentés !

Il est par exemple possible d’utiliser un fichier Jenkinsfile au lieu de détailler le script du pipeline directement dans Jenkins (voir la section précédente), ce qui peut être intéressant pour les raisons suivantes : 

  • Ils peuvent être partagés et modifiés par plusieurs développeurs, ce qui permet une meilleure collaboration et une meilleure transparence sur les processus de déploiement.
  • Ils sont placés dans le code source de l’application et sont versionnés, cela permet de retrouver facilement l'historique des pipelines et de faire des annulations (« rollbacks ») en cas de besoin.

Pour utiliser le fichier Jenkinsfile que nous avons stocké sur un dépôt Git, il faut : 

  1. Choisir l’option « Pipeline script from SCM » :
  2. Choisir GIT comme SCM
  3. Mettre votre dépôt (le fork que nous avons fait précédemment)
  4. Choisir la branche dans laquelle le fichier sera disponible sur le dépôt, par exemple `main`
  5. Dans « Script Path » en bas, mettez le nom Jenkinsfile et, pour finir, enregistrez la nouvelle configuration.

Sur le dépôt git (ça peut être par exemple un dépôt sur GitHub), créez un nouveau fichier que vous allez nommer Jenkinsfile, mettez le script pipeline de test et enregistrez comme l’image ci-dessous : 

Enfin, nous testons le job modifié sur Jenkins, il ira chercher le Jenkinsfile sur le dépôt git (localisé sur GitHub dans notre exemple) pour faire les tests comme ci-dessous :

Nous pouvons également utiliser Jenkins as code (« JCasC ») qui simplifie la tâche complexe de configurer Jenkins et ses plugins en permettant à l'ensemble de la configuration d'être définie dans un format YAML ou JSON simple et facile à lire. 

Cela élimine la nécessité de réglages manuels et permet à la configuration d'être facilement mise en production. Avec JCasC, nous pouvons versionner et suivre l'historique des modifications apportées à la configuration de Jenkins, ce qui facilite la gestion des erreurs et des rollbacks en cas de besoin. De plus, cela permet de maintenir une configuration standardisée et de faciliter les déploiements sur de multiples environnements.

Il est aussi possible d'automatiser la création de jobs et de pipelines, ainsi que la gestion des utilisateurs et des rôles.

Les plugins sont des modules additionnels qui permettent à Jenkins de s'intégrer à d'autres outils et services. Il existe plus de 1000 plugins officiels sur le site web de Jenkins, chacun ayant des fonctionnalités spécifiques, ce qui va permettre d'étendre ses fonctions à l’infini.

Jenkins offre une intégration avec des outils de gestion de code source tels que Git, ce qui permet de déclencher automatiquement des builds via les webhooks et des tests en conséquence dès qu’un changement de code est détecté. Les utilisateurs peuvent également configurer des workflows de build complexes en utilisant des plugins tels que Jenkins Pipeline et Jenkins Workflow.

Conclusion

Jenkins est un outil incroyablement puissant pour les DevOps qui cherchent à automatiser les processus de test et de déploiement. Il permet de réduire les erreurs et de libérer du temps pour les tâches plus importantes. 

Cet article a donné un aperçu de l'utilisation de Jenkins en créant un premier pipeline CI/CD avec un scénario de la vie courante, et a montré les possibilités illimitées de l'outil pour aller plus loin.