4、Jenkins Pipeline语法:声明式Pipeline vs 脚本式Pipeline、Pipeline基本结构、Stage与Step设计、Post条件处理

好,咱们今天聊聊 Jenkins Pipeline 的语法。说实话,Pipeline 是 Jenkins 的核心能力,没有它,Jenkins 就是个普通的手动构建工具。我刚开始接触时也被两种语法搞晕过——声明式和脚本式,到底选哪个?别急,咱们一个一个拆开看。

4.1 声明式 Pipeline vs 脚本式 Pipeline

这两种语法,说白了就是「写配置」和「写代码」的区别。

声明式 Pipeline:结构固定,像填表格。你只需要告诉 Jenkins「我要做什么」,它帮你管流程。适合大多数场景,尤其是团队里不全是开发人员的情况。

脚本式 Pipeline:更灵活,像写程序。你可以用 Groovy 语言写逻辑判断、循环、异常处理。适合复杂场景,比如动态生成 Stage。

我个人习惯:能用声明式就用声明式。为什么?因为可读性强,团队成员一看就懂。我在项目中遇到过,脚本式 Pipeline 写得太灵活,后来维护的人根本看不懂,最后全重写了。

核心区别一句话:声明式是「框架内填空」,脚本式是「自由发挥」。新手建议从声明式入手。

来看个对比示例:

// 声明式 Pipeline
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                echo 'Building...'
            }
        }
    }
}

// 脚本式 Pipeline
node {
    stage('Build') {
        echo 'Building...'
    }
}

看出来了吗?声明式多了 pipelinestages 这些关键字,结构更清晰。脚本式就一个 node 块,剩下的全靠 Groovy 代码。

4.2 Pipeline 基本结构

声明式 Pipeline 的结构其实很固定,就像盖房子有地基、柱子、屋顶一样。我把它拆成几个核心部分:

组成部分 作用 是否必须
pipeline 最外层容器,所有内容都包在里面
agent 指定在哪个节点上运行(比如用哪个 Jenkins Slave)
stages 包含所有 Stage 的集合
stage 单个阶段,比如「编译」「测试」「部署」
steps 每个 Stage 里具体执行的命令
post Pipeline 或 Stage 执行后的处理逻辑
environment 定义环境变量
options 配置 Pipeline 的选项(如超时、重试)

嗯,这里要注意:agent 可以放在全局,也可以放在某个 Stage 里。比如编译用 Linux 节点,测试用 Windows 节点,完全没问题。

pipeline {
    agent none  // 全局不指定,让每个 Stage 自己指定
    stages {
        stage('Build') {
            agent { label 'linux' }
            steps {
                sh 'make'
            }
        }
        stage('Test') {
            agent { label 'windows' }
            steps {
                bat 'test.bat'
            }
        }
    }
}

4.3 Stage 与 Step 设计

Stage 和 Step 是 Pipeline 的灵魂。Stage 是「阶段」,Step 是「步骤」。你想想看,一个完整的 CI/CD 流程,其实就是把多个 Stage 串起来。

Stage 设计原则

  • 每个 Stage 只做一件事。别把编译和测试写在一个 Stage 里,否则失败了都不知道是哪步出的问题。
  • Stage 名称要见名知意。用 BuildUnit TestIntegration TestDeploy to Staging 这种,别用 Step1Step2
  • 可以并行执行。比如单元测试和代码扫描可以同时跑,用 parallel 关键字。

Step 设计技巧

  • 每个 Step 尽量短小精悍。一个 Step 里写几十行 shell 脚本?那还不如直接写个脚本文件。
  • 善用 Jenkins 插件提供的 Step。比如 shbatgitdocker,别自己造轮子。
  • 我建议把常用的 Step 封装成共享库,团队里直接调用,省得重复写。

小技巧:Stage 里可以用 when 条件判断,比如只在 master 分支才执行部署。这样能省不少时间。

stage('Deploy to Production') {
    when {
        branch 'master'
    }
    steps {
        echo 'Deploying to production...'
    }
}

4.4 Post 条件处理

Post 是 Pipeline 里的「善后处理」。不管构建成功还是失败,你总得做点什么吧?比如发通知、清理环境、保存日志。

Post 支持的条件有这些:

条件 触发时机
always 无论结果如何,都会执行
success 只有成功时才执行
failure 只有失败时才执行
unstable 构建结果不稳定时(比如测试有失败但没崩溃)
changed 当前构建结果与上一次不同时
aborted 用户手动取消时

我曾经踩过一个坑:在 Post 里写清理逻辑,结果忘了加 always,导致构建失败时临时文件没删掉,下次构建直接报磁盘空间不足。嗯,从那以后我养成了习惯——清理操作一定要用 always

post {
    always {
        echo '清理临时文件...'
        cleanWs()  // 清理工作空间
    }
    success {
        echo '构建成功,发送通知'
        // 调用通知插件
    }
    failure {
        echo '构建失败,记录日志'
        // 保存构建日志到外部存储
    }
    changed {
        echo '构建状态发生变化'
        // 比如从成功变成失败,或者从失败变成成功
    }
}

注意:Post 可以放在 Pipeline 级别(全局),也可以放在 Stage 级别(只针对某个阶段)。全局 Post 在全部 Stage 执行完后触发,Stage 级别的 Post 在该 Stage 结束后立即触发。

举个例子,你可以在 Deploy 这个 Stage 的 Post 里单独处理部署失败的情况,而不影响其他 Stage 的 Post 逻辑。

stage('Deploy') {
    steps {
        // 部署操作
    }
    post {
        failure {
            echo '部署失败,回滚版本'
            // 执行回滚脚本
        }
    }
}

好了,Pipeline 语法的核心内容就这些。声明式还是脚本式?我建议你先用声明式,等遇到复杂需求再切换到脚本式。Stage 和 Step 的设计,记住「单一职责」原则。Post 条件处理,别忘了 always 清理环境。

下一章咱们聊聊 Jenkins 的凭证管理和安全配置,这可是生产环境的重中之重。