文章持续更新ing
一、快速开始
pipeline {
agent any
environment {
APP_NAME = 'demo-app'
}
stages {
stage('Build') {
steps {
echo "Building ${APP_NAME}"
sh 'echo build'
}
}
}
post {
success {
echo 'Success'
}
failure {
echo 'Failed'
}
}
}
1. pipeline:整个流水线的最外层定义。
2. agent:指定在哪台 Jenkins 节点执行。指定 Pipeline 或 Stage 在哪个执行环境(节点/容器)上运行。
agent any // 任意可用节点
agent none // 不分配全局节点,每个 stage 单独指定
agent { label 'linux' } // 指定带 linux 标签的节点
# 补充:动态agent
# https://plugins.jenkins.io/kubernetes/#plugin-content-configuration-reference
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: golang
image: your-registry/ci/go:1.22
command: ["cat"]
tty: true
- name: kaniko
image: gcr.io/kaniko-project/executor:debug
command: ["/busybox/cat"]
tty: true
'''
}
}
| 项目 | agent any |
agent kubernetes |
|---|---|---|
| 执行位置 | 任意已有 Jenkins 节点 | 动态创建 Kubernetes Pod |
| 环境依赖 | 节点须预装 Go、Docker、curl 等 | 在 Pod 镜像/YAML 中定义环境 |
| 环境隔离 | 节点可能残留上次构建文件 | 每次构建全新、隔离 |
| 配置复杂度 | 低,适合入门 | 较高,需要 Pod YAML、镜像构建方案 |
| Docker 构建 | 节点有 Docker 即可 | 通常使用 Kaniko、BuildKit 或 DinD,不能默认直接 docker build |
3. stages / stage:将流程拆成清晰步骤,如 Checkout、Build、Test、Deploy。
stages {
stage('Build') {
steps {
sh 'npm run build'
}
}
}
4. steps:某个阶段中实际执行的动作。
steps {
echo 'Start build'
sh 'pwd'
sh 'ls -la'
}
5. sh 与 bat:执行命令行。
sh 'npm ci' // Linux / macOS Agent
bat 'npm ci' // Windows Agent
多行 Shell:
sh '''
npm ci
npm run build
'''
6. environment:定义环境变量,避免把配置散落在命令中。
environment {
REGISTRY = 'harbor.example.com'
IMAGE_NAME = 'team/demo-app'
}
在 Groovy 字符串中使用:
echo "Image: ${REGISTRY}/${IMAGE_NAME}"
在 Shell 命令中使用:
sh 'docker build -t $REGISTRY/$IMAGE_NAME:$BUILD_NUMBER .'
7. when:控制阶段是否执行,常用于区分分支。
stage('Deploy') {
when {
branch 'main'
}
steps {
sh './deploy.sh'
}
}
8. post:定义成功、失败或始终执行的收尾动作。
post {
always {
archiveArtifacts artifacts: 'build/**', allowEmptyArchive: true
}
success {
echo 'Pipeline succeeded'
}
failure {
echo 'Check Console Output for errors'
}
}
二、Agent
2.1 顶层 agent和Stage agent的区别
| 顶层 agent | Stage agent | |
|---|---|---|
| timeout 计时起点 | agent 分配之后开始 | agent 分配之前开始 |
| agent 分配时间 | 不计入 timeout | 计入 timeout |
| 风险 | 无 | agent 延迟可能导致 timeout 失败 |
2.2 stage参数
| 参数 | 用途 | 示例 |
|---|---|---|
any |
任意可用节点 | agent any |
none |
不分配全局 agent,每个 Stage 自己指定 | agent none |
label |
指定标签的节点 | agent { label 'linux && docker' } |
node |
同 label,但支持更多选项(如 customWorkspace) | agent { node { label 'xxx' } } |
docker |
在 Docker 容器中运行 | agent { docker 'maven:3.9' } |
dockerfile |
用源码中的 Dockerfile 构建容器 | agent { dockerfile true } |
kubernetes |
在 K8s Pod 中运行 | agent { kubernetes { yaml '...' } } |
三、环境变量
Jenkins Pipeline 中,环境变量通过全局对象 env 访问,例如:
echo "Build number: ${env.BUILD_NUMBER}"
常用内置变量包括:
BUILD_NUMBER:当前构建编号BUILD_ID:构建 IDJOB_NAME:任务名称BUILD_URL:本次构建页面链接WORKSPACE:当前构建工作目录JENKINS_URL:Jenkins 地址
完整变量列表可在 Jenkins 的 ${JENKINS_URL}/pipeline-syntax/globals#env 查看。
3.1 普通环境变量
Declarative Pipeline 使用 environment:
pipeline {
agent any
environment {
APP_NAME = 'chat-agent'
REGISTRY = 'harbor.example.com'
}
stages {
stage('Build') {
steps {
sh 'echo "Building $APP_NAME"'
}
}
}
}
作用域有两种:
-
pipeline下的environment:全局生效,所有 Stage 都可使用。 -
stage下的environment:仅对该 Stage 的步骤生效。stage('Build') {
environment {
BUILD_MODE = 'production'
}
steps {
sh 'echo $BUILD_MODE'
}
}
Scripted Pipeline 不用 environment,而使用 withEnv:
withEnv(['APP_NAME=chat-agent']) {
sh 'echo $APP_NAME'
}
补充:Declarative Pipeline和Scripted Pipeline的区别
| 对比 | Declarative Pipeline | Scripted Pipeline |
|---|---|---|
| 风格 | 声明"流水线有哪些阶段" | 用 Groovy 编程描述"如何执行" |
| 基本入口 | pipeline {} |
node {} |
| 结构约束 | 强,必须遵循固定结构 | 弱,可自由组合代码 |
| 易读性 | 高,适合团队维护 | 较低,逻辑复杂时不易读 |
| 错误提示 | 较早、较清晰 | 常在运行时才暴露 |
| 条件/循环/函数 | 支持有限,复杂时需 script {} |
原生支持 if、循环、函数、异常处理等 |
| 推荐场景 | 常规构建、测试、镜像发布、部署 | 动态生成 Stage、复杂分支或循环逻辑 |
3.2 动态设置变量
可以从 Shell 命令获取返回值:
environment {
VERSION = """${sh(
returnStdout: true,
script: 'git rev-parse --short HEAD'
).trim()}"""
}
注意:
- 动态执行
sh时,顶层必须有可用的agent,不能是agent none。 returnStdout: true的输出通常带换行,建议加.trim()。(因为sh(returnStdout: true, ...)返回的是命令的完整标准输出 ,通常包含末尾换行符\n。).trim()会移除字符串开头和末尾的空格、制表符和换行,保留中间内容。returnStatus: true可获取命令退出码。
3.3 使用环境变量注入 Jenkins 凭据
出于安全考虑,密码、Token 等不应该直接写进 Jenkinsfile,应先在 Jenkins 的 Credentials 中创建凭据,再通过凭据 ID 引用。
3.3.1 Secret Text
-
Token
environment {
HARBOR_TOKEN = credentials('harbor-token')
}
在 Shell 中使用:
sh 'curl -H "Authorization: Bearer $HARBOR_TOKEN" https://harbor.example.com'
-
用户名和密码
environment {
HARBOR_CREDS = credentials('harbor-username-password')
}
在environment中声明一次凭据绑定后,Jenkins 会自动注入三个变量:
HARBOR_CREDS # username:password
HARBOR_CREDS_USR # username
HARBOR_CREDS_PSW # password
_USR 和 _PSW 不需要、也不应分别写入 environment。使用如下,以登录 Harbor为例:
sh 'echo "$HARBOR_CREDS_PSW" | docker login harbor.example.com -u "$HARBOR_CREDS_USR" --password-stdin'
3.3.2 Secret File
如 Kubernetes 的配置文件可使用Secret File进行保存:
environment {
KUBECONFIG_FILE = credentials('kubeconfig-credential')
}
变量值是 Jenkins 临时创建的安全文件路径:
sh 'kubectl --kubeconfig "$KUBECONFIG_FILE" get pods'
3.4 安全要点
- 凭据放在 Jenkins Credentials,不提交到 Git。
- 尽量把凭据只定义在需要它的
stage,缩小作用域。 - Jenkins 会在日志中掩码部分敏感值,但这只是降低误泄露风险,不代表绝对安全。
- Shell 步骤中优先使用单引号包裹 Groovy 字符串,如
sh 'echo $TOKEN',让 Shell 展开变量;避免在 Groovy 双引号字符串中直接插入敏感变量。
补充:Groovy/Jenkinsfile 的引号,以及Shell 命令里的引号
| 层级 | 写法 | 是否展开变量 | 示例结果 / 用途 |
|---|---|---|---|
| Groovy(Jenkinsfile 外层) | 'echo $TOKEN' |
Groovy 不展开 | 原样传给 Shell;Shell 再展开 $TOKEN。适合凭据。 |
| Groovy(Jenkinsfile 外层) | "echo ${env.BUILD_NUMBER}" |
Groovy 展开 | 先变成 echo 123,再交给 Shell。适合非敏感变量。 |
| Groovy 多行 | '''echo "$TOKEN"''' |
Groovy 不展开 | 多行命令,交给 Shell 展开 $TOKEN。适合凭据和复杂命令。 |
| Groovy 多行 | """echo ${env.BUILD_NUMBER}""" |
Groovy 展开 | 多行命令中使用非敏感 Jenkins 变量。 |
| Shell(命令内部) | "$TOKEN" |
Shell 展开 | 输出 Token 的值;保留值内的空格,是常用安全写法。 |
| Shell(命令内部) | '$TOKEN' |
Shell 不展开 | 原样输出 $TOKEN。 |
| Shell(命令内部) | $TOKEN | Shell 展开 | 可以使用,但如果值包含空格,可能被拆分成多个参数。 |
推荐组合:
| 场景 | 推荐写法 | 原因 |
|---|---|---|
| 使用密码、Token、密钥 | sh 'curl -H "Authorization: Bearer $TOKEN" ...' |
凭据由 Shell 展开,不经 Groovy 插值。 |
| 多行登录 Harbor | `sh '''echo "$HARBOR_CREDS_PSW" | docker login ...'''` |
| 引用构建号、分支名等非敏感 Jenkins 值 | sh "docker build -t app:${env.BUILD_NUMBER} ." |
Groovy 插值直观可读。 |
| Shell 内传递路径/变量 | sh 'cd "$WORKSPACE" && ./build.sh' |
双引号避免路径中有空格时出错。 |
四、测试结果保存
| 要点 | 说明 |
|---|---|
| 问题 | 控制台输出太多,难以定位失败测试 |
| 解决 | Jenkins 自动记录和汇总测试结果文件 |
| 前提 | 测试运行时能输出测试结果文件(如 JUnit XML) |
| 内置支持 | junit步骤(Declarative Pipeline 自带) |
| 扩展支持 | 其他插件可处理非 JUnit 格式的测试报告 |
4.1 junit 步骤用法
post {
always {
junit 'build/reports/**/*.xml' // 收集 JUnit 格式的测试报告
}
}
| 属性 | 说明 |
|---|---|
always |
无论构建成功/失败,都执行 |
路径通配符 **/*.xml |
递归匹配所有 XML 测试报告 |
补充:构建状态的区别
| 状态 | 颜色 | 含义 |
|---|---|---|
| SUCCESS | 绿色 | 全部通过 |
| UNSTABLE | 黄色 | 有测试失败,但 Pipeline 本身执行成功 |
| FAILED | 红色 | Pipeline 执行失败(编译错误、脚本异常等) |
关键区别:测试失败 = UNSTABLE(黄),构建脚本失败 = FAILED(红)
4.2 构建产物归档:archiveArtifacts
post {
always {
archiveArtifacts artifacts: 'build/libs/**/*.jar', fingerprint: true
junit 'build/reports/**/*.xml'
}
}
| 参数 | 说明 |
|---|---|
artifacts |
要归档的文件路径(支持通配符) |
fingerprint |
生成文件指纹,用于追踪产物来源 |
| 省略参数名 | 如果只传路径,可简写 archiveArtifacts 'build/libs/**/*.jar' |
简写语法:
// 完整写法
archiveArtifacts artifacts: 'build/libs/**/*.jar', fingerprint: true
// 简写(仅路径,省略参数名)
archiveArtifacts 'build/libs/**/*.jar'
注意:多参数时必须显式指定参数名。
完整示例
pipeline {
agent any
stages {
stage('Build') {
steps {
sh './gradlew build'
}
}
stage('Test') {
steps {
sh './gradlew check' // 运行测试
}
}
}
post {
always {
// 归档构建产物(jar 包)
archiveArtifacts artifacts: 'build/libs/**/*.jar', fingerprint: true
// 收集测试报告
junit 'build/reports/**/*.xml'
}
}
}
五、清理和通知
5.1 清理
post 部分的作用
| 特性 | 说明 |
|---|---|
| 保证执行 | Pipeline 结束时必定运行,无论成败 |
| 用途 | 清理资源、发送通知、记录状态 |
| 位置 | Pipeline 顶层或 Stage 级别 |
post 的条件块
| 条件 | 触发时机 | 推荐 |
|---|---|---|
always |
总是执行(无论成败) | 放清理 |
success |
仅 Pipeline 成功时 | 放轻量通知 |
unstable |
Pipeline 不稳定时(测试失败) | |
failure |
Pipeline 失败时 | 放告警 |
changed |
当前构建状态与上一次不同时(成功→失败 或 失败→成功) | 用于状态翻转告警 |
完整示例
post {
always {
echo 'One way or another, I have finished'
deleteDir() // 清理工作区
}
success {
echo 'I succeeded!'
}
unstable {
echo 'I am unstable :/'
}
failure {
echo 'I failed :('
}
changed {
echo 'Things were different before...'
}
}
常用内置步骤
| 步骤 | 用途 |
|---|---|
deleteDir() |
删除当前工作区目录(清理) |
cleanWs() |
更彻底的清理(需 Workspace Cleanup 插件) |
echo |
打印日志 |
5.2 通知
5.2.1 邮件通知
post {
failure {
mail to: 'team@example.com',
subject: "Failed Pipeline: ${currentBuild.fullDisplayName}",
body: "Something is wrong with ${env.BUILD_URL}"
}
}
5.2.2 Hipchat 通知
post {
failure {
hipchatSend message: "Attention @here ${env.JOB_NAME} #${env.BUILD_NUMBER} has failed.",
color: 'RED'
}
}
5.2.3 Slack 通知
post {
success {
slackSend channel: '#ops-room',
color: 'good',
message: "The pipeline ${currentBuild.fullDisplayName} completed successfully."
}
}
六、部署
6.1 基本部署 Pipeline
| 阶段 | 作用 |
|---|---|
| Build | 编译构建 |
| Test | 测试验证 |
| Deploy | 部署发布 |
pipeline {
agent any
stages {
stage('Build') { steps { echo 'Building' } }
stage('Test') { steps { echo 'Testing' } }
stage('Deploy') { steps { echo 'Deploying' } }
}
}
前序阶段失败时,后续部署不会执行。
6.2 Stage即部署环境
使用清晰的 Stage 名称区分部署目标,例如 Deploy - Staging 和 Deploy - Production。
stage('Deploy - Staging') {
steps {
sh './deploy staging' // 部署到预发布
sh './run-smoke-tests' // 冒烟测试
}
}
stage('Deploy - Production') {
steps {
sh './deploy production' // 部署到生产
}
}
部署到 Staging 后执行冒烟测试,验证基础功能正常,再考虑发布 Production。
6.3 持续部署 vs 持续交付
| 持续部署(Continuous Deployment) | 持续交付(Continuous Delivery) | |
|---|---|---|
| 定义 | 代码自动部署到生产环境 | 代码可自动部署,但需人工触发 |
| 人工确认 | 不需要 | 需要 |
| 风险 | 高,问题直接上线 | 可控,有人工把关 |
| 适用 | 高度自动化、测试完善 | 大多数企业场景 |
官方页面观点:纯自动持续部署不一定是好实践,持续交付更稳妥。
6.4 人工确认:input 步骤
| 用途 | 在阶段间暂停,等待人工审批 |
|---|---|
| 场景 | Staging 验证通过后,人工确认再上 Production |
| 效果 | Pipeline 暂停,页面显示交互按钮 |
stage('Sanity check') {
steps {
input "Does the staging environment look ok?" // 等待人工确认
}
}
执行效果:
Pipeline paused at: Sanity check
[Proceed] [Abort] ← 页面上出现按钮,需人工点击
完整 Pipeline 示例:
pipeline {
agent any
stages {
stage('Build') {
steps { sh './build' }
}
stage('Test') {
steps { sh './test' }
}
stage('Deploy - Staging') {
steps {
sh './deploy staging'
sh './run-smoke-tests'
}
}
stage('Sanity check') { // 人工卡点
steps {
input "Does the staging environment look ok?"
}
}
stage('Deploy - Production') {
steps { sh './deploy production' }
}
}
}
七、补充
7.1 Jenkinsfile存放位置
- 存放在GitLab端
如果Jenkinsfile存放在GitLab端,是否还需要Pull SourceCode阶段?(Jenkins是否会拉取项目其他代码?)
参考链接1:Using a Jenkinsfile
参考链接2: Getting started with Pipeline
| 情况 | 是否要在 Jenkinsfile 写 checkout scm |
|---|---|
| 使用默认检出 | 不需要。Jenkins 在进入第一个可执行 Stage 前自动检出项目代码。 |
| 显式控制检出流程 | 需要。关闭默认检出后,自己写 checkout scm。 |
| 按指定 Tag、额外仓库或自定义 Git 行为检出 | 需要。写完整 checkout(...)配置。 |

- 存放在Jenkins端

7.2 凭证创建
凭证创建在项目目录下,而不是个人的凭证库
7.3 问题
- 为什么需要先打包上传到Nexus?可以本地打包,也可以由Dockerfile包含打包。
答:(1)解耦,编译环境和运行环境分离(2)中间的打包产物可以存档,并且被多个环境使用(3)镜像构建过程更快只需要COPY现成的。
- Jenkins怎样将各个过程连通起来的?以及中间产生的产物?
Jenkins 每个 Pipeline 运行时,会在 Agent(构建节点)上创建一个工作目录,所有步骤共享这个目录。中间产物就是靠文件系统传递的,而非网络。
- 为什么Jenkins可以做到打包、构建?这应该不是借助自身的能力
Jenkins 本身并不内置打包或 Docker 的能力,它是调度器 + 插件系统,真正起作用的是所在的服务器环境。实际上是解析 Pipeline 脚本 → 生成 Shell 命令 → 调用服务器上的外部工具(Maven、Docker、Git...),所以这就要求Jenkins Agent / Node 服务器预先安装要使用到的工具(比如Git、mvn、java等)
- Jenkins的Agent和Node 服务器
Agent 是 Jenkins 的"抽象概念",Node 是"物理实体"。
参考
Jenkins用户手册:Jenkins Handbook
和GitLab联动:Jenkins | GitLab Docs
如有问题或建议,欢迎在评论区中留言~