Jenkins Pipeline进阶 - 条件判断、并行执行与流程控制

Pipeline进阶

一、前言

上一篇我们学习了Pipeline的基础语法,能写出基本的构建流程了。但在实际工作中,构建流程往往更加复杂——你可能需要根据不同条件执行不同的步骤,或者让多个测试任务并行运行。本文将带你掌握Pipeline的进阶用法。

二、when指令 - 条件判断

when指令可以控制某个stage是否执行,这是Pipeline中最常用的条件控制方式。

2.1 基本用法

pipeline {
    agent any
    
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }
        
        stage('Deploy to Dev') {
            when {
                branch 'develop'
            }
            steps {
                sh './deploy-dev.sh'
            }
        }
        
        stage('Deploy to Prod') {
            when {
                branch 'main'
            }
            steps {
                sh './deploy-prod.sh'
            }
        }
    }
}

上面的例子中,当构建 develop 分支时,只执行Deploy to Dev;构建 main 分支时,只执行Deploy to Prod。

2.2 when条件类型

// 1. 分支判断
when { branch 'main' }
when { branch pattern: 'release/.*', comparator: 'REGEXP' }

// 2. 环境变量判断
when { environment name: 'DEPLOY_ENV', value: 'production' }

// 3. 表达式判断
when { expression { return env.BUILD_NUMBER.toInteger() % 2 == 0 } }
when { expression { return params.DEPLOY == true } }

// 4. 构建原因判断
when { buildingTag() }         // 构建标签时执行
when { tag 'v*' }              // 匹配标签模式
when { changelog '.*BUGFIX.*' } // 变更日志匹配
when { changeRequest() }        // PR/MR时执行
when { not { changeRequest() } } // 非PR/MR时执行

// 5. 组合条件
when {
    allOf {
        branch 'main'
        environment name: 'DEPLOY_ENV', value: 'production'
    }
}

when {
    anyOf {
        branch 'main'
        branch 'develop'
    }
}

when {
    not {
        branch 'test'
    }
}

2.3 实战示例 - 多环境部署

pipeline {
    agent any
    
    parameters {
        choice(name: 'ENV', choices: ['dev', 'staging', 'production'], description: '部署环境')
        booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: '跳过测试')
    }
    
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }
        
        stage('Test') {
            when {
                expression { return !params.SKIP_TESTS }
            }
            steps {
                sh 'mvn test'
            }
        }
        
        stage('Deploy Dev') {
            when {
                anyOf {
                    expression { return params.ENV == 'dev' }
                    expression { return params.ENV == 'staging' }
                    expression { return params.ENV == 'production' }
                }
            }
            steps {
                sh './deploy.sh dev'
            }
        }
        
        stage('Deploy Staging') {
            when {
                anyOf {
                    expression { return params.ENV == 'staging' }
                    expression { return params.ENV == 'production' }
                }
            }
            steps {
                sh './deploy.sh staging'
            }
        }
        
        stage('Deploy Production') {
            when {
                expression { return params.ENV == 'production' }
            }
            steps {
                input '确认部署到生产环境?'
                sh './deploy.sh production'
            }
        }
    }
}

三、并行执行 - parallel

当多个任务之间没有依赖关系时,可以让它们并行执行,大大缩短构建时间。

3.1 基本并行

pipeline {
    agent any
    
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package -DskipTests'
            }
        }
        
        stage('Parallel Tests') {
            parallel {
                stage('Unit Tests') {
                    steps {
                        sh 'mvn test'
                    }
                }
                stage('Integration Tests') {
                    steps {
                        sh 'mvn verify -P integration-test'
                    }
                }
                stage('Code Quality') {
                    steps {
                        sh 'mvn sonar:sonar'
                    }
                }
            }
        }
        
        stage('Deploy') {
            steps {
                sh './deploy.sh'
            }
        }
    }
}

3.2 并行中指定不同Agent

pipeline {
    agent none
    
    stages {
        stage('Build') {
            agent { label 'linux' }
            steps {
                sh 'mvn clean package -DskipTests'
                stash includes: 'target/*.jar', name: 'artifact'
            }
        }
        
        stage('Cross-Platform Tests') {
            parallel {
                stage('Linux Test') {
                    agent { label 'linux' }
                    steps {
                        unstash 'artifact'
                        sh 'java -jar target/*.jar test'
                    }
                }
                stage('Windows Test') {
                    agent { label 'windows' }
                    steps {
                        unstash 'artifact'
                        bat 'java -jar target/*.jar test'
                    }
                }
                stage('macOS Test') {
                    agent { label 'macos' }
                    steps {
                        unstash 'artifact'
                        sh 'java -jar target/*.jar test'
                    }
                }
            }
        }
    }
}

3.3 并行矩阵 - matrix

matrix指令是更高级的并行方式,适用于需要在不同配置组合下测试的场景:

pipeline {
    agent any
    
    stages {
        stage('Matrix Test') {
            matrix {
                axes {
                    axis {
                        name 'BROWSER'
                        values 'chrome', 'firefox', 'safari'
                    }
                    axis {
                        name 'PLATFORM'
                        values 'linux', 'windows', 'macos'
                    }
                }
                
                stages {
                    stage('Test') {
                        steps {
                            echo "Testing on ${PLATFORM} with ${BROWSER}"
                            sh "./run-test.sh ${PLATFORM} ${BROWSER}"
                        }
                    }
                }
                
                // 排除某些组合
                excludes {
                    exclude {
                        axis {
                            name 'BROWSER'
                            values 'safari'
                        }
                        axis {
                            name 'PLATFORM'
                            values 'linux', 'windows'
                        }
                    }
                }
            }
        }
    }
}

上面的matrix会生成9个并行任务(3浏览器 × 3平台),但排除了Safari在Linux和Windows上的组合,实际执行7个并行任务。

四、script块 - 脚本式语法

在Declarative Pipeline中,可以用script块嵌入Scripted Pipeline语法,实现更灵活的控制。

4.1 变量和条件

pipeline {
    agent any
    
    stages {
        stage('Set Config') {
            steps {
                script {
                    // 定义变量
                    def deployTarget = ''
                    if (env.BRANCH_NAME == 'main') {
                        deployTarget = 'production'
                    } else if (env.BRANCH_NAME == 'develop') {
                        deployTarget = 'staging'
                    } else {
                        deployTarget = 'dev'
                    }
                    env.DEPLOY_TARGET = deployTarget
                    echo "部署目标: ${deployTarget}"
                }
            }
        }
        
        stage('Deploy') {
            steps {
                sh "./deploy.sh ${env.DEPLOY_TARGET}"
            }
        }
    }
}

4.2 循环处理

pipeline {
    agent any
    
    stages {
        stage('Deploy Multiple Services') {
            steps {
                script {
                    def services = ['user-service', 'order-service', 'payment-service', 'notification-service']
                    
                    for (service in services) {
                        echo "部署 ${service}..."
                        sh "./deploy-service.sh ${service}"
                    }
                }
            }
        }
    }
}

4.3 错误处理

pipeline {
    agent any
    
    stages {
        stage('Deploy with Retry') {
            steps {
                script {
                    def maxRetries = 3
                    def retryCount = 0
                    
                    while (retryCount < maxRetries) {
                        try {
                            sh './deploy.sh'
                            echo '部署成功!'
                            break
                        } catch (Exception e) {
                            retryCount++
                            echo "部署失败,第 ${retryCount} 次重试..."
                            if (retryCount >= maxRetries) {
                                error "部署失败,已达最大重试次数"
                            }
                            sleep(time: 10, unit: 'SECONDS')
                        }
                    }
                }
            }
        }
    }
}

五、stash和unstash - 跨阶段传递文件

不同阶段的Agent可能不同,需要用stash/unstash传递构建产物。

pipeline {
    agent none
    
    stages {
        stage('Build') {
            agent { label 'builder' }
            steps {
                sh 'mvn clean package'
                // 保存构建产物
                stash includes: 'target/*.jar', name: 'build-artifact'
            }
        }
        
        stage('Test') {
            agent { label 'tester' }
            steps {
                // 取出构建产物
                unstash 'build-artifact'
                sh 'java -jar target/*.jar test'
            }
        }
        
        stage('Deploy') {
            agent { label 'deployer' }
            steps {
                unstash 'build-artifact'
                sh 'scp target/*.jar deploy@prod-server:/app/'
            }
        }
    }
}

六、options指令 - Pipeline选项

pipeline {
    agent any
    
    options {
        // 构建超时时间
        timeout(time: 1, unit: 'HOURS')
        
        // 构建重试次数
        retry(3)
        
        // 保留构建历史
        buildDiscarder(logRotator(numToKeepStr: '10'))
        
        // 禁止并发构建
        disableConcurrentBuilds()
        
        // 跳过默认的checkout
        skipDefaultCheckout()
        
        // 添加时间戳到日志
        timestamps()
        
        // 安静期(防止频繁触发)
        quietPeriod(10)
    }
    
    stages {
        stage('Build') {
            options {
                // 单个阶段也可以设置超时
                timeout(time: 30, unit: 'MINUTES')
            }
            steps {
                sh 'mvn clean package'
            }
        }
    }
}

七、triggers指令 - 触发器

pipeline {
    agent any
    
    // 定时触发
    triggers {
        cron('H 2 * * *')          // 每天凌晨2点
        pollSCM('H/5 * * * *')     // 每5分钟检查SCM
    }
    
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }
    }
}

八、实战 - 完整的多环境部署Pipeline

pipeline {
    agent any
    
    options {
        timeout(time: 2, unit: 'HOURS')
        timestamps()
        buildDiscarder(logRotator(numToKeepStr: '20'))
        disableConcurrentBuilds()
    }
    
    environment {
        APP_NAME = 'my-application'
        REGISTRY = 'registry.example.com'
        IMAGE_TAG = "${REGISTRY}/${APP_NAME}:${BUILD_NUMBER}"
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Build & Test') {
            parallel {
                stage('Compile') {
                    steps {
                        sh 'mvn clean package -DskipTests'
                    }
                }
                stage('Code Quality') {
                    steps {
                        sh 'mvn sonar:sonar'
                    }
                }
            }
        }
        
        stage('Unit Tests') {
            steps {
                sh 'mvn test'
            }
        }
        
        stage('Docker Build') {
            when {
                anyOf {
                    branch 'main'
                    branch 'develop'
                }
            }
            steps {
                sh "docker build -t ${IMAGE_TAG} ."
                sh "docker push ${IMAGE_TAG}"
            }
        }
        
        stage('Deploy Staging') {
            when {
                branch 'develop'
            }
            steps {
                sh "kubectl set image deployment/${APP_NAME} ${APP_NAME}=${IMAGE_TAG} -n staging"
            }
        }
        
        stage('Deploy Production') {
            when {
                branch 'main'
            }
            steps {
                input '确认部署到生产环境?'
                sh "kubectl set image deployment/${APP_NAME} ${APP_NAME}=${IMAGE_TAG} -n production"
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
        success {
            echo '🎉 Pipeline执行成功!'
        }
        failure {
            echo '❌ Pipeline执行失败,请检查!'
        }
    }
}

九、总结

本文详细介绍了Pipeline的进阶用法:

  • when指令:分支、环境变量、表达式等条件判断
  • parallel:并行执行多个无依赖的阶段,缩短构建时间
  • matrix:多配置组合的矩阵式并行
  • script块:嵌入脚本式语法,实现复杂逻辑
  • stash/unstash:跨阶段传递文件
  • options:超时、重试、日志等Pipeline选项
  • 实战案例:完整的多环境部署Pipeline

掌握了这些进阶技巧,你就可以写出专业级别的CI/CD流水线了!

发表回复

后才能评论