Jenkins-Pipeline流水线
核心概念
- Pipeline(流水线):代表整个CI/CD流程,通常通过一个名为
Jenkinsfile的文本文件定义。这个文件可以存储在项目的源代码仓库中,实现“Pipeline as Code”(流水线即代码),便于版本控制和团队协作。 - Stage(阶段):Pipeline 中的逻辑分组单元,代表流程中的一个大的步骤,如构建、测试、部署等。每个 Stage 包含一系列具体的 Step。
- Step(步骤):Pipeline 中最小的可执行单元,代表一个具体的操作,例如执行一条 Shell 命令、拉取代码、发送邮件等。
- Agent(代理):指定 Pipeline 或特定 Stage 在哪个 Jenkins 节点(Master 或 Agent)上执行。你可以指定标签或使用 Docker 容器作为运行环境。
- Post(后置处理):定义 Pipeline 或 Stage 执行完成后,根据结果(成功、失败、总是等)执行的操作,例如清理资源、发送通知。
特性
Jenkins Pipeline 的强大之处在于其丰富的高级特性:
- 参数化构建:允许在触发 Pipeline 时传入参数,增加灵活性。
- 并行执行:
parallel指令可以让多个 Stage 或 Step 同时执行,显著缩短整体执行时间。 - 条件执行:
when指令可以根据条件(如分支、变量值等)决定是否执行某个 Stage。 - 异常处理:通过
try-catch块捕获和处理异常,提高流程的健壮性。 - 集成凭证管理:安全地使用密码、API 密钥等敏感信息,避免硬编码在脚本中。
Jenkinsfile 的优势
将 Pipeline 定义在 Jenkinsfile 并存入代码库有诸多好处:
- 版本化与可追溯:Pipeline 的变更历史与代码变更历史一同管理。
- 协作与复审:团队成员可以像审查代码一样审查 CI/CD 流程。
- 单一可信源:为所有分支和拉取请求自动提供一致的构建流程。
- 可移植性:易于在多个 Jenkins 实例间迁移或复制项目。
核心结构
Pipeline 两种语法
| 特性 | 声明式 Pipeline (Declarative Pipeline) | 脚本式 Pipeline (Scripted Pipeline) |
|---|---|---|
| 结构 | 结构固定,更严格,必须包含在 pipeline {}块内。 | 结构灵活,基于 Groovy 语法,最外层由 node {}包裹。 |
| 语法 | 提供更简洁、直观的语法,每个语句通常独立一行,行尾无需分号。 | 可以直接使用 Groovy 脚本及其语法结构(如循环、条件判断)。 |
| 学习曲线 | 易于上手,适合初学者。 | 需要一定的 Groovy 脚本知识,灵活性更高。 |
| 错误检查 | 在执行前会进行更多的验证和错误检查。 | 错误可能需要在执行过程中才发现。 |
声明式 Pipeline 示例
groovy
pipeline {
agent any // 指定执行节点,any 表示任意可用节点
stages { // 阶段集合
stage('Build') { // 定义一个名为 Build 的阶段
steps { // 步骤集合
// 在这里定义具体的步骤,例如:
sh 'mvn clean package' // 执行 Shell 命令
}
}
stage('Test') {
steps {
sh 'mvn test'
}
}
}
post {
always {
echo 'Pipeline 执行完成(无论成功与否)'
}
success {
echo 'Pipeline 执行成功!'
}
failure {
echo 'Pipeline 执行失败!'
}
}
}脚本式 Pipeline 示例
groovy
node {
stage('Build') {
echo 'Building...'
}
}Pipeline 核心组成
pipeline:声明其内容为一个声明式的 Pipeline 脚本,是声明式 Pipeline 的最外层块。agent:定义整个 Pipeline 或特定阶段在哪个 Jenkins 节点(Agent)上执行。常见参数:any: 在任何可用的 Agent 上执行。none: 不指定全局 Agent,需要在每个stage中单独定义。label: 在指定标签的 Agent 上执行,例如agent { label 'java' }。docker: 在 Docker 容器中执行,例如agent { docker 'maven:3.8.1-openjdk-17' }。
stages:包含一个或多个stage的容器,描述了整个流水线的多个阶段(如构建、测试、部署)。stage:代表 Pipeline 中的一个阶段,每个stage都需要有一个唯一的名称来描述其任务(例如 'Build'、'Test'、'Deploy')。一个stage内部可以包含steps、stages(嵌套)或parallel。steps:位于stage指令块内部,包含一个或多个具体的步骤(step)。这是真正执行操作的地方,例如执行 Shell 命令、编译代码等。post:定义在 Pipeline 或阶段完成后的一些附加操作,通常根据构建结果状态来执行。常用条件块:always: 无论结果如何都执行。success: 仅当当前状态为成功时执行。failure: 仅当当前状态为失败时执行。unstable: 当当前状态为不稳定时执行。changed: 如果当前构建的状态与上次构建的状态不同则执行。fixed: 当上次构建的状态为失败或不稳定,且当前构建的状态为成功时执行。regression: 当上次构建的状态为成功,且当前构建的状态为失败、不稳定或中止时执行。aborted: 当当前构建的状态为中止(一般为人为中止)时执行。cleanup: 清理条件块,在其他所有条件块执行完成后执行。
Jenkins指令
Jenkins Pipeline 提供了丰富的指令来帮助你定义和管理持续集成/持续部署 (CI/CD) 流程。下面是一些核心指令的完整语法和案例,均基于 声明式 Pipeline。
environment
用于设置环境变量,可定义在 pipeline 顶层或特定 stage 中。
groovy
pipeline {
agent any
// 全局作用域变量 (整条流水线可用)
environment {
PROJECT_NAME = "my-app" // 普通变量
// 安全地引用Jenkins中预定义的凭据(例如Secret Text类型)
API_TOKEN = credentials('my-api-token-credential-id')
// 对于Username and password类型凭据,会自动生成两个附加变量:MYVARNAME_USR 和 MYVARNAME_PSW
DB_CREDS = credentials('db-user-pass-id') // 会生成 DB_CREDS_USR 和 DB_CREDS_PSW
}
stages {
stage('Example') {
// 阶段作用域变量 (仅本阶段可用)
environment {
DEPLOY_ENV = 'staging'
// 可以引用全局作用域变量
URL = "https://${PROJECT_NAME}.example.com"
}
steps {
// 🔧 步骤作用域变量 (仅 withEnv 包裹的步骤内可用)
withEnv(['TEMPORARY_VAR=temp_value']) {
sh '''
echo "Project: $PROJECT_NAME"
echo "Deploy Env: $DEPLOY_ENV"
echo "API Token is set (securely): $API_TOKEN"
echo "DB User: $DB_CREDS_USR"
echo "Temporary Var: $TEMPORARY_VAR"
'''
}
// 步骤外无法访问 TEMPORARY_VAR
}
}
}
}options
用于配置 Pipeline 本身的选项。
groovy
pipeline {
agent any
options {
timeout(time: 1, unit: 'HOURS') // 设置Pipeline运行的超时时间
retry(3) // 失败后,重试整个Pipeline的次数
timestamps() // 在控制台打印所有操作的时间戳
buildDiscarder(logRotator(numToKeepStr: '10')) // 仅保留最近10次构建的历史
disableConcurrentBuilds() // 禁止并发执行Pipeline
skipDefaultCheckout() // 默认跳过来自源代码控制的代码检出
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}parameters
定义参数化构建的参数,允许用户在触发 Pipeline 时输入。
groovy
pipeline {
agent any
parameters {
string(name: 'DEPLOY_TARGET', defaultValue: 'staging', description: 'Target environment to deploy to')
choice(name: 'LOG_LEVEL', choices: ['INFO', 'DEBUG', 'WARN'], description: 'Logging level')
booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'Whether to run the test suite')
text(name: 'CUSTOM_CONFIG', defaultValue: '', description: 'Multi-line custom configuration')
// 文件参数 (需要安装相应插件)
// base64File name: 'CONFIG_FILE', description: 'A base64 encoded config file'
}
stages {
stage('Deploy') {
steps {
script {
echo "Deploying to: ${params.DEPLOY_TARGET}"
echo "Log level: ${params.LOG_LEVEL}"
echo "Run tests: ${params.RUN_TESTS}"
echo "Custom config: ${params.CUSTOM_CONFIG}"
// 使用文件参数内容
// sh 'echo $CONFIG_FILE | base64 --decode > config.yaml'
}
}
}
}
}triggers
定义 Pipeline 的自动化触发方式。
groovy
pipeline {
agent any
triggers {
// 定时构建,使用cron语法
cron('H */4 * * 1-5') // 每周一到周五,每4小时构建一次(H代表哈希,让负载均匀)
// 轮询SCM(不推荐,建议使用Webhooks)
// pollSCM('H */2 * * *') // 每2分钟检查一次SCM变更
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}tools
自动安装并配置所需的工具,并将其加入 PATH 环境变量。这要求工具已在 Jenkins 的 Global Tool Configuration 中预配置。
groovy
pipeline {
agent any
tools {
maven 'Maven-3.8.6' // 使用Jenkins中预配置的名为'Maven-3.8.6'的Maven版本
jdk 'JDK-17' // 使用Jenkins中预配置的名为'JDK-17'的JDK版本
nodejs 'NodeJS-16' // 使用Jenkins中预配置的名为'NodeJS-16'的Node.js版本
}
environment {
// 通常tools会自动修改PATH,但有时也可显式引用
MAVEN_HOME = tool 'Maven-3.8.6'
PATH = "${tool 'Maven-3.8.6'}/bin:${env.PATH}"
}
stages {
stage('Build') {
steps {
sh 'mvn --version'
sh 'java -version'
sh 'node --version'
}
}
}
}input
暂停 Pipeline,等待用户输入或确认。
groovy
pipeline {
agent any
stages {
stage('Deploy to Staging') {
steps {
echo 'Deploying to staging environment...'
}
}
stage('Approve Production Deploy') {
steps {
// 暂停Pipeline,等待用户输入
input(
message: 'Deploy to production?',
ok: 'Confirm',
submitter: 'admin,deploy-manager', // 可选:指定批准人
parameters: [ // 可选:在批准时可以同时提供一些参数
string(name: 'RELEASE_NOTES', defaultValue: '', description: 'Release notes')
]
)
script {
echo "User provided release notes: ${params.RELEASE_NOTES}"
}
}
}
stage('Deploy to Production') {
steps {
echo 'Deploying to production...'
}
}
}
}when
根据条件决定是否执行某个 stage。
groovy
pipeline {
agent any
parameters {
choice(name: 'DEPLOY_ENV', choices: ['dev', 'staging', 'prod'], description: 'Deployment Environment')
}
environment {
SKIP_DEPLOY = 'false'
}
stages {
stage('Build') {
steps {
echo 'Building the application...'
}
}
stage('Deploy to Dev') {
when {
// 当部署环境参数为'dev'时执行
expression { params.DEPLOY_ENV == 'dev' }
}
steps {
echo 'Deploying to development environment...'
}
}
stage('Deploy to Staging') {
when {
allOf {
// 当部署环境参数为'staging' AND 环境变量SKIP_DEPLOY不为'true'时执行
expression { params.DEPLOY_ENV == 'staging' }
expression { env.SKIP_DEPLOY != 'true' }
}
}
steps {
echo 'Deploying to staging environment...'
}
}
stage('Deploy to Production') {
when {
// 更复杂的条件:分支为main或master,且环境是prod,且未跳过部署
allOf {
branch 'main|master' // 使用正则表达式
expression { params.DEPLOY_ENV == 'prod' }
expression { env.SKIP_DEPLOY != 'true' }
}
// anyOf, not 等也可用于构建复杂条件
}
steps {
echo 'Deploying to production environment...'
}
}
stage('Run Tests') {
when {
// 只有当之前的阶段都没有失败时才会执行
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
}
steps {
echo 'Running tests...'
}
}
}
}parallel
并行执行多个 stage 或 step,以提升构建效率。
groovy
pipeline {
agent none // 顶层agent设为none,在并行stage中单独指定
stages {
stage('Build and Test') {
steps {
echo 'This stage runs before the parallel execution.'
}
}
stage('Run Parallel Tests') {
// 可以设置failFast true,表示任何一个并行任务失败就立即终止所有任务
failFast true
parallel {
stage('Unit Tests - Linux') {
agent {
label 'linux-agent'
}
steps {
echo 'Running unit tests on Linux...'
sh './run-unit-tests.sh'
}
}
stage('Unit Tests - Windows') {
agent {
label 'windows-agent'
}
steps {
echo 'Running unit tests on Windows...'
bat '.\\run-unit-tests.bat'
}
}
stage('Integration Tests') {
agent any
stages {
// 并行阶段内部还可以有串行的子阶段
stage('Init DB') {
steps {
echo 'Initializing test database...'
}
}
stage('Run IT') {
steps {
echo 'Running integration tests...'
}
}
}
}
}
}
stage('Deploy') {
steps {
echo 'This stage runs after the parallel execution.'
}
}
}
}实战案例:项目自动更新
假设有一个基于 Docker 的项目更新流程,其 Jenkins Pipeline 可能包含以下阶段:
groovy
pipeline {
agent { label 'your-agent-label' } // 在指定标签的 agent 上运行
environment {
// 定义环境变量
DEPLOY_PATH = '/path/to/your/app'
BACKUP_SUFFIX = "-backup-${currentBuild.number}"
}
stages {
stage('拉取代码') {
steps {
git branch: 'main', url: 'https://your-git-repo.com/your-project.git'
}
}
stage('备份旧文件') {
steps {
sh """
cp ${DEPLOY_PATH}/your-app.jar ${DEPLOY_PATH}/your-app.jar${BACKUP_SUFFIX}
echo "旧文件已备份。"
"""
}
}
stage('构建应用') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('更新文件') {
steps {
sh """
cp target/your-app.jar ${DEPLOY_PATH}/
chmod 755 ${DEPLOY_PATH}/your-app.jar
"""
}
}
stage('重启服务') {
steps {
sh 'docker restart your-app-container'
}
}
}
post {
success {
emailext to: 'team@example.com',
subject: "SUCCESS: Pipeline ${currentBuild.fullDisplayName}",
body: "项目已成功更新并部署。\n详情: ${env.BUILD_URL}"
}
failure {
emailext to: 'team@example.com',
subject: "FAILURE: Pipeline ${currentBuild.fullDisplayName}",
body: "项目更新失败,请检查日志。\n详情: ${env.BUILD_URL}"
}
}
}