Le pipeline déclaratif couvre 90% des besoins. Pour les 10% restants — logique dynamique, code réutilisable entre projets, matrices de test — il faut monter en puissance. Ce cours couvre le pipeline scripté, les Shared Libraries, les stages parallèles, et les approval gates.
Scripté vs déclaratif : quand choisir lequel
Le pipeline scripté utilise du Groovy pur. Pas de structure imposée, pas de pipeline {} — juste un node {} et du code. C’est puissant mais moins lisible.
node('linux') {
try {
stage('Checkout') { checkout scm }
stage('Build') {
sh 'npm ci'
sh 'npm run build'
}
stage('Test') { sh 'npm test' }
if (env.BRANCH_NAME == 'main') {
stage('Deploy') { sh './deploy.sh production' }
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
slackSend color: 'danger', message: "❌ ${env.JOB_NAME} échoué"
throw e
} finally {
cleanWs()
}
}
💡 Le meilleur compromis : reste en déclaratif et utilise script {} pour les blocs qui nécessitent du Groovy (boucles, conditions complexes). Ça garde la lisibilité tout en permettant la flexibilité.
⚠️ Si tu te retrouves avec script {} dans chaque stage, c’est un signal : extrais la logique dans une Shared Library plutôt que de polluer le Jenkinsfile.
Shared Libraries : le code pipeline réutilisable
Le problème classique : 15 microservices avec des Jenkinsfiles quasi identiques. Un changement de logique = 15 fichiers à modifier. La Shared Library centralise le code pipeline dans un repo Git dédié.
Structure
jenkins-shared-lib/
├── vars/ # Fonctions globales (le plus utilisé)
│ ├── buildDocker.groovy
│ ├── notifySlack.groovy
│ └── standardPipeline.groovy
├── src/ # Classes Groovy (logique complexe)
│ └── com/example/
│ └── Docker.groovy
└── resources/ # Fichiers statiques (templates)
Créer une fonction réutilisable
Fichier vars/buildDocker.groovy — chaque fichier dans vars/ devient une fonction appelable directement dans les Jenkinsfiles :
def call(Map config = [:]) {
def image = config.imageName ?: error("imageName requis")
def registry = config.registry ?: 'registry.company.com'
def tag = config.tag ?: env.BUILD_NUMBER
echo "🐳 Building ${registry}/${image}:${tag}"
sh "docker build -t ${registry}/${image}:${tag} ."
withCredentials([usernamePassword(
credentialsId: config.credentialsId ?: 'registry-creds',
usernameVariable: 'USER', passwordVariable: 'PASS'
)]) {
sh "echo \$PASS | docker login ${registry} -u \$USER --password-stdin"
sh "docker push ${registry}/${image}:${tag}"
}
return "${registry}/${image}:${tag}"
}
Créer un pipeline template complet
Le pattern le plus puissant : un template dans vars/standardPipeline.groovy qui encapsule tout le workflow.
def call(Map config = [:]) {
pipeline {
agent none
options {
timeout(time: config.timeout ?: 30, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '10'))
timestamps()
}
stages {
stage('Test') {
agent { docker { image config.buildImage ?: 'node:20-alpine' } }
steps {
checkout scm
sh config.testCommand ?: 'npm ci && npm test'
}
}
stage('Build & Push') {
agent any
steps {
checkout scm
buildDocker(imageName: config.imageName, registry: config.registry)
}
}
stage('Deploy') {
agent any
when { branch 'main' }
steps {
sh config.deployCommand ?: "kubectl apply -f k8s/"
}
}
}
post {
success { slackSend color: 'good', message: "✅ ${env.JOB_NAME} #${env.BUILD_NUMBER}" }
failure { slackSend color: 'danger', message: "❌ ${env.JOB_NAME} #${env.BUILD_NUMBER}" }
}
}
}
Résultat : chaque microservice a un Jenkinsfile de 10 lignes :
@Library('my-shared-lib') _
standardPipeline(
imageName: 'mon-service-api',
buildImage: 'node:20-alpine',
testCommand: 'npm ci && npm test -- --coverage',
timeout: 20
)
🔥 Un changement dans la lib = tous les pipelines mis à jour au prochain build. Configure la lib dans Manage Jenkins → System → Global Pipeline Libraries avec le repo Git et la branche par défaut.
Stages parallèles et matrix
Les stages parallèles exécutent plusieurs branches simultanément. Le gain de temps est énorme sur les suites de tests.
stage('Quality Gates') {
failFast true
parallel {
stage('Unit Tests') {
agent { docker { image 'node:20' } }
steps { sh 'npm run test:unit' }
}
stage('Integration Tests') {
agent { docker { image 'node:20' } }
steps { sh 'npm run test:integration' }
}
stage('E2E Tests') {
agent { docker { image 'cypress/included:13' } }
steps { sh 'npm run test:e2e' }
}
stage('Security Scan') {
agent { docker { image 'node:20' } }
steps { sh 'npm audit --audit-level=high' }
}
}
}
failFast true stoppe tous les stages parallèles dès qu’un seul échoue — pas besoin d’attendre les 3 autres si les unit tests cassent en 30 secondes.
Pour les matrices de compatibilité (Node 18/20/22 × Linux/Windows), la directive matrix génère automatiquement les combinaisons :
stage('Compatibility') {
matrix {
axes {
axis { name 'NODE_VERSION'; values '18', '20', '22' }
axis { name 'OS'; values 'linux', 'windows' }
}
excludes {
exclude {
axis { name 'NODE_VERSION'; values '18' }
axis { name 'OS'; values 'windows' }
}
}
stages {
stage('Test') {
agent { label "${OS}" }
steps { sh "nvm use ${NODE_VERSION} && npm test" }
}
}
}
}
🎯 5 combinaisons testées en parallèle (18/windows exclu). Sans matrix, ça serait 5 stages copié-collés.
Input et Approval Gates
L’input pause le pipeline et attend une action humaine. Indispensable pour les déploiements production qui nécessitent une validation.
stage('Approve Production') {
agent none // Libère l'executor pendant l'attente
options { timeout(time: 1, unit: 'HOURS') }
steps {
script {
def approval = input(
message: 'Déployer en production ?',
ok: '🚀 Deploy!',
submitter: 'ops-team,release-managers',
parameters: [
choice(name: 'STRATEGY', choices: ['rolling', 'blue-green', 'canary'])
]
)
env.DEPLOY_STRATEGY = approval
}
}
}
stage('Deploy Production') {
agent any
steps {
sh "./deploy.sh production --strategy=${env.DEPLOY_STRATEGY}"
}
}
⚠️ agent none sur le stage d’input est critique — sans ça, l’executor est bloqué pendant toute l’attente. Avec 4 executors et 4 builds en attente d’approbation, plus rien ne tourne.
💡 Utilise milestone pour éviter les race conditions : si le build #5 est approuvé avant le #4, le milestone annule automatiquement le #4 pour ne pas écraser le déploiement plus récent.
Cas entreprise : 40 microservices, 1 Shared Library
Une plateforme SaaS avec 40 microservices — Java, Node.js, Python. Avant la Shared Library : 40 Jenkinsfiles de 80-150 lignes chacun, maintenus par chaque équipe. Résultat : des incohérences partout (certains pushaient des images sans tag, d’autres oubliaient le scan de sécurité).
Après la migration :
- 1 Shared Library avec 3 templates :
javaPipeline,nodePipeline,pythonPipeline - Chaque template inclut : build → test → scan SAST → Docker build/push → deploy staging auto → approval → deploy prod
- 40 Jenkinsfiles de 8-15 lignes qui appellent le bon template avec leurs paramètres
Un audit sécurité a demandé d’ajouter un scan Trivy sur toutes les images. Changement dans la lib : 1 commit, 1 review, 1 merge. Les 40 services ont eu le scan Trivy au build suivant, sans toucher un seul Jenkinsfile.
Pièges classiques et résumé
⚠️ La sandbox Groovy — Jenkins exécute les Jenkinsfiles dans une sandbox qui bloque certaines méthodes Groovy. Les Shared Libraries dans vars/ tournent hors sandbox (trusted), mais src/ est sandboxé par défaut. Si tu as des RejectedAccessException, configure les script approvals dans Manage Jenkins → In-process Script Approval.
⚠️ Le stash trop gros — stash stocke les fichiers sur le controller Jenkins. Pour des artifacts de 500MB+, c’est un goulot d’étranglement. Utilise archiveArtifacts ou un stockage externe (S3, Nexus, Artifactory) pour les gros fichiers.
🔥 Le versionning de la lib — En prod, pin ta Shared Library sur un tag : @Library('my-lib@v2.1.0'). Ne pointe jamais sur main en production — un merge accidentel dans la lib casserait tous tes pipelines simultanément.
Le pipeline avancé Jenkins repose sur trois piliers. Les Shared Libraries centralisent la logique et transforment 40 Jenkinsfiles verbeux en 40 appels de 10 lignes. Les stages parallèles et matrix divisent le temps de feedback. Les approval gates avec input + milestone sécurisent les déploiements production. Combine les trois pour un CI/CD maintenable, rapide et fiable.
🖥️ Pratique sur ton propre serveur
Pour suivre Jenkins en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.
Contenu réservé aux abonnés
Ce chapitre fait partie de la formation complète. Abonne-toi pour débloquer tous les contenus.
Débloquer pour 29 CHF/moisLe chapitre 1 de chaque formation est gratuit.
Série pas encore débloquée
Termine la série prérequise d'abord pour accéder à ce contenu.
Aller à la série prérequiseSérie : Jenkins
3 / 8- Jenkins #1 — Introduction : Pourquoi Jenkins, Architecture et Premier Job
- Jenkins #2 — Premier Pipeline : Jenkinsfile et Pipeline Déclaratif
- 3 Jenkins #3 — Pipeline Avancé : Scripté, Shared Libraries et Parallel Stages
- 4 Jenkins #4 — Plugins Essentiels : Docker, K8s, Git, SonarQube et Slack
- 5 Jenkins #5 — Jenkins et Docker : Build d'Images, DinD et Kaniko
- 6 Jenkins #6 — Jenkins sur Kubernetes : Helm, Pod Templates et Scaling
- 7 Jenkins #7 — Sécurité : RBAC, Credentials, Audit Trail et Hardening
- 8 Jenkins #8 — Production : HA, Backup, Monitoring et Migration
Sur cette page
Articles liés
Jenkins #1 — Introduction : Pourquoi Jenkins, Architecture et Premier Job
Découvre Jenkins de A à Z : pourquoi c'est le standard CI/CD, l'architecture master/agent, l'installation sur Ubuntu et ton premier job freestyle.
Jenkins #2 — Premier Pipeline : Jenkinsfile et Pipeline Déclaratif
Apprends à écrire ton premier Jenkinsfile, maîtrise le pipeline déclaratif avec stages, steps et post, et découvre Blue Ocean UI.
Jenkins #4 — Plugins Essentiels : Docker, K8s, Git, SonarQube et Slack
Tour d'horizon des plugins Jenkins incontournables : Docker, Kubernetes, Git, SonarQube, Slack, credentials binding et leur configuration complète.