Jenkins上手记录

按当前行业情况来讲严重过时的内容

工作中接手了个项目是使用jenkins构建的,所以借着机会完整看一下Jenkins官方文档并作记录。

说起来,其实早在7~8年前工作中就在用jenkins了,但那时是别人搭建并配置好,甚至都没有自动触发逻辑——仍然需要手动build,实在说不上是多么“utilised”了这个软件。

基本概念

  • Jenkins由Java编写
  • 类似Gradle, Jenkins自身实现了通用部分的抽象,包括基本的调度和执行等,然后通过plugin来扩展了具体功能,只是Jenkins的plugin能做得更多更广
  • 通常我们用一个独立的文件来定义构建流程。在Jenkins中这个文件叫作Jenkinsfile。它有两种格式——声明式(declarative)和脚本式(scripted)。实际上所谓声明式就是通过groovy实现的DSL,底层是已经结构化得脚本命令。相比脚本他结构更清晰,但同时失去了灵活性。对一般项目而言声明式已经足够强大。另外,其实我们可以在任意需要的位置编写groovy脚本,从而在整体declarative的同时部分scripted
  • Jenkinsfile可以放在项目根目录下,或者也可以在独立的仓库中维护
  • 我们见到的Jenkins界面运行的服务器通常叫做Jenkins controller(以前叫master)。但执行构建一般不是在同一个服务器上,这些构建用的机器通常叫做agent(以前叫node)

Jenkins Controller

  • Jenkins Controller(原称 Master)是 Jenkins 的核心服务,负责管理和调度构建任务、维护 Job 与 Pipeline 配置、处理来自 SCM 的 Webhook 事件,并通过 Web UI 和 API 对外提供构建管理与状态信息
  • Controller 本身通常不执行实际的构建任务,而是将构建分发到一个或多个 Jenkins Agent 上执行

项目类型

初始化 Jenkins 以后,我们就可以在 Jenkins 网页上点击 Add Item 来创建项目。这里的 Item 会有多种类型可供选择,不同类型基本也对应了 Jenkins 不同历史阶段的使用方式:

Folder

用于组织项目结构,本身不执行构建

Freestyle Project

我觉得这个不如叫做oldschool project。早年我接触的应该也是这种类型。它是最早期、也最“GUI 驱动”的 Jenkins 项目类型。相关构建参数都通过jenkins界面去配置和定义。

在早期阶段,Freestyle Project 通过 GUI 对构建过程中的单个执行步骤做了较好的抽象,例如拉取 Git 代码、执行 Shell 命令等。

Multi-configuration Project

也叫Matrix Project。配置和使用方式上类似Freestyle,同时支持在多个参数组合(矩阵)配置下执行同一套构建逻辑。有点类似Android Gradle Plugin的build variant。

Pipeline Project

相比Freestyle,这是对现代CI有更深入理解后的阶段性产物。这里开始构建流程不再是通过网页,而是在Jenksfile中进行定义。

但这一阶段的Jenkins处理多分支复杂项目时还是会力不从心,由此引出了我们常用的multibranch pipeline。

Multibranch Pipeline

它允许每个分支定义自己的Jenkinsfile。在配置好git仓库地址后,Jenkins就会扫描所有分支,对每个包含 Jenkinsfile 的分支创建一个子 Pipeline。并且支持PR/MR自动构建。

项目创建后,每个项目名称就是一个job name。

Organization Folder

和github/gitlab等绑定,自动为org下的所有项目生成multibranch pipeline。

Plugin

在 Jenkinsfile 中,开发者主要关注项目的构建流程本身,而构建所依赖的插件能力、全局配置或敏感信息(例如访问令牌、凭据等),通常由 Jenkins 管理员提前配置好。

这些配置既可以通过管理员账号登录 Jenkins,在管理页面中完成;也可以通过更现代的方式——“配置即代码(Configuration as Code)”进行管理,即使用独立的配置文件。

此外,像 Docker 或 k8s 这类现代 CI 场景,也需要通过安装相应的插件才能在 Jenkins 中得到支持。

JenkinsFile

Jenkinsfile结构大概是这样:

pipeline {
  agent any

  stages {
    stage('Prepare') {
      steps {
        checkout scm
      }
    }

    stage('Build') {
      steps {
        sh '''
          chmod +x ./gradlew
          ./gradlew assembleRelease
        '''
      }
    }
  }
}

他本质上是一个基于Groovy的DSL实现。

参数

Jenkins Pipeline 在构建时可以接收参数,可以通过 parameters 块在 Jenkinsfile 中进行定义。

pipeline {
    agent any

    parameters {
        string(
            name: 'BRANCH_NAME',
            defaultValue: 'main',
            description: 'target branch'
        )

        booleanParam(
            name: 'RUN_TESTS',
            defaultValue: true,
            description: 'whether to perform UT'
        )
    }
    
    ...
}

当 Pipeline 定义了参数后,构建会以 参数化构建(Parameterized Build)的形式进行:

  • 通过 Web UI、API 或 Webhook 触发构建时,可以指定参数
  • 若未显式传入参数,则使用 defaultValue

在 Pipeline 脚本中,可以通过 params.<PARAM_NAME> 的方式访问参数值。此外,Jenkins 会将这些参数同时注入为构建时的环境变量,在执行的步骤(如 shbat)中可以通过环境变量的方式访问。

结果处理

post命令类似我们常用的回调函数。可以在在 Pipeline 或 Stage 块内定义,用于处理全局或局部阶段的构建结果。比如归档产物、发布测试报告、资源清理以及通知等操作。

post 支持按构建结果进行条件划分:

  • always:无论结果如何都会执行
  • success:构建成功时执行
  • failure:构建失败时执行
  • unstable:构建结果为 UNSTABLE 时执行
  • aborted:构建被中断时执行
  • changed:本次构建结果与上一次不同
pipeline {
  agent any

  stages {
	...

    stage('Build') {
		steps {
            sh '''
              chmod +x ./gradlew
              ./gradlew assembleRelease
            '''
		}
        post {
            always {
            	echo 'build finished'
            }
            failure {
                echo 'build failed'
            }
        }
    }
  }
}

并行执行(parallel)

pipeline在设计上是串行执行,但类似测试,lint等构建不互相影响的可以并行执行缩短时间。

并行分支一般有各自的 workspace/节点环境;要共享文件用 stash/unstash(或归档再取)。

组合构建

像前面的multi configuration,要同一套构建步骤跑多套组合时可以用matrix。

stage('Matrix Test') {
    matrix {
        axes {
            axis {
                name 'JDK'
                values '17', '21'
            }
            axis {
                name 'OS'
                values 'linux', 'windows'
            }
        }
        stages {
            stage('Test') {
                steps {
                    echo "JDK=${JDK}, OS=${OS}"
                    sh 'make test'
                }
            }
        }
    }
}

条件

按条件决定某个 stage 是否执行(分支、PR、参数、变更路径等),尤其适合 Multibranch。例如在针对release和其他分支时,可以执行不同的构建命令。

stage('Deploy') {
    when { branch 'main' }
    steps { sh './deploy.sh' }
}

构建产物归档(archiveArtifacts)

将构建产物持久化保存,绑定到一次构建记录中,供后续下载或审计

post {
    always {
        archiveArtifacts artifacts: 'dist/**', allowEmptyArchive: true
    }
}
  • 归档文件会出现在 Jenkins UI 的 Artifacts中

  • 文件生命周期与构建记录一致

  • 通常放在 Pipeline 级或 Stage 级 post

  • 不用于在后续 stage 中继续使用

文件暂存与传递(stash / unstash)

在 Pipeline 内部,在不同 stage、不同 agent、或并行分支之间传递文件

stage('Build') {
    steps {
        sh 'make build'
        stash name: 'build-output', includes: 'dist/**'
    }
}

stage('Test') {
    steps {
        unstash 'build-output'
        sh 'make test'
    }
}
  • 只在 同一次 Pipeline 执行内有效
  • 不会出现在 Jenkins UI 的 Artifacts 中
  • Pipeline 结束后即被清理
  • 本质是 Jenkins 内部的临时文件存储

Workspace

Workspace 是 Jenkins 在某个 Agent 节点上,为某个 Job 的某一次构建分配的一块工作目录。

Workspace 与 Agent 绑定:如果多个 stage 在同一个 agent 上顺序执行,它们通常会共享同一个 workspace。

parallelmatrix 场景下,由于存在并发执行,Jenkins 会为每个并发执行单元提供隔离的 workspace(或等效的隔离视图),以避免并发写入造成冲突。

需要注意的是,如果显式指定共享路径(例如使用 customWorkspacews(),或在并行分支中写入同一个外部目录),仍然可能发生文件冲突。

Agent

Agent(也叫 Node) 是 Jenkins 用来实际执行构建步骤的执行环境,可以是:

  • 一台物理机
  • 一台虚拟机
  • 一个 Docker 容器
  • 一个 Kubernetes Pod

在 Jenkinsfile 里:

agent any
agent { label 'linux' }

表达的都是:

这段 Pipeline / Stage 在什么执行环境上跑

Executor

Executor 是 Agent 上的一个 “并发执行槽位”。

  • 一个 executor = 同一时间 只能执行一个任务
  • executor 的数量 = 这个 agent 最多能同时跑多少个任务

一个 agent 拥有多少个 executors 是由 Jenkins 管理员在节点配置中决定的,通常会根据该节点的机器配置(CPU、内存、IO 等)以及预期的任务负载来设置。

Webhooks / API

配置好 Jenkins 项目后,进入具体的 Job 页面,在浏览器地址后追加 /api,可以查看该 Job 所支持的 REST API 描述信息。

例如,通过 REST API 可以主动触发构建:

无参数构建

POST /job/<job-name>/build

参数化构建

/job/<job-name>/buildWithParameters?BRANCH=main&RUN_TESTS=true

需要注意的是,REST API 并不属于 Webhook, 它用于外部系统主动调用Jenkins, 而 Webhook 用于 SCM(如 GitLab、GitHub)在事件发生时 主动通知 Jenkins。

在实际开发中,最常直接使用的是上述 /build/buildWithParameters 这类构建 API。而 Webhook 通常需要在 GitLab / GitHub 的仓库或组织设置页面中配置,用于将 push、Merge Request 等事件分发给 Jenkins,从而触发分支扫描或构建。

由于 Webhook 配置发生在 SCM 与 Jenkins 的集成层,通常需要在 Jenkins 项目或 Organization Folder 中提前完成 SCM Source 及发现策略等相关配置,在项目数量较多的情况下,这些集成配置往往由平台或管理员统一维护;相较于 GitLab 自身的 CI(配置即代码、随仓库演进),Jenkins 在初始接入和集中管理上会带来更高的配置成本,也使得部分与构建触发相关的逻辑需要在平台层进行约定和维护。