Aller au contenu principal
JenkinsCI/CDDevOps

Jenkins #2 — Premier Pipeline : Jenkinsfile et Pipeline Déclaratif

30 min de lecture Jenkins — Chapitre 2

Apprends à écrire ton premier Jenkinsfile, maîtrise le pipeline déclaratif avec stages, steps et post, et découvre Blue Ocean UI.

Les jobs Freestyle de Jenkins sont bien pour débuter, mais en production ils ne tiennent pas : pas versionnés, pas reproductibles, pas reviewables. Le Pipeline as Code résout tout ça — tu décris ton CI/CD dans un fichier Jenkinsfile versionné dans ton repo Git. C’est la base de tout Jenkins moderne.

Pipeline déclaratif : la syntaxe complète

Le pipeline déclaratif est le format recommandé. Sa structure est rigide mais lisible — parfait pour les équipes. Voici le squelette complet :

pipeline {
    agent any

    environment {
        APP_NAME = 'mon-app'
        GIT_HASH = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
        DOCKER_CREDS = credentials('docker-hub-creds')
    }

    options {
        timeout(time: 30, unit: 'MINUTES')
        buildDiscarder(logRotator(numToKeepStr: '10'))
        timestamps()
        disableConcurrentBuilds()
    }

    parameters {
        choice(name: 'ENVIRONMENT', choices: ['staging', 'production'], description: 'Cible')
        booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: 'Passer les tests')
    }

    stages {
        stage('Build') {
            steps {
                sh 'npm ci'
                sh 'npm run build'
            }
        }
        stage('Test') {
            steps {
                sh 'npm test -- --coverage'
            }
            post {
                always { junit 'reports/**/*.xml' }
            }
        }
        stage('Deploy') {
            when { branch 'main' }
            steps {
                sh "./deploy.sh ${params.ENVIRONMENT}"
            }
        }
    }

    post {
        success { slackSend color: 'good', message: "✅ ${env.JOB_NAME} #${env.BUILD_NUMBER} OK" }
        failure { slackSend color: 'danger', message: "❌ ${env.JOB_NAME} #${env.BUILD_NUMBER} FAILED" }
        always { cleanWs() }
    }
}

💡 Chaque section a un rôle précis : environment pour les variables, options pour le comportement global, parameters pour rendre le pipeline interactif, stages pour les étapes, et post pour les actions conditionnelles après l’exécution.

Agent : où ça tourne

L’agent définit l’environnement d’exécution. Tu peux l’assigner globalement ou par stage — c’est ce qui donne à Jenkins sa flexibilité.

// N'importe quel agent disponible
agent any

// Agent avec label (ex: machine avec Docker)
agent { label 'linux && docker' }

// Conteneur Docker (isolé, reproductible)
agent {
    docker {
        image 'node:20-alpine'
        args '-v /tmp:/tmp'
    }
}

// Pod Kubernetes (éphémère, scalable)
agent {
    kubernetes {
        yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          containers:
          - name: node
            image: node:20
            command: ['sleep', 'infinity']
        '''
    }
}

🔥 En production, utilise agent { docker { ... } } ou agent { kubernetes { ... } } pour l’isolation. Un agent any sur un nœud partagé finit toujours par créer des conflits de dépendances entre projets.

When : exécution conditionnelle des stages

La directive when contrôle si un stage s’exécute. C’est indispensable pour les pipelines multi-branches.

stages {
    stage('Deploy Staging') {
        when { branch 'develop' }
        steps { sh './deploy.sh staging' }
    }

    stage('Deploy Production') {
        when {
            allOf {
                branch 'main'
                not { changeRequest() }
            }
        }
        steps { sh './deploy.sh production' }
    }

    stage('Tagged Release') {
        when {
            tag pattern: 'v\\d+\\.\\d+\\.\\d+', comparator: 'REGEXP'
        }
        steps { sh './release.sh' }
    }

    stage('Feature Test') {
        when {
            expression { return params.SKIP_TESTS == false }
        }
        steps { sh 'npm test' }
    }
}

⚠️ Sans when, chaque stage s’exécute systématiquement. Sur un Multibranch Pipeline, ça veut dire que ta feature branch tente de déployer en prod — pas idéal.

Pipeline complet : app Node.js réaliste

Voici un Jenkinsfile production-ready qui build, test, push une image Docker, et déploie :

pipeline {
    agent none

    environment {
        REGISTRY = 'registry.company.com'
        IMAGE = 'mon-app'
    }

    stages {
        stage('Quality') {
            parallel {
                stage('Lint') {
                    agent { docker { image 'node:20-alpine' } }
                    steps {
                        sh 'npm ci && npm run lint'
                    }
                }
                stage('Unit Tests') {
                    agent { docker { image 'node:20-alpine' } }
                    steps {
                        sh 'npm ci && npm test -- --coverage'
                    }
                    post {
                        always { junit 'reports/junit.xml' }
                    }
                }
                stage('Security') {
                    agent { docker { image 'node:20-alpine' } }
                    steps {
                        sh 'npm ci && npm audit --audit-level=high'
                    }
                }
            }
        }

        stage('Build & Push Image') {
            agent { label 'docker' }
            steps {
                script {
                    def tag = "${BUILD_NUMBER}-${GIT_COMMIT.take(7)}"
                    withCredentials([usernamePassword(
                        credentialsId: 'registry-creds',
                        usernameVariable: 'USER',
                        passwordVariable: 'PASS'
                    )]) {
                        sh """
                            docker build -t ${REGISTRY}/${IMAGE}:${tag} .
                            echo \$PASS | docker login ${REGISTRY} -u \$USER --password-stdin
                            docker push ${REGISTRY}/${IMAGE}:${tag}
                        """
                    }
                    env.IMAGE_TAG = tag
                }
            }
        }

        stage('Deploy') {
            agent { label 'kubectl' }
            when { branch 'main' }
            steps {
                sh "kubectl set image deployment/${IMAGE} app=${REGISTRY}/${IMAGE}:${env.IMAGE_TAG}"
            }
        }
    }

    post {
        success { slackSend color: 'good', message: "✅ ${JOB_NAME} #${BUILD_NUMBER}" }
        failure { slackSend color: 'danger', message: "❌ ${JOB_NAME} #${BUILD_NUMBER}" }
    }
}

🎯 Note le parallel dans le stage Quality — lint, tests et audit de sécurité tournent en même temps. Sur un pipeline de 10 minutes, ça peut te faire gagner 5 minutes de feedback.

Multibranch Pipeline : un pipeline par branche

Le Multibranch Pipeline scanne ton repo Git, détecte toutes les branches avec un Jenkinsfile, et crée automatiquement un pipeline par branche. C’est le type de job à utiliser en production.

Configuration : New ItemMultibranch PipelineBranch Sources : ton repo Git → Build Configuration : Jenkinsfile. Jenkins scanne et crée :

mon-projet/
├── main         → pipeline de la branche main
├── develop      → pipeline de la branche develop
├── feature/auth → pipeline de la feature branch
└── PR-42        → pipeline de la Pull Request #42

Chaque branche a son historique de builds indépendant. Les branches supprimées = pipelines nettoyés automatiquement.

💡 Configure un webhook GitHub/GitLab plutôt que le polling SCM. Le polling toutes les minutes génère du trafic inutile et ajoute du délai. Le webhook déclenche le build instantanément au push.

Cas entreprise, pièges et résumé

Une équipe de 15 développeurs avait 45 jobs Freestyle Jenkins. Chaque job était configuré manuellement dans l’UI — pas de versioning, pas de review. Un jour, le serveur Jenkins a crashé. La restauration du backup a pris 4 heures, et 8 jobs avaient des configs corrompues.

Leur migration vers Pipeline as Code :

  • Semaine 1 : conversion des 10 jobs les plus critiques en Jenkinsfiles déclaratifs
  • Semaine 2 : passage en Multibranch Pipeline avec webhook GitHub
  • Semaine 3 : ajout de stages parallèles (lint + test + security scan)
  • Résultat : temps de feedback moyen passé de 18 min à 7 min, zéro perte de config possible (tout est dans Git)

Les pièges à éviter :

⚠️ Le Replay abusé — Jenkins permet de rejouer un build avec un Jenkinsfile modifié sans commit. Pratique pour débugger, mais certaines équipes l’utilisent comme workflow permanent. Résultat : le Jenkinsfile dans Git ne correspond plus à ce qui tourne vraiment. Utilise Replay uniquement pour le debug, puis commit la correction.

⚠️ Les credentials dans les logs — Un sh "echo ${DOCKER_PASSWORD}" affiche le mot de passe en clair dans les logs. Utilise toujours withCredentials ou credentials() dans le bloc environment — Jenkins les masque automatiquement.

🔥 Valide avant de push — L’API Jenkins peut valider la syntaxe de ton Jenkinsfile :

curl -X POST -F "jenkinsfile=<Jenkinsfile" \
  http://jenkins.company.com/pipeline-model-converter/validate

⚠️ Blue Ocean en maintenance — Blue Ocean offre une UI moderne mais est en maintenance mode depuis 2023. Utilise-le pour la visualisation, pas comme outil principal. L’UI classique reste la référence pour la config.

Le Pipeline as Code transforme Jenkins d’un outil fragile en une solution robuste. Le Jenkinsfile déclaratif couvre 90% des besoins : agent pour l’environnement, stages pour les étapes, when pour les conditions, post pour les actions post-build, et parallel pour la vitesse. Le Multibranch Pipeline automatise la gestion des branches. Versionne tout dans Git — ton CI/CD devient aussi auditable que ton code.

🖥️ 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.

Obtenir 200$

Articles liés