【CICD】Jenkins Pipeline 快速学习入门

文章持续更新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. shbat:执行命令行。

复制代码
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:构建 ID
  • JOB_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

  1. Token

    environment {
    HARBOR_TOKEN = credentials('harbor-token')
    }

在 Shell 中使用:

复制代码
sh 'curl -H "Authorization: Bearer $HARBOR_TOKEN" https://harbor.example.com'
  1. 用户名和密码

    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 - StagingDeploy - 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存放位置

  1. 存放在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(...)配置。
  1. 存放在Jenkins端

7.2 凭证创建

凭证创建在项目目录下,而不是个人的凭证库

7.3 问题

  1. 为什么需要先打包上传到Nexus?可以本地打包,也可以由Dockerfile包含打包。

答:(1)解耦,编译环境和运行环境分离(2)中间的打包产物可以存档,并且被多个环境使用(3)镜像构建过程更快只需要COPY现成的。

  1. Jenkins怎样将各个过程连通起来的?以及中间产生的产物?

Jenkins 每个 Pipeline 运行时,会在 Agent(构建节点)上创建一个工作目录,所有步骤共享这个目录。中间产物就是靠文件系统传递的,而非网络。

  1. 为什么Jenkins可以做到打包、构建?这应该不是借助自身的能力

Jenkins 本身并不内置打包或 Docker 的能力,它是调度器 + 插件系统,真正起作用的是所在的服务器环境。实际上是解析 Pipeline 脚本 → 生成 Shell 命令 → 调用服务器上的外部工具(Maven、Docker、Git...),所以这就要求Jenkins Agent / Node 服务器预先安装要使用到的工具(比如Git、mvn、java等)

  1. Jenkins的Agent和Node 服务器

Agent 是 Jenkins 的"抽象概念",Node 是"物理实体"。

参考

Jenkins用户手册:Jenkins Handbook

和GitLab联动:Jenkins | GitLab Docs


如有问题或建议,欢迎在评论区中留言~

相关推荐
神秘的MT1 小时前
C# WinForm Socket 类 常用方法 (C# .NET,TCP 场景)
服务器·网络·学习·tcp/ip·c#·winform
艾莉丝努力练剑2 小时前
【MYSQL】MYSQL学习的一大重点:事务(上)- 原子性与持久性
android·数据库·学习·mysql·面试
丶玉面小蛟龙3 小时前
DGCNN网络学习
学习·点云·dgcnn
晓梦林9 小时前
BUUCTF findKey-学习笔记
笔记·学习
0566469 小时前
agent学习Day15——SQLAlchemy 查询过滤、分页与历史列表接口
python·学习·fastapi
nxb55611 小时前
haproxy算法,ip透传,自定义haproxy错误页面
运维·服务器
AKAMAI11 小时前
你的源服务器可能是你做出的最昂贵决定
运维·人工智能·云计算
炎武丶航12 小时前
汽车功能测试学习(1):FCW前方碰撞预警
功能测试·学习·汽车
Dxy123931021612 小时前
Linux 编译安装 Python 3.12.10(多版本共存,不破坏系统Python)
linux·运维·python