samedi 11 février 2017

Introduction au pipeline de Jenkins

J'utilise Jenkins depuis quelques années. Un peu comme tout le monde je suis passé à la 2 au moment de sa sortie. Comme beaucoup, je n'ai pas vraiment vu de grosse différence avec les versions 1.6xx (si ce n'est que mes jobs ne fonctionnait plus mais ça, c'est une autre histoire).
Au bout d'un moment je suis tombé sur une pépite : les pipeline/workflow de Jenkins.

Mais avant de parler de ça, je vais vous parler de ce que je faisais traditionnellement.

Création de job et parallélisation


Pour résumé, je gère énormément d'installations ou de compilation en tout genre et Jenkins me sert d'esclave personnel : il lance des playbooks Ansible, des shells Unix ou des compilations maven (beurk !). Je colle généralement ces scripts dans le repository Git des projets et je les appelle depuis Jenkins. Le petit problème est que j'ai quand même à créer ces jobs et que je dois gérer l'enchaînement de ces éléments.

Fonction de la difficulté de la tâche, je passe soit par des déclenchements de jobs (natif Jenkins) soit carrément des jobs permettant d'orchestrer plusieurs lancements (plugin Multijob).

Pour ceux qui ne le connaîtrait pas, le plugin Multijob est tout de même bien fait et permet de très facilement gérer des lancements d'opérations en parallèle assez complexe (cf capture d'écran).

Exemple de Multijob
Malgré tout, ça implique que pour chaque phase, vous aurez un job à définir (qui ne vont pas contenir grand chose si ce n'est un appel à un playbook Ansible) ainsi qu'un job supplémentaire permettant de piloter le lancement des autres jobs et ceci depuis l'interface graphique de Jenkins.

Vous l'aurez compris, ça fait beaucoup d'opérations manuelles à réaliser et vous n'avez pas vraiment de moyen de faire ça proprement (sauf à bricoler les fichiers XML de Jenkins). Autre problème, vous ne savez pas qui modifie quoi à quel moment. Vous n'avez aucun moyen de savoir si une modification a été réalisée par quelqu'un qui pourrait expliquer le gros plantage de compilation au moment de la release de votre projet.

C'est là qu'on va voir en quoi les pipelines/workflows Jenkins peuvent répondre à ce type de problème.

Les pipelines sous Jenkins


Première chose à savoir, vous n'avez plus à aller dans l'interface Jenkins pour mettre à jour vos jobs : vous créez votre Job dans l'interface Jenkins (vivement qu'on puisse scripter ça proprement ...), vous associez votre repository de code, vous rentrez le nom du fichier décrivant le pipeline et c'est à peu près tout. Et le reste me direz-vous ? On va voir que le pipeline remplace ça avantageusement :

  • Besoin de récupérer la dernière version du code ? Utilisez le mot clé checkout scm ;
  • Besoin de lancer une tâche ? Le mot clé sh est là pour ça ;
  • Lancement en parallèle ? La fonction parallel est là pour ça !
Vous l'aurez compris, ça permet de remplacer à la fois vos micro jobs ainsi que le job chapeau de pilotage des autres jobs.
Autre chose, c'est du groovy ce qui permet de faire des choses relativement propre par rapport à du shell Unix. Il est même possible de faire des librairies de fonction pour réutiliser du code. C'est magique je vous avais dit !

Exemple de pipeline


Avant d'aller plus loin, voici un petit exemple de pipeline :
node() {
  // Étape 1
  stage('etapes-1') {
    parallel(
      'etape-1.1': {
        echo "Job 1 de l'étape 1"
      },
      'etape-1.2': {
        echo "Job 2 de l'étape 1"
      },
    )
  }
  // Étape 2
  stage('etapes-2') {
    echo "Job 1 de l'étape 2"
  }
  // Étape 3
  stage('etapes-3') {
    parallel(
      'etape-3.1': {
        // Cette opération doit prendre moins de 10 secondes
        timeout(time: 10, unit: 'SECONDS') {
          echo "Job 1 de l'étape 3"
        }
      },
      'etape-3.2': {
        echo "Job 2 de l'étape 3"
      },
      'etape-3.3': {
        echo "Job 3 de l'étape 3"
      },
    )
  }
}
Les sections parallel vous permettent de lancer x jobs en même temps. Jenkins attendra gentiment que tous les jobs finissent leur lancement. Vous avez peur qu'un de ces petits coquins ne prenne trop de temps et ne vous rende jamais la main ? Ajoutez une section timeout et vous êtes sauvé !
Vous avez envie de gérer la reprise sur incident ? C'est du groovy ! Ajoutez une section try/catch !

Intégration dans Jenkins


On a vu à quoi ressemblait le code de notre pipeline. On va maintenant voir comment l'intégrer dans Jenkins :


On y voit plusieurs opérations :
  • Création du job pipeline dans Jenkins ;
  • Lancement du job et visualisation du résultat ;
  • Affichage dans la nouvelle interface Jenkins Blue Ocean.
Ici, le code a été intégré directement dans l'interface Jenkins. On peut utiliser ce mécanisme dans le cadre d'une mise au point ou de test mais si vous voulez travailler proprement, rien n'égale l'intégration dans un repository Git. Ça tombe bien, on va voir ça dans le chapitre suivant.

Stockage du pipeline dans Git


Première chose, nous allons créer un repository Git avec notre code pipeline :
Création du repository :

$ mkdir git/test
$ cd git/test
$ git init .

Intégration du code :

$ git add Jenkinsfile
$ git commit -m "Ajout fichier Jenkinsfile d'exemple."
[master (commit racine) ccc0630] Ajout fichier Jenkinsfile d'exemple.
 1 file changed, 34 insertions(+)
 create mode 100644 Jenkinsfile

Reste maintenant à intégrer notre code dans l'interface de Jenkins de la manière suivante :


NB : Si vous appelez votre fichier de pipeline autrement que Jenkinsfile, il faudra le préciser au moment de la création de votre job.

Pour finir


Voilà, ça sera tout pour l'instant. Ça donne déjà un bon aperçu de ce qu'il est possible de faire. Dans un prochain article, j'essaierai de vous présenter une autre fonction très sympathique des pipelines : l'écriture de librairie pipeline (avec le mot clé load). En attendant, bonne intégration continue !