Skip to content

Jenkins-Pipeline流水线

核心概念

  1. Pipeline(流水线):代表整个CI/CD流程,通常通过一个名为 Jenkinsfile 的文本文件定义。这个文件可以存储在项目的源代码仓库中,实现“Pipeline as Code”(流水线即代码),便于版本控制和团队协作。
  2. Stage(阶段):Pipeline 中的逻辑分组单元,代表流程中的一个大的步骤,如构建、测试、部署等。每个 Stage 包含一系列具体的 Step。
  3. Step(步骤):Pipeline 中最小的可执行单元,代表一个具体的操作,例如执行一条 Shell 命令、拉取代码、发送邮件等。
  4. Agent(代理):指定 Pipeline 或特定 Stage 在哪个 Jenkins 节点(Master 或 Agent)上执行。你可以指定标签或使用 Docker 容器作为运行环境。
  5. Post(后置处理):定义 Pipeline 或 Stage 执行完成后,根据结果(成功、失败、总是等)执行的操作,例如清理资源、发送通知。

特性

Jenkins Pipeline 的强大之处在于其丰富的高级特性:

  • 参数化构建:允许在触发 Pipeline 时传入参数,增加灵活性。
  • 并行执行parallel 指令可以让多个 Stage 或 Step 同时执行,显著缩短整体执行时间。
  • 条件执行when 指令可以根据条件(如分支、变量值等)决定是否执行某个 Stage。
  • 异常处理:通过 try-catch 块捕获和处理异常,提高流程的健壮性。
  • 集成凭证管理:安全地使用密码、API 密钥等敏感信息,避免硬编码在脚本中。

Jenkinsfile 的优势

将 Pipeline 定义在 Jenkinsfile 并存入代码库有诸多好处:

  • 版本化与可追溯:Pipeline 的变更历史与代码变更历史一同管理。
  • 协作与复审:团队成员可以像审查代码一样审查 CI/CD 流程。
  • 单一可信源:为所有分支和拉取请求自动提供一致的构建流程。
  • 可移植性:易于在多个 Jenkins 实例间迁移或复制项目。

核心结构

Pipeline 两种语法

特性声明式 Pipeline (Declarative Pipeline)脚本式 Pipeline (Scripted Pipeline)
结构结构固定,更严格,必须包含在 pipeline {}块内。结构灵活,基于 Groovy 语法,最外层由 node {}包裹。
语法提供更简洁、直观的语法,每个语句通常独立一行,行尾无需分号。可以直接使用 Groovy 脚本及其语法结构(如循环、条件判断)。
学习曲线易于上手,适合初学者。需要一定的 Groovy 脚本知识,灵活性更高。
错误检查在执行前会进行更多的验证和错误检查。错误可能需要在执行过程中才发现。

声明式 Pipeline 示例

groovy
pipeline {
    agent any // 指定执行节点,any 表示任意可用节点
    stages { // 阶段集合
        stage('Build') { // 定义一个名为 Build 的阶段
            steps { // 步骤集合
                // 在这里定义具体的步骤,例如:
                sh 'mvn clean package' // 执行 Shell 命令
            }
        }
        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }
    }
    post {
        always {
            echo 'Pipeline 执行完成(无论成功与否)'
        }
        success {
            echo 'Pipeline 执行成功!'
        }
        failure {
            echo 'Pipeline 执行失败!'
        }
    }
}

脚本式 Pipeline 示例

groovy
node {
    stage('Build') {
        echo 'Building...'
    }
}

Pipeline 核心组成

  1. pipeline声明其内容为一个声明式的 Pipeline 脚本,是声明式 Pipeline 的最外层块。
  2. agent定义整个 Pipeline 或特定阶段在哪个 Jenkins 节点(Agent)上执行。常见参数:
    • any: 在任何可用的 Agent 上执行。
    • none: 不指定全局 Agent,需要在每个 stage中单独定义。
    • label: 在指定标签的 Agent 上执行,例如 agent { label 'java' }
    • docker: 在 Docker 容器中执行,例如 agent { docker 'maven:3.8.1-openjdk-17' }
  3. stages包含一个或多个 stage的容器,描述了整个流水线的多个阶段(如构建、测试、部署)。
  4. stage代表 Pipeline 中的一个阶段,每个 stage都需要有一个唯一的名称来描述其任务(例如 'Build'、'Test'、'Deploy')。一个 stage内部可以包含 stepsstages(嵌套)或 parallel
  5. steps位于 stage指令块内部,包含一个或多个具体的步骤(step)。这是真正执行操作的地方,例如执行 Shell 命令、编译代码等。
  6. post定义在 Pipeline 或阶段完成后的一些附加操作,通常根据构建结果状态来执行。常用条件块:
    • always: 无论结果如何都执行。
    • success: 仅当当前状态为成功时执行。
    • failure: 仅当当前状态为失败时执行。
    • unstable: 当当前状态为不稳定时执行。
    • changed: 如果当前构建的状态与上次构建的状态不同则执行。
    • fixed: 当上次构建的状态为失败或不稳定,且当前构建的状态为成功时执行。
    • regression: 当上次构建的状态为成功,且当前构建的状态为失败、不稳定或中止时执行。
    • aborted: 当当前构建的状态为中止(一般为人为中止)时执行。
    • cleanup: 清理条件块,在其他所有条件块执行完成后执行。

Jenkins指令

Jenkins Pipeline 提供了丰富的指令来帮助你定义和管理持续集成/持续部署 (CI/CD) 流程。下面是一些核心指令的完整语法和案例,均基于 声明式 Pipeline

environment

用于设置环境变量,可定义在 pipeline 顶层或特定 stage 中。

groovy
pipeline {
    agent any
    // 全局作用域变量 (整条流水线可用)
    environment {
        PROJECT_NAME = "my-app" // 普通变量
        // 安全地引用Jenkins中预定义的凭据(例如Secret Text类型)
        API_TOKEN = credentials('my-api-token-credential-id')
        // 对于Username and password类型凭据,会自动生成两个附加变量:MYVARNAME_USR 和 MYVARNAME_PSW
        DB_CREDS = credentials('db-user-pass-id') // 会生成 DB_CREDS_USR 和 DB_CREDS_PSW
    }
    stages {
        stage('Example') {
            // 阶段作用域变量 (仅本阶段可用)
            environment {
                DEPLOY_ENV = 'staging'
                // 可以引用全局作用域变量
                URL = "https://${PROJECT_NAME}.example.com"
            }
            steps {
                // 🔧 步骤作用域变量 (仅 withEnv 包裹的步骤内可用)
                withEnv(['TEMPORARY_VAR=temp_value']) {
                    sh '''
                        echo "Project: $PROJECT_NAME"
                        echo "Deploy Env: $DEPLOY_ENV"
                        echo "API Token is set (securely): $API_TOKEN"
                        echo "DB User: $DB_CREDS_USR"
                        echo "Temporary Var: $TEMPORARY_VAR"
                    '''
                }
                // 步骤外无法访问 TEMPORARY_VAR
            }
        }
    }
}

options

用于配置 Pipeline 本身的选项。

groovy
pipeline {
    agent any
    options {
        timeout(time: 1, unit: 'HOURS') // 设置Pipeline运行的超时时间
        retry(3) // 失败后,重试整个Pipeline的次数
        timestamps() // 在控制台打印所有操作的时间戳
        buildDiscarder(logRotator(numToKeepStr: '10')) // 仅保留最近10次构建的历史
        disableConcurrentBuilds() // 禁止并发执行Pipeline
        skipDefaultCheckout() // 默认跳过来自源代码控制的代码检出
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

parameters

定义参数化构建的参数,允许用户在触发 Pipeline 时输入。

groovy
pipeline {
    agent any
    parameters {
        string(name: 'DEPLOY_TARGET', defaultValue: 'staging', description: 'Target environment to deploy to')
        choice(name: 'LOG_LEVEL', choices: ['INFO', 'DEBUG', 'WARN'], description: 'Logging level')
        booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'Whether to run the test suite')
        text(name: 'CUSTOM_CONFIG', defaultValue: '', description: 'Multi-line custom configuration')
        // 文件参数 (需要安装相应插件)
        // base64File name: 'CONFIG_FILE', description: 'A base64 encoded config file'
    }
    stages {
        stage('Deploy') {
            steps {
                script {
                    echo "Deploying to: ${params.DEPLOY_TARGET}"
                    echo "Log level: ${params.LOG_LEVEL}"
                    echo "Run tests: ${params.RUN_TESTS}"
                    echo "Custom config: ${params.CUSTOM_CONFIG}"
                    // 使用文件参数内容
                    // sh 'echo $CONFIG_FILE | base64 --decode > config.yaml'
                }
            }
        }
    }
}

triggers

定义 Pipeline 的自动化触发方式。

groovy
pipeline {
    agent any
    triggers {
        // 定时构建,使用cron语法
        cron('H */4 * * 1-5') // 每周一到周五,每4小时构建一次(H代表哈希,让负载均匀)
        // 轮询SCM(不推荐,建议使用Webhooks)
        // pollSCM('H */2 * * *') // 每2分钟检查一次SCM变更
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

tools

自动安装并配置所需的工具,并将其加入 PATH 环境变量。这要求工具已在 Jenkins 的 Global Tool Configuration 中预配置。

groovy
pipeline {
    agent any
    tools {
        maven 'Maven-3.8.6' // 使用Jenkins中预配置的名为'Maven-3.8.6'的Maven版本
        jdk 'JDK-17' // 使用Jenkins中预配置的名为'JDK-17'的JDK版本
        nodejs 'NodeJS-16' // 使用Jenkins中预配置的名为'NodeJS-16'的Node.js版本
    }
    environment {
        // 通常tools会自动修改PATH,但有时也可显式引用
        MAVEN_HOME = tool 'Maven-3.8.6'
        PATH = "${tool 'Maven-3.8.6'}/bin:${env.PATH}"
    }
    stages {
        stage('Build') {
            steps {
                sh 'mvn --version'
                sh 'java -version'
                sh 'node --version'
            }
        }
    }
}

input

暂停 Pipeline,等待用户输入或确认。

groovy
pipeline {
    agent any
    stages {
        stage('Deploy to Staging') {
            steps {
                echo 'Deploying to staging environment...'
            }
        }
        stage('Approve Production Deploy') {
            steps {
                // 暂停Pipeline,等待用户输入
                input(
                    message: 'Deploy to production?',
                    ok: 'Confirm',
                    submitter: 'admin,deploy-manager', // 可选:指定批准人
                    parameters: [ // 可选:在批准时可以同时提供一些参数
                        string(name: 'RELEASE_NOTES', defaultValue: '', description: 'Release notes')
                    ]
                )
                script {
                    echo "User provided release notes: ${params.RELEASE_NOTES}"
                }
            }
        }
        stage('Deploy to Production') {
            steps {
                echo 'Deploying to production...'
            }
        }
    }
}

when

根据条件决定是否执行某个 stage。

groovy
pipeline {
    agent any
    parameters {
        choice(name: 'DEPLOY_ENV', choices: ['dev', 'staging', 'prod'], description: 'Deployment Environment')
    }
    environment {
        SKIP_DEPLOY = 'false'
    }
    stages {
        stage('Build') {
            steps {
                echo 'Building the application...'
            }
        }
        stage('Deploy to Dev') {
            when {
                // 当部署环境参数为'dev'时执行
                expression { params.DEPLOY_ENV == 'dev' }
            }
            steps {
                echo 'Deploying to development environment...'
            }
        }
        stage('Deploy to Staging') {
            when {
                allOf {
                    // 当部署环境参数为'staging' AND 环境变量SKIP_DEPLOY不为'true'时执行
                    expression { params.DEPLOY_ENV == 'staging' }
                    expression { env.SKIP_DEPLOY != 'true' }
                }
            }
            steps {
                echo 'Deploying to staging environment...'
            }
        }
        stage('Deploy to Production') {
            when {
                // 更复杂的条件:分支为main或master,且环境是prod,且未跳过部署
                allOf {
                    branch 'main|master' // 使用正则表达式
                    expression { params.DEPLOY_ENV == 'prod' }
                    expression { env.SKIP_DEPLOY != 'true' }
                }
                // anyOf, not 等也可用于构建复杂条件
            }
            steps {
                echo 'Deploying to production environment...'
            }
        }
        stage('Run Tests') {
            when {
                // 只有当之前的阶段都没有失败时才会执行
                expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
            }
            steps {
                echo 'Running tests...'
            }
        }
    }
}

parallel

并行执行多个 stage 或 step,以提升构建效率。

groovy
pipeline {
    agent none // 顶层agent设为none,在并行stage中单独指定
    stages {
        stage('Build and Test') {
            steps {
                echo 'This stage runs before the parallel execution.'
            }
        }
        stage('Run Parallel Tests') {
            // 可以设置failFast true,表示任何一个并行任务失败就立即终止所有任务
            failFast true
            parallel {
                stage('Unit Tests - Linux') {
                    agent {
                        label 'linux-agent'
                    }
                    steps {
                        echo 'Running unit tests on Linux...'
                        sh './run-unit-tests.sh'
                    }
                }
                stage('Unit Tests - Windows') {
                    agent {
                        label 'windows-agent'
                    }
                    steps {
                        echo 'Running unit tests on Windows...'
                        bat '.\\run-unit-tests.bat'
                    }
                }
                stage('Integration Tests') {
                    agent any
                    stages {
                        // 并行阶段内部还可以有串行的子阶段
                        stage('Init DB') {
                            steps {
                                echo 'Initializing test database...'
                            }
                        }
                        stage('Run IT') {
                            steps {
                                echo 'Running integration tests...'
                            }
                        }
                    }
                }
            }
        }
        stage('Deploy') {
            steps {
                echo 'This stage runs after the parallel execution.'
            }
        }
    }
}

实战案例:项目自动更新

假设有一个基于 Docker 的项目更新流程,其 Jenkins Pipeline 可能包含以下阶段:

groovy
pipeline {
    agent { label 'your-agent-label' } // 在指定标签的 agent 上运行

    environment {
        // 定义环境变量
        DEPLOY_PATH = '/path/to/your/app'
        BACKUP_SUFFIX = "-backup-${currentBuild.number}"
    }

    stages {
        stage('拉取代码') {
            steps {
                git branch: 'main', url: 'https://your-git-repo.com/your-project.git'
            }
        }
        stage('备份旧文件') {
            steps {
                sh """
                    cp ${DEPLOY_PATH}/your-app.jar ${DEPLOY_PATH}/your-app.jar${BACKUP_SUFFIX}
                    echo "旧文件已备份。"
                """
            }
        }
        stage('构建应用') {
            steps {
                sh 'mvn clean package -DskipTests'
            }
        }
        stage('更新文件') {
            steps {
                sh """
                    cp target/your-app.jar ${DEPLOY_PATH}/
                    chmod 755 ${DEPLOY_PATH}/your-app.jar
                """
            }
        }
        stage('重启服务') {
            steps {
                sh 'docker restart your-app-container'
            }
        }
    }
    post {
        success {
            emailext to: 'team@example.com',
                    subject: "SUCCESS: Pipeline ${currentBuild.fullDisplayName}",
                    body: "项目已成功更新并部署。\n详情: ${env.BUILD_URL}"
        }
        failure {
            emailext to: 'team@example.com',
                    subject: "FAILURE: Pipeline ${currentBuild.fullDisplayName}",
                    body: "项目更新失败,请检查日志。\n详情: ${env.BUILD_URL}"
        }
    }
}