第45章|Jenkins Pipeline 1:Declarative vs Scripted 与 Jenkinsfile

上一章把 Jenkins 比作“改装车间”;本章把流水线写成图纸——放进仓库里评审、回滚、复用。 Declarative Pipeline带护栏的高速公路:结构清晰、lint 友好; Scripted Pipeline越野赛道:Groovy 自由度大,适合复杂编排。 你会掌握 Jenkinsfile 的骨架、两种风格的分工,以及何时在 Declarative 里嵌 script {}“开外挂”。

PaC

Jenkinsfile

  • SCM 版本化
  • code review
  • replay / replayed
Declarative

Opinionated

  • stages / steps
  • post / when
  • Blue Ocean
Scripted

Groovy power

  • node / stage
  • CPS · sandbox
  • escape hatch

1. 从“点鼠标”到 Pipeline as Code

Freestyle job 把逻辑藏在服务器配置里,难以 diff;Pipeline 把构建、测试、发布写成 Groovy 脚本,通常命名为仓库根目录的 Jenkinsfile,由 Multibranch PipelinePipeline job 从 SCM 拉取。 这样流水线变更与业务代码走同一套评审流程——CI/CD 本身也进入变更管理

Pipeline as Code: single source in Git Git repo Jenkinsfile declarative or scripted top-level src/ test/ … same commit PR changes pipeline + product together Jenkins controller SCM checkout → CPS transform → run on agent stages as model Blue Ocean / stage view logs + artifacts replay for hotfix (careful) Shared libraries (next chapter) inject reusable Groovy
图 1:Jenkinsfile 与业务代码同仓,流水线变更可评审、可 bisect。

2. Declarative Pipeline:结构即文档

顶层必须是 pipeline {}。常用块:agent(在哪跑)、options(超时、重试、构建丢弃)、 environment(键值与凭据绑定)、stages(阶段树)、post(always / success / failure 等收尾)。 when 控制阶段是否执行;steps 里放 shbatcheckout scm 等。

pipeline {
  agent { label 'linux && docker' }
  options {
    timeout(time: 45, unit: 'MINUTES')
    timestamps()
  }
  environment {
    CI = 'true'
  }
  stages {
    stage('Build') {
      steps {
        sh 'npm ci'
        sh 'npm run build'
      }
    }
    stage('Test') {
      steps {
        sh 'npm test'
      }
    }
  }
  post {
    always {
      junit 'reports/**/*.xml'
    }
    failure {
      echo 'Build failed — notify on-call'
    }
  }
}

3. Scripted Pipeline:自由编排的 Groovy

Scripted 以 node 包裹,内部用 stage 组织;你可以写任意 Groovy 控制流(循环、函数、库调用)。 代价是更难静态分析,新人上手曲线陡,且更容易写出非 CPS 友好的代码导致诡异挂起——需要理解 Pipeline 的 Continuation-Passing Style 执行模型。

node('linux') {
  stage('Checkout') {
    checkout scm
  }
  stage('Build') {
    sh 'mvn -B -DskipTests clean package'
  }
}
Two flavors, one engine (Groovy CPS) Declarative pipeline → agent → stages → steps validators · VS Code extension · lint escape: script { } for advanced Groovy VS Scripted node { stage { … } } full Groovy · loops · libraries watch for CPS pitfalls · use @NonCPS carefully
图 2:Declarative 偏“约束即助力”;Scripted 偏“全能但自负盈亏”。

4. 在 Declarative 中使用 script {}

当 Declarative 的固定块不够用(例如动态决定并行矩阵),可在 steps 里嵌 script { } 写 Groovy。 这是官方支持的逃生舱:保持外层结构清晰,内层处理脏活。

stage('Matrix-ish') {
  steps {
    script {
      def langs = ['en', 'zh']
      langs.each { lang ->
        echo "Testing locale: ${lang}"
      }
    }
  }
}
CPS 提示:Pipeline 在沙箱中运行,许多标准 Groovy/Java API 受限;复杂解析或大数据结构可考虑 @NonCPS 函数(有严格限制,查阅官方文档)。不要把重型计算塞进 Pipeline——交给真正的构建步骤。

5. 并行与 post:把时间掰成并行流

parallel 可在 Scripted 或 Declarative 的 script 中使用(Declarative 也有 parallel stage 模式,依版本与习惯)。 post 块统一处理通知、归档、清理 workspace,避免每个 stage 复制粘贴。

6. 对照表:怎么选?

场景 更倾向
团队刚 Pipeline 化,需要可读模板 Declarative 为主
复杂动态矩阵、重度 Groovy 抽象 Scripted 或 Declarative + shared lib
需要 UI / linter 强约束 Declarative
遗留大量 Scripted,迁移成本高 渐进封装进共享库(下一章)

CPS 与沙箱(挂起时该查什么)

Pipeline 使用 Continuation-Passing Style:许多步骤不是“普通同步调用”,而是可被序列化、恢复的执行块。 若在 Pipeline 里滥用某些 Java API,可能遇到不允许的方法看似死锁的挂起script {} 仍在沙箱规则之下——需要特权时走管理员审批的脚本批准或把逻辑挪到普通构建脚本(shell、maven)里执行。

Sandbox + CPS (conceptual) Pipeline thread sh / checkout / sleep → CPS-safe steps yield / resume between steps sandbox fence Blocked / needs approval arbitrary Java reflection · filesystem tricks prefer external scripts for heavy logic
图 3:把 Pipeline 想成“可暂停的协程”+ 沙箱围栏;越界就换工具链,而不是硬关沙箱。
Execution mental model checkout build unit lint package post { always { … } } archive artifacts · junit · notify · wipe workspace
图 4:主线 stage 与并行分支汇合;post 像终点站里的统一清场与通报。

7. 本章清单

  1. 新建 Multibranch Pipeline,从仓库读取 Jenkinsfile,跑通 Declarative 最小示例。
  2. 对比同一流水线的 Scripted 写法,体会 node / stage 差异。
  3. 为失败场景配置 post 通知与报告归档。
  4. 阅读官方文档中 CPS / sandbox 章节,遇到挂起时知道往哪查。
  5. 预告:把重复 Groovy 抽进 Shared Library(下一章)。
← 上一章:Jenkins 入门 下一章:共享库 →

web · 轻松学习