跳到主要内容

Pipeline 基础

本章将介绍 Jenkins Pipeline 的基础语法和核心概念,帮助你掌握声明式 Pipeline 的编写。

Pipeline 简介

Pipeline 是 Jenkins 的核心功能,允许你使用代码定义整个 CI/CD 流程。

Pipeline 类型对比

特性Declarative PipelineScripted Pipeline
语法结构化、简单灵活、强大
学习曲线平缓较陡
错误检查自动验证运行时检查
适用场景标准 CI/CD 流程复杂逻辑处理
代码复用共享库共享库

为什么选择 Declarative Pipeline?

  1. 易于理解和维护:结构清晰,语法简洁
  2. 内置验证:提交前可检查语法错误
  3. Blue Ocean 支持:现代化可视化界面
  4. 最佳实践内置:默认启用推荐配置

Pipeline 基本结构

pipeline {
// 代理配置
agent any

// 环境变量
environment {
APP_NAME = 'my-app'
VERSION = '1.0.0'
}

// 参数定义
parameters {
string(name: 'BRANCH', defaultValue: 'main', description: '分支名称')
choice(name: 'ENV', choices: ['dev', 'test', 'prod'], description: '环境')
}

// 构建选项
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
}

// 工具配置
tools {
maven 'Maven-3.8'
jdk 'JDK-11'
}

// 触发器
triggers {
cron('H/15 * * * *')
}

// 阶段定义
stages {
stage('Build') {
steps {
echo 'Building...'
}
}
}

// 后置处理
post {
always {
echo 'Always executed'
}
}
}

Agent 配置

Agent 指定 Pipeline 在哪里运行。

常用配置

// 任意可用节点
pipeline {
agent any
// ...
}

// 指定标签
pipeline {
agent {
label 'linux && docker'
}
// ...
}

// 指定节点
pipeline {
agent {
node {
label 'linux'
customWorkspace '/var/jenkins/workspace/custom'
}
}
// ...
}

// Docker 容器
pipeline {
agent {
docker {
image 'maven:3.8-openjdk-11'
args '-v /root/.m2:/root/.m2'
}
}
// ...
}

// Kubernetes Pod
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.8-openjdk-11
command: ['cat']
tty: true
'''
}
}
// ...
}

阶段级 Agent

不同阶段可以使用不同的 Agent:

pipeline {
agent none

stages {
stage('Build') {
agent {
docker { image 'maven:3.8' }
}
steps {
sh 'mvn clean compile'
}
}

stage('Test') {
agent {
docker { image 'node:16' }
}
steps {
sh 'npm test'
}
}
}
}

Stage 和 Steps

Stage(阶段)

Stage 是 Pipeline 的逻辑分组:

stages {
stage('Checkout') {
steps {
git 'https://github.com/example/repo.git'
}
}

stage('Build') {
steps {
sh 'mvn clean package'
}
}

stage('Test') {
steps {
sh 'mvn test'
}
}

stage('Deploy') {
steps {
sh 'mvn deploy'
}
}
}

Steps(步骤)

Steps 是具体的操作命令:

steps {
// Shell 命令
sh 'echo "Hello"'
sh '''
echo "Line 1"
echo "Line 2"
'''

// Windows 批处理
bat 'echo Hello'

// PowerShell
powershell 'Write-Output "Hello"'

// 输出信息
echo 'Message'

// 错误处理
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
sh 'might-fail.sh'
}
}

环境变量

定义环境变量

pipeline {
agent any

environment {
// 静态值
APP_NAME = 'my-application'
VERSION = '1.0.0'

// 动态值
BUILD_TIME = sh(script: 'date +%Y%m%d%H%M%S', returnStdout: true).trim()

// 凭据
DB_PASSWORD = credentials('db-password')

// 组合值
FULL_NAME = "${APP_NAME}-${VERSION}"
}

stages {
stage('Build') {
steps {
echo "Building ${env.FULL_NAME}"
echo "Build time: ${env.BUILD_TIME}"
}
}
}
}

使用环境变量

steps {
// 使用 env 对象
echo "Build number: ${env.BUILD_NUMBER}"

// 直接使用变量名
echo "Job name: ${JOB_NAME}"

// Shell 中使用
sh 'echo "Build: ${BUILD_NUMBER}"'

// 设置环境变量
script {
env.MY_VAR = 'value'
}
}

内置环境变量

变量名说明
BUILD_NUMBER构建编号
BUILD_ID构建 ID
BUILD_URL构建 URL
JOB_NAME任务名称
JOB_URL任务 URL
WORKSPACE工作空间路径
NODE_NAME节点名称
BUILD_USER触发用户

参数化构建

定义参数

pipeline {
agent any

parameters {
// 字符串参数
string(
name: 'BRANCH',
defaultValue: 'main',
description: 'Git branch to build'
)

// 选项参数
choice(
name: 'ENVIRONMENT',
choices: ['development', 'testing', 'production'],
description: 'Deployment environment'
)

// 布尔参数
booleanParam(
name: 'SKIP_TESTS',
defaultValue: false,
description: 'Skip test execution'
)

// 文本参数
text(
name: 'DESCRIPTION',
defaultValue: '',
description: 'Build description'
)

// 密码参数
password(
name: 'SECRET_KEY',
description: 'Secret key'
)

// 文件参数
file(
name: 'CONFIG_FILE',
description: 'Configuration file'
)
}

stages {
stage('Build') {
steps {
echo "Branch: ${params.BRANCH}"
echo "Environment: ${params.ENVIRONMENT}"
}
}
}
}

使用参数

stages {
stage('Checkout') {
steps {
git branch: params.BRANCH, url: 'https://github.com/example/repo.git'
}
}

stage('Test') {
when {
expression { !params.SKIP_TESTS }
}
steps {
sh 'mvn test'
}
}

stage('Deploy') {
steps {
script {
if (params.ENVIRONMENT == 'production') {
input message: '确认部署到生产环境?'
}
sh "./deploy.sh ${params.ENVIRONMENT}"
}
}
}
}

条件执行

When 条件

stages {
stage('Deploy to Dev') {
when {
branch 'develop'
}
steps {
sh './deploy-dev.sh'
}
}

stage('Deploy to Prod') {
when {
branch 'main'
}
steps {
sh './deploy-prod.sh'
}
}

stage('Release') {
when {
allOf {
branch 'main'
expression { params.RELEASE == true }
}
}
steps {
sh './release.sh'
}
}

stage('Specific Branch') {
when {
anyOf {
branch 'main'
branch 'release/*'
}
}
steps {
echo 'Deploying...'
}
}
}

条件表达式

when {
// 分支匹配
branch 'main'

// 环境变量
environment name: 'DEPLOY_TO', value: 'production'

// 表达式
expression { params.ENVIRONMENT == 'production' }

// 标签
tag 'release-*'

// 变更集
changeset "**/*.js"

// 触发原因
triggeredBy 'TimerTrigger'
}

并行执行

Parallel Stages

pipeline {
agent any

stages {
stage('Build') {
steps {
sh 'mvn clean compile'
}
}

stage('Test') {
parallel {
stage('Unit Tests') {
steps {
sh 'mvn test'
}
}
stage('Integration Tests') {
steps {
sh 'mvn integration-test'
}
}
stage('UI Tests') {
steps {
sh 'npm run test:ui'
}
}
}
}

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

动态并行

pipeline {
agent any

stages {
stage('Build Matrix') {
steps {
script {
def platforms = ['linux', 'windows', 'macos']
def parallelStages = [:]

platforms.each { platform ->
parallelStages[platform] = {
stage("Build on ${platform}") {
echo "Building for ${platform}"
// 实际构建命令
}
}
}

parallel parallelStages
}
}
}
}
}

后置处理

Post 条件

pipeline {
agent any

stages {
stage('Build') {
steps {
sh 'mvn clean package'
}
}
}

post {
// 总是执行
always {
echo 'Pipeline completed'
cleanWs() // 清理工作空间
}

// 构建成功时
success {
echo 'Build succeeded!'
mail to: '[email protected]',
subject: "Build Success: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Build completed successfully"
}

// 构建失败时
failure {
echo 'Build failed!'
mail to: '[email protected]',
subject: "Build Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Build failed. Check console output"
}

// 不稳定时
unstable {
echo 'Build is unstable'
}

// 被中止时
aborted {
echo 'Build was aborted'
}

// 状态改变时
changed {
echo 'Build status changed'
}

// 固定条件
fixed {
echo 'Build was fixed'
}

// 回归时
regression {
echo 'Build regressed'
}
}
}

输入和审批

人工审批

pipeline {
agent any

stages {
stage('Build') {
steps {
sh 'mvn clean package'
}
}

stage('Deploy to Staging') {
steps {
sh './deploy-staging.sh'
}
}

stage('Deploy to Production') {
steps {
script {
def userInput = input(
id: 'DeployApproval',
message: '部署到生产环境?',
parameters: [
choice(
name: 'ENVIRONMENT',
choices: ['production-us', 'production-eu'],
description: '选择部署区域'
),
string(
name: 'VERSION',
description: '部署版本'
),
booleanParam(
name: 'SKIP_BACKUP',
defaultValue: false,
description: '跳过备份'
)
],
submitter: 'admin,deployer',
submitterParameter: 'APPROVER'
)

echo "Approved by: ${userInput.APPROVER}"
echo "Environment: ${userInput.ENVIRONMENT}"

sh "./deploy-prod.sh ${userInput.ENVIRONMENT} ${userInput.VERSION}"
}
}
}
}
}

错误处理

异常处理

pipeline {
agent any

stages {
stage('Build') {
steps {
script {
try {
sh 'mvn clean package'
} catch (Exception e) {
echo "Build failed: ${e.getMessage()}"
currentBuild.result = 'FAILURE'
error('Stopping pipeline')
}
}
}
}

stage('Test') {
steps {
// 忽略错误继续执行
catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') {
sh 'mvn test'
}
}
}

stage('Optional Step') {
steps {
// 允许失败
warnError('Step failed but continuing') {
sh 'might-fail.sh'
}
}
}
}
}

完整示例

pipeline {
agent {
label 'linux && docker'
}

environment {
APP_NAME = 'my-app'
DOCKER_REGISTRY = 'registry.example.com'
GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
}

parameters {
string(name: 'BRANCH', defaultValue: 'main', description: 'Git branch')
choice(name: 'ENV', choices: ['dev', 'test', 'prod'], description: 'Environment')
booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: 'Skip tests')
}

options {
buildDiscarder(logRotator(numToKeepStr: '10'))
timeout(time: 1, unit: 'HOURS')
disableConcurrentBuilds()
timestamps()
}

stages {
stage('Checkout') {
steps {
checkout scm
echo "Building branch: ${params.BRANCH}"
}
}

stage('Build') {
steps {
script {
def image = docker.build("${DOCKER_REGISTRY}/${APP_NAME}:${GIT_COMMIT_SHORT}")
}
}
}

stage('Test') {
when {
expression { !params.SKIP_TESTS }
}
parallel {
stage('Unit Tests') {
steps {
sh 'docker run --rm ${DOCKER_REGISTRY}/${APP_NAME}:${GIT_COMMIT_SHORT} npm test'
}
}
stage('Lint') {
steps {
sh 'docker run --rm ${DOCKER_REGISTRY}/${APP_NAME}:${GIT_COMMIT_SHORT} npm run lint'
}
}
}
}

stage('Push Image') {
when {
anyOf {
branch 'main'
branch 'release/*'
}
}
steps {
script {
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-credentials') {
docker.image("${DOCKER_REGISTRY}/${APP_NAME}:${GIT_COMMIT_SHORT}").push()
docker.image("${DOCKER_REGISTRY}/${APP_NAME}:${GIT_COMMIT_SHORT}").push('latest')
}
}
}
}

stage('Deploy') {
when {
branch 'main'
}
steps {
script {
if (params.ENV == 'prod') {
input message: '确认部署到生产环境?', ok: '部署'
}
sh "./deploy.sh ${params.ENV} ${GIT_COMMIT_SHORT}"
}
}
}
}

post {
always {
sh 'docker system prune -f'
cleanWs()
}
success {
echo 'Pipeline completed successfully!'
}
failure {
mail to: '[email protected]',
subject: "Pipeline Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Check console output at ${env.BUILD_URL}"
}
}
}

下一步

掌握 Pipeline 基础后,你可以:

  1. 学习更多 Pipeline 脚本