Jenkins pipeline avec Gitlab et sonarqube pour module NodeJS
Introduction
L’objectif de cet article est de montrer comment créer un pipeline Jenkins pour du code NodeJS.
Les technologies utilisées pour le versionning sont gitlab pour l’environnement local et github pour la production.
De plus, pour assurer une qualité du code et le respect des standards, l’outil sonarqube est utilisé.
Pré requis
- Serveur Gitlab
- Serveur Jenkins avec / sans worker
- NodeJS de disponible sur l’instance de Jenkins
- Serveur Sonarqube
Tous ces outils sont déployés en conteneur avec Docker
Pour Jenkins, mon setup comprend un jenkins Master déployé en Docker et une node installée directement sur un CentOS, toutes les commandes ci-dessous seront exécutées sur la node.
La node a plusieurs outils d’installés (voir annexe pour certains)
- NodeJS + NPM
- Sonarqube scanner
- Docker
- CA Certificate personnalisé
- Sendgrid
Étape 1 - Installer les plug-ins dans Jenkins
Il faut installer les plug-ins suivants
- git
- Gitlab
- Gitlab API
- credentials
- Gitlab authentication
- sonarqube scanner
- blue ocean plugin
Pour installer les plug-ins, allez
Manage Jenkins > Manage Plugins > Available
Puis vous pouvez utiliser le filtre en haut à droite.
Étape 2 - Installer sonarqube scanner sur la machine
Il faut télécharger ce fichier puis l’extraire,
cd /opt
wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.4.0.2170-linux.zip
unzip sonar-scanner-cli-4.4.0.2170-linux.zip
Étape 2.1 - OPTIONEL - Un CA Certificate personnalisé
Dans le cas où vous avez votre propre Certificat, vous pouvez l’ajouter comme ceci
keytool -import -noprompt -trustcacerts -file /etc/pki/ca-trust/source/anchors/cacert.pem -alias studiowebux -keystore /opt/sonar-scanner-4.4.0.2170-linux/jre/lib/security/cacerts -storepass changeit
Étape 2.2 - Exemple avec Ansible
Voici un script ansible pour configurer le tout,
- name: Download and extract sonarqube cli and add cacert
hosts: jenkins-worker
gather_facts: no
vars:
url: "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.4.0.2170-linux.zip"
ca_certificate_name: "studiowebux"
tasks:
- name: Download and extract sonar-scanner-cli
unarchive:
src: "{{ url }}"
dest: "/opt"
- name: Add cacert to java
shell: |
keytool -import -noprompt -trustcacerts -file /etc/pki/ca-trust/source/anchors/{{ ca_certificate_name }}.pem -alias studiowebux -keystore /opt/sonar-scanner-4.4.0.2170-linux/jre/lib/security/cacerts -storepass changeit
Étape 3 - Générer les tokens
Étape 3.1 - Sonarqube
Allez sur votre instance de sonarqube, puis cliquez sur Administration
Puis choisissez le menu Security et cliquez sur User
Dans ma configuration, seulement le compte administrateur existe, alors vous devez cliquer sur les trois barres pour générer un token.
Donner un nom au token, puis cliquez sur Generate
Ne perdez pas le token, sauvegarder le à un endroit sécuritaire, après avoir fermé la fenêtre, vous ne pourrez plus le voir.
Dans le cas où votre token a été découvert, vous pouvez le révoquer et en générer un nouveau.
Voici un exemple
Étape 3.2 - Gitlab
Sur gitlab, cliquez sur votre profile en haut à droite, puis choisissez Settings
Dans le menu de gauche, cliquez sur Access Token
Donner un nom à votre access token
puis cocher l’option API, ensuite Create new access token
Ne perdez pas le token, sauvegarder le à un endroit sécuritaire, après avoir fermé la fenêtre, vous ne pourrez plus le voir.
Dans le cas où votre token a été découvert, vous pouvez le révoquer et en générer un nouveau.
Étape 4 - Configurer Jenkins
Étape 4.1 - Créer les credentials dans jenkins
Manage Jenkins > Manage Credentials
Cliquez sur le lien (global) ( dans la deuxième section de la page )
Cliquez sur Add Credentials
Gitlab
Pour l’identifiant gitlab, choisissez Gitlab API Token puis copier votre access token dans le champ API token Assurez-vous de mettre un ID significatif
Sonarqube
Pour l’identifiant sonarqube, choisissez Secret Text puis copier votre token dans le champ secret Assurez-vous de mettre un ID significatif
Github
Pour cet identifiant je vous recommande de générer une clé SSH sur Github, pour faire mes tests, j’ai simplement entré le username/password
Voici ce que vous devriez avoir
Étape 4.2 - Configurer Jenkins et les plug-ins
Allez à Manage Jenkins > Configure System
Étape 4.2.1 - Configurer sonarqube
Voici ma configuration, vous devez modifier le tout pour ajuster avec votre configuration.
le nom que vous configurez est important il sera utilisé dans le jenkinsfile Pour l’authentification, choissisez le token qui a été créé à l’étape précédente
Pour terminer la configuration de sonarqube, il faut définir le répertoire où se trouve l’outil (/opt/sonar-scanner-cli-4.4.0.2170-linux
)
Naviguer à Manage Jenkins > Global Tool Configuration > la section Sonarqube Scanner
Voici ma configuration
Pour continuer la configuration, il faut retourner à la page Manage Jenkins > Configure System
Étape 4.2.2 - Configurer Gitlab
Voici ma configuration, vous devez modifier le tout pour ajuster avec votre configuration.
Même chose, choissisez le token précédemment créé,
Étape 4.2.3 - Configurer sendgrid
Voici ma configuration
Étape 5 - Création d’un Jenkinsfile
Voici un exemple avec un de mes modules NodeJS
pipeline {
agent {
node {
label 'nodejs'
}
}
stages {
stage('Preparation') {
steps {
sh 'git remote add prod https://github.com/studiowebux/webux-sql.git || true'
}
}
stage('Dependencies') {
steps {
sh 'npm install'
}
}
stage('Lint') {
steps {
sh 'npm run-script lint'
}
}
stage('Code Analysis') {
steps {
script {
def scannerHome = tool 'sonarqube';
withSonarQubeEnv("sonarqube") {
sh "${tool("sonarqube")}/bin/sonar-scanner"
}
}
}
}
stage('Test') {
steps {
sh 'npm run test'
}
}
stage('Versionning') {
steps {
script {
env.RELEASE_SCOPE = input message: 'User input required', ok: 'Continue',
parameters: [choice(name: 'RELEASE_SCOPE', choices: 'patch\nminor\nmajor', description: 'What is the release scope?')]
}
echo "${env.RELEASE_SCOPE}"
}
}
stage('Staging') {
steps {
withCredentials([usernamePassword(credentialsId: 'git:1f00e77842774986a932a1367b515be6efb49cae2d1a134a1988a651d8ff094b', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]){
sh('''
git config --local credential.helper "!f() { echo username=\\$GIT_USERNAME; echo password=\\$GIT_PASSWORD; }; f"
git push origin master
git config --unset credential.helper
''')
}
withCredentials([usernamePassword(credentialsId: 'GitHub', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]){
sh('''
git config --local credential.helper "!f() { echo username=\\$GIT_USERNAME; echo password=\\$GIT_PASSWORD; }; f"
git push prod master
git config --unset credential.helper
''')
}
sh "npm version ${env.RELEASE_SCOPE}"
sh 'npm publish --registry=https://npm.webux.lab'
input 'Deploy to production ?'
}
}
stage('Production') {
steps {
withCredentials([usernamePassword(credentialsId: 'git:1f00e77842774986a932a1367b515be6efb49cae2d1a134a1988a651d8ff094b', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]){
sh('''
git config --local credential.helper "!f() { echo username=\\$GIT_USERNAME; echo password=\\$GIT_PASSWORD; }; f"
git push origin master
git config --unset credential.helper
''')
}
withCredentials([usernamePassword(credentialsId: 'GitHub', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]){
sh('''
git config --local credential.helper "!f() { echo username=\\$GIT_USERNAME; echo password=\\$GIT_PASSWORD; }; f"
git push prod master
git config --unset credential.helper
''')
}
sh 'npm publish --access public'
mail(to: 'tommy@studiowebux.com', subject: 'Webux-sql - Published', body: 'Webux-sql has been published to production')
}
}
}
post {
failure {
mail to: 'tommy@studiowebux.com',
subject: "Failed Pipeline ${currentBuild.fullDisplayName}",
body: " For details about the failure, see ${env.BUILD_URL}"
sh 'git config --unset credential.helper'
}
}
}
Ce pipeline fait plusieurs choses,
- Installer les dépendances directement sur le worker
- Vérifier le lint en utilisant eslint
- Lancer les tests en utilisant jest
- Lancer le scanner de sonarqube
- Demander quelle version à déployer (patch | minor | major)
- Deployer le code sur le npm local
- Demander si on veut déployer en production (sur npmjs)
Voici un build réussi
Étape 6 - Ajouter ce pipeline à votre Jenkins
Il faut aller dans Open Blue Ocean (à partir du menu de gauche)
Cliquer sur New Pipeline
Choisissez git et entrer l’URL de votre repository ensuite choisissez l’identifiant qui a été ajouté plus tot.
Et pour créer le tout, cliquer sur Create Pipeline
Conclusion
Voilà !
Vous avez maintenant un Jenkins pipeline intégré avec Gitlab, GitHub, Sonarqube, Jest, eslint et tout ce dont vous voulez ajouter.
C’est la première fois que je travaille avec les pipelines, je vais continuer d’explorer le tout et ajouter du contenu sur mes découvertes.
Annexe
Scripts Ansible
Configuration initiale du worker
- name: Install Utilities
hosts: jenkins-worker
gather_facts: no
tasks:
- name: Install Tools and Utilities
package:
name:
- nano
- git
- unzip
- net-tools
- tmux
state: latest
- name: Install NodeJS repository
shell: "curl -sL https://rpm.nodesource.com/setup_12.x | bash -"
- name: Install NodeJS
yum:
name: nodejs
state: latest
Ajout du CA certificate pour NPM local (verdaccio)
- name: Add the CA certificate to npm
hosts: jenkins-worker
gather_facts: no
vars:
ca_certificate_name: "studiowebux_CA"
tasks:
- name: add the CA certificate to NPM
shell: "npm config set cafile /etc/pki/ca-trust/source/anchors/{{ ca_certificate_name }}.pem"
- name: Validate the CA certificate
shell: "npm config get cafile"
register: cafile_output
- debug:
msg: "{{ cafile_output }}"
- name: Set the registry to npmjs
shell: "npm config set registry http://registry.npmjs.org/"
Installation et configuration personnalisée de Docker
- name: Install and Configure Docker CE on CentOS / RHEL
hosts: docker
gather_facts: no
vars:
srv_DOCKER: "/srv/DOCKER"
# Be sure to specify a range that will not interfere with libvirt or other networks
docker_network: "--default-address-pool base=172.24.0.0/13,size=24"
tasks:
- name: Create `{{ srv_DOCKER }}/data` directory
file:
path: "{{ srv_DOCKER }}/data"
state: directory
- name: Install `dependencies`
package:
name:
- yum-utils
- device-mapper-persistent-data
- lvm2
state: present
- name: Add `docker-ce` repository
shell: |
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
- name: Install `docker-ce`
package:
name:
- docker-ce
- docker-ce-cli
- containerd.io
state: present
# https://linuxconfig.org/how-to-move-docker-s-default-var-lib-docker-to-another-directory-on-ubuntu-debian-linux
- name: Configure docker data directory to `{{ srv_DOCKER }}/data` and configure the network
lineinfile:
path: /lib/systemd/system/docker.service
regexp: "ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock"
line: "ExecStart=/usr/bin/dockerd -g {{ srv_DOCKER }}/data -H fd:// --containerd=/run/containerd/containerd.sock {{ docker_network }}"
- name: Remove `/var/lib/docker`
file:
path: "/var/lib/docker/"
state: absent
- name: Enable and Start Docker
systemd:
daemon_reload: yes
name: docker
state: started
enabled: yes
- name: Install `docker-compose` 1.26.0
shell: 'curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose'
- name: Set `docker-compose` Permission
file:
path: "/usr/local/bin/docker-compose"
mode: 0755
- name: Create link to /usr/bin/docker-compose
file:
src: "/usr/local/bin/docker-compose"
dest: "/usr/bin/docker-compose"
state: link
# This module was causing issue with libvirtd
# But after some reading, the conflict was caused by the default docker network.
- name: Blacklist br_netfilter module
shell: "echo install br_netfilter /bin/true > /etc/modprobe.d/disable-br-netfilter.conf"
- debug:
msg: "Add your user to docker group like that : `sudo usermod -aG docker [your-user]`"