Jenkins入门教程(六):声明式Pipeline详解

声明式Pipeline是Jenkins推荐的Pipeline语法,本文将详细介绍其语法结构、常用指令和完整的实战示例。

基本结构

声明式Pipeline必须包含在pipeline块中:

pipeline {
    agent any           // 必需:指定执行环境
    
    stages {            // 必需:包含所有阶段
        stage('阶段名') {
            steps {     // 必需:具体执行步骤
                // 执行命令
            }
        }
    }
}

agent指令详解

agent定义Pipeline在哪里执行:

agent any

pipeline {
    agent any  // 在任意可用节点上执行
    
    stages {
        stage('Build') {
            steps {
                sh 'echo "Running on node: ${NODE_NAME}"'
            }
        }
    }
}

// 执行结果
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/my-job
[Pipeline] sh
+ echo 'Running on node: Jenkins'
Running on node: Jenkins

agent none

pipeline {
    agent none  // 不指定全局agent,每个stage单独指定
    
    stages {
        stage('Build on Linux') {
            agent { label 'linux' }
            steps {
                sh 'make build'
            }
        }
        
        stage('Build on Windows') {
            agent { label 'windows' }
            steps {
                bat 'msbuild solution.sln'
            }
        }
    }
}

agent label

pipeline {
    agent {
        label 'linux && docker'  // 需要同时有linux和docker标签的节点
    }
    
    stages {
        stage('Check') {
            steps {
                sh '''
                    echo "Node: ${NODE_NAME}"
                    echo "Labels: ${NODE_LABELS}"
                    docker version
                '''
            }
        }
    }
}

// 执行结果
[Pipeline] node
Running on docker-agent-01 in /home/jenkins/workspace/my-job
[Pipeline] sh
+ echo 'Node: docker-agent-01'
Node: docker-agent-01
+ echo 'Labels: linux docker java'
Labels: linux docker java
+ docker version
Client: Docker Engine - Community
 Version: 24.0.7

agent docker

pipeline {
    agent {
        docker {
            image 'maven:3.8-openjdk-17'
            args '-v $HOME/.m2:/root/.m2 -e MAVEN_OPTS="-Xmx1024m"'
            label 'docker'  // 可选:在特定节点上运行容器
        }
    }
    
    stages {
        stage('Build') {
            steps {
                sh '''
                    echo "=== Container Info ==="
                    cat /etc/os-release | head -2
                    
                    echo "\n=== Java Version ==="
                    java -version
                    
                    echo "\n=== Maven Version ==="
                    mvn -version
                '''
            }
        }
    }
}

// 执行结果
[Pipeline] withDockerContainer
Jenkins does not seem to be running inside a container
$ docker run -d -v /home/jenkins/.m2:/root/.m2 ...
[Pipeline] sh
+ echo '=== Container Info ==='
=== Container Info ===
+ cat /etc/os-release
NAME="Debian GNU/Linux"
VERSION="11 (bullseye)"

=== Java Version ===
openjdk version "17.0.9" 2023-10-17

=== Maven Version ===
Apache Maven 3.8.7

agent dockerfile

pipeline {
    agent {
        dockerfile {
            filename 'Dockerfile.build'  // 自定义Dockerfile
            dir 'docker'                  // Dockerfile所在目录
            additionalBuildArgs '--build-arg VERSION=1.0'
        }
    }
    
    stages {
        stage('Build') {
            steps {
                sh 'make build'
            }
        }
    }
}

environment指令

pipeline {
    agent any
    
    // 全局环境变量
    environment {
        APP_NAME = 'my-application'
        VERSION = '1.0.0'
        DEPLOY_ENV = 'staging'
        // 使用凭据
        DOCKER_CREDS = credentials('docker-hub-creds')
        // 动态值
        BUILD_TIME = sh(script: 'date +%Y%m%d-%H%M%S', returnStdout: true).trim()
    }
    
    stages {
        stage('Print Global Env') {
            steps {
                sh '''
                    echo "App: ${APP_NAME}"
                    echo "Version: ${VERSION}"
                    echo "Build Time: ${BUILD_TIME}"
                '''
            }
        }
        
        stage('Stage Level Env') {
            // stage级别环境变量
            environment {
                STAGE_VAR = 'only-in-this-stage'
                DATABASE_URL = 'jdbc:mysql://localhost:3306/test'
            }
            steps {
                sh '''
                    echo "Stage Var: ${STAGE_VAR}"
                    echo "DB URL: ${DATABASE_URL}"
                '''
            }
        }
    }
}

// 执行结果
[Pipeline] sh
+ echo 'App: my-application'
App: my-application
+ echo 'Version: 1.0.0'
Version: 1.0.0
+ echo 'Build Time: 20260208-120000'
Build Time: 20260208-120000

+ echo 'Stage Var: only-in-this-stage'
Stage Var: only-in-this-stage
+ echo 'DB URL: jdbc:mysql://localhost:3306/test'
DB URL: jdbc:mysql://localhost:3306/test

options指令

pipeline {
    agent any
    
    options {
        // 超时设置
        timeout(time: 30, unit: 'MINUTES')
        
        // 构建保留策略
        buildDiscarder(logRotator(
            numToKeepStr: '10',           // 保留最近10个构建
            artifactNumToKeepStr: '5',    // 保留最近5个产物
            daysToKeepStr: '30'           // 保留30天
        ))
        
        // 禁止并发构建
        disableConcurrentBuilds()
        
        // 添加时间戳到日志
        timestamps()
        
        // 跳过默认checkout
        skipDefaultCheckout()
        
        // 失败时重试
        retry(3)
        
        // ANSI颜色输出
        ansiColor('xterm')
    }
    
    stages {
        stage('Build') {
            options {
                timeout(time: 10, unit: 'MINUTES')  // stage级别超时
            }
            steps {
                sh 'make build'
            }
        }
    }
}

// 日志输出带时间戳
[2026-02-08T12:00:00.000Z] [Pipeline] sh
[2026-02-08T12:00:00.123Z] + make build
[2026-02-08T12:00:05.456Z] Build complete

parameters指令

pipeline {
    agent any
    
    parameters {
        // 字符串参数
        string(
            name: 'BRANCH',
            defaultValue: 'main',
            description: '要构建的分支名'
        )
        
        // 选择参数
        choice(
            name: 'ENVIRONMENT',
            choices: ['dev', 'staging', 'prod'],
            description: '部署环境'
        )
        
        // 布尔参数
        booleanParam(
            name: 'SKIP_TESTS',
            defaultValue: false,
            description: '是否跳过测试'
        )
        
        // 密码参数
        password(
            name: 'DEPLOY_TOKEN',
            defaultValue: '',
            description: '部署令牌'
        )
        
        // 多行文本
        text(
            name: 'RELEASE_NOTES',
            defaultValue: '',
            description: '发布说明'
        )
    }
    
    stages {
        stage('Use Parameters') {
            steps {
                sh '''
                    echo "Branch: ${BRANCH}"
                    echo "Environment: ${ENVIRONMENT}"
                    echo "Skip Tests: ${SKIP_TESTS}"
                '''
                
                script {
                    if (params.SKIP_TESTS) {
                        echo 'Tests will be skipped'
                    } else {
                        echo 'Running tests...'
                    }
                }
            }
        }
    }
}

// 首次运行后,Jenkins会显示参数输入界面
// 用户可以填写参数后点击"开始构建"

triggers指令

pipeline {
    agent any
    
    triggers {
        // 定时构建 (cron语法)
        cron('H 2 * * *')  // 每天凌晨2点
        
        // 轮询SCM
        pollSCM('H/5 * * * *')  // 每5分钟检查一次
        
        // 上游项目触发
        upstream(
            upstreamProjects: 'job1,job2',
            threshold: hudson.model.Result.SUCCESS
        )
    }
    
    stages {
        stage('Build') {
            steps {
                sh 'make build'
            }
        }
    }
}

// Cron表达式说明
// 分 时 日 月 周
// H 2 * * *      每天凌晨2点(H表示随机分钟,避免同时触发)
// H/15 * * * *   每15分钟
// H 9-17 * * 1-5 周一到周五的9-17点每小时
// H H 1,15 * *   每月1日和15日

tools指令

pipeline {
    agent any
    
    tools {
        jdk 'JDK17'       // 全局工具配置中的JDK名称
        maven 'Maven3'    // 全局工具配置中的Maven名称
        nodejs 'Node18'   // 全局工具配置中的NodeJS名称
    }
    
    stages {
        stage('Check Tools') {
            steps {
                sh '''
                    echo "JAVA_HOME: $JAVA_HOME"
                    java -version
                    
                    echo "\nMAVEN_HOME: $MAVEN_HOME"
                    mvn -version
                    
                    echo "\nNode:"
                    node -v
                    npm -v
                '''
            }
        }
    }
}

// 执行结果
JAVA_HOME: /var/lib/jenkins/tools/hudson.model.JDK/JDK17
openjdk version "17.0.9" 2023-10-17

MAVEN_HOME: /var/lib/jenkins/tools/hudson.tasks.Maven_MavenInstallation/Maven3
Apache Maven 3.8.7

Node:
v18.19.0
10.2.3

input指令

pipeline {
    agent any
    
    stages {
        stage('Build') {
            steps {
                sh 'make build'
            }
        }
        
        stage('Deploy to Production') {
            input {
                message '确认部署到生产环境?'
                ok '确认部署'
                submitter 'admin,deployer'  // 只有这些用户可以确认
                parameters {
                    choice(
                        name: 'TARGET',
                        choices: ['app-server-1', 'app-server-2', 'all'],
                        description: '选择部署目标'
                    )
                }
            }
            steps {
                echo "Deploying to: ${TARGET}"
                sh './deploy.sh ${TARGET}'
            }
        }
    }
}

// Pipeline会暂停,等待用户在Jenkins界面确认

post指令

pipeline {
    agent any
    
    stages {
        stage('Build') {
            steps {
                sh 'make build'
            }
        }
        
        stage('Test') {
            steps {
                sh 'make test'
            }
            // stage级别的post
            post {
                always {
                    junit 'test-results/*.xml'
                }
            }
        }
    }
    
    // Pipeline级别的post
    post {
        always {
            echo 'Pipeline完成,无论成功失败都执行'
            archiveArtifacts artifacts: 'logs/**', allowEmptyArchive: true
        }
        
        success {
            echo 'Pipeline成功!'
            slackSend color: 'good', message: "Build succeeded: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
        }
        
        failure {
            echo 'Pipeline失败!'
            slackSend color: 'danger', message: "Build failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
            mail to: 'team@example.com',
                 subject: "Build Failed: ${env.JOB_NAME}",
                 body: "Check console output at ${env.BUILD_URL}"
        }
        
        unstable {
            echo 'Pipeline不稳定(如测试失败)'
        }
        
        aborted {
            echo 'Pipeline被中止'
        }
        
        changed {
            echo '状态发生变化(如从失败变成功)'
        }
        
        fixed {
            echo '从失败恢复到成功'
        }
        
        regression {
            echo '从成功变成失败'
        }
        
        cleanup {
            echo '最后执行,用于清理'
            cleanWs()  // 清理工作空间
        }
    }
}

完整实战示例

pipeline {
    agent any
    
    options {
        timeout(time: 30, unit: 'MINUTES')
        buildDiscarder(logRotator(numToKeepStr: '10'))
        timestamps()
        disableConcurrentBuilds()
    }
    
    environment {
        APP_NAME = 'my-java-app'
        DOCKER_REGISTRY = 'registry.example.com'
        MAVEN_OPTS = '-Xmx1024m'
    }
    
    parameters {
        choice(name: 'ENV', choices: ['dev', 'staging', 'prod'], description: '部署环境')
        booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: '跳过测试')
    }
    
    tools {
        jdk 'JDK17'
        maven 'Maven3'
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
                sh 'git log -1 --pretty=format:"%h %s"'
            }
        }
        
        stage('Build') {
            steps {
                sh 'mvn clean compile'
            }
        }
        
        stage('Test') {
            when {
                expression { !params.SKIP_TESTS }
            }
            steps {
                sh 'mvn test'
            }
            post {
                always {
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }
        
        stage('Package') {
            steps {
                sh 'mvn package -DskipTests'
                archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
            }
        }
        
        stage('Docker Build') {
            steps {
                script {
                    docker.build("${DOCKER_REGISTRY}/${APP_NAME}:${BUILD_NUMBER}")
                }
            }
        }
        
        stage('Deploy') {
            when {
                expression { params.ENV == 'prod' }
            }
            input {
                message '确认部署到生产环境?'
                ok 'Deploy'
            }
            steps {
                sh "./deploy.sh ${params.ENV}"
            }
        }
    }
    
    post {
        success {
            echo 'Build succeeded!'
        }
        failure {
            echo 'Build failed!'
        }
        cleanup {
            cleanWs()
        }
    }
}

总结

本文详细介绍了声明式Pipeline的各种指令,包括agent、environment、options、parameters、triggers、tools、input和post。掌握这些指令可以构建功能完善的CI/CD流程。

下一篇我们将学习Pipeline中的条件判断和并行执行。

发表回复

后才能评论