Pipeline 基础
本章将介绍 Jenkins Pipeline 的基础语法和核心概念,帮助你掌握声明式 Pipeline 的编写。
Pipeline 简介
Pipeline 是 Jenkins 的核心功能,允许你使用代码定义整个 CI/CD 流程。
Pipeline 类型对比
| 特性 | Declarative Pipeline | Scripted Pipeline |
|---|---|---|
| 语法 | 结构化、简单 | 灵活、强大 |
| 学习曲线 | 平缓 | 较陡 |
| 错误检查 | 自动验证 | 运行时检查 |
| 适用场景 | 标准 CI/CD 流程 | 复杂逻辑处理 |
| 代码复用 | 共享库 | 共享库 |
为什么选择 Declarative Pipeline?
- 易于理解和维护:结构清晰,语法简洁
- 内置验证:提交前可检查语法错误
- Blue Ocean 支持:现代化可视化界面
- 最佳实践内置:默认启用推荐配置
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 基础后,你可以: