Jenkins Pipeline
Jenkins Pipeline 是一套插件,支持在 Jenkins 中实现和集成持续交付流水线。Pipeline 将原本独立运行的多个任务连接起来,实现复杂的构建、测试和部署流程的自动化。
什么是 Pipeline?
Pipeline 是 Jenkins 2.0 的核心特性,它使用代码(Groovy 语法)来定义整个构建流程:
- 代码化:Pipeline 以代码形式存储,可以版本控制
- 可持久化:Pipeline 可以暂停等待人工输入后继续执行
- 可视化:通过 Blue Ocean 插件提供可视化界面
- 可扩展:支持丰富的插件生态系统
Pipeline 的两种语法
1. 声明式 Pipeline(推荐)
声明式 Pipeline 使用结构化方式定义流水线,更易读、易维护。
pipeline {
agent any // 在任何可用节点上运行
environment {
APP_NAME = 'my-application'
VERSION = '1.0.0'
}
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/example/repo.git'
}
}
stage('Build') {
steps {
sh 'mvn clean package'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh 'scp target/*.jar user@server:/opt/app/'
}
}
}
post {
success {
echo '构建成功!'
}
failure {
echo '构建失败!'
mail to: '[email protected]',
subject: "构建失败: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "请检查: ${env.BUILD_URL}"
}
}
}
2. 脚本式 Pipeline
脚本式 Pipeline 提供更灵活的 Groovy 语法,适合复杂逻辑。
node {
try {
stage('Checkout') {
checkout scm
}
stage('Build') {
sh 'mvn clean package'
}
stage('Test') {
sh 'mvn test'
}
if (env.BRANCH_NAME == 'main') {
stage('Deploy') {
sh 'scp target/*.jar user@server:/opt/app/'
}
}
currentBuild.result = 'SUCCESS'
} catch (Exception e) {
currentBuild.result = 'FAILURE'
throw e
} finally {
junit 'target/surefire-reports/*.xml'
}
}
Pipeline 核心概念
Agent
Agent 指定 Pipeline 或 Stage 在哪个节点上运行。
pipeline {
// 在任何可用节点上运行
agent any
// 指定标签
agent { label 'linux && java11' }
// 使用 Docker 容器
agent {
docker {
image 'maven:3.8.6-openjdk-11'
args '-v /root/.m2:/root/.m2'
}
}
// Kubernetes Pod
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.8.6-openjdk-11
command: ['cat']
tty: true
'''
}
}
}
Stages 和 Stage
Stages 包含一个或多个 Stage,每个 Stage 代表一个阶段。
pipeline {
agent any
stages {
// 并行执行多个 Stage
stage('Parallel Stage') {
parallel {
stage('Unit Tests') {
steps {
sh 'mvn test -Dtest=UnitTests'
}
}
stage('Integration Tests') {
steps {
sh 'mvn test -Dtest=IntegrationTests'
}
}
stage('Code Quality') {
steps {
sh 'mvn sonar:sonar'
}
}
}
}
// 顺序执行
stage('Build') {
steps {
sh 'mvn clean package'
}
}
}
}
Steps
Steps 定义在每个 Stage 中执行的具体操作。
stage('Example') {
steps {
// 执行 Shell 命令
sh 'echo "Hello World"'
sh '''
echo "多行命令"
ls -la
pwd
'''
// 执行 Windows 批处理
bat 'echo Hello'
// 执行 PowerShell
powershell 'Write-Output "Hello"'
// 打印信息
echo "当前构建号: ${env.BUILD_NUMBER}"
// 设置环境变量
script {
env.MY_VAR = 'some value'
}
// 使用工具
withMaven(maven: 'Maven-3.8') {
sh 'mvn clean install'
}
// 使用凭证
withCredentials([usernamePassword(
credentialsId: 'my-credentials',
usernameVariable: 'USER',
passwordVariable: 'PASS'
)]) {
sh 'echo $USER'
}
}
}
Environment
定义环境变量,可在整个 Pipeline 中使用。
pipeline {
agent any
environment {
// 普通变量
APP_NAME = 'myapp'
VERSION = '1.0.0'
// 从凭证获取
API_KEY = credentials('api-key-id')
// 动态设置
BUILD_DATE = sh(script: 'date +%Y%m%d', returnStdout: true).trim()
}
stages {
stage('Build') {
environment {
// Stage 级别的环境变量
STAGE_VAR = 'stage value'
}
steps {
echo "Building ${env.APP_NAME} v${env.VERSION}"
echo "Build date: ${env.BUILD_DATE}"
}
}
}
}
Post
定义 Pipeline 或 Stage 运行后的操作。
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'mvn clean package'
}
}
}
post {
// 总是执行
always {
cleanWs() // 清理工作空间
}
// 成功时执行
success {
echo '构建成功!'
slackSend color: 'good', message: '构建成功'
}
// 失败时执行
failure {
echo '构建失败!'
mail to: '[email protected]',
subject: "构建失败: ${env.JOB_NAME}",
body: "查看详情: ${env.BUILD_URL}"
}
// 不稳定时执行(如测试失败)
unstable {
echo '构建不稳定'
}
// 状态改变时执行
changed {
echo '构建状态发生变化'
}
// 成功或不稳定时执行
fixed {
echo '之前失败,现在成功了'
}
// 失败或不稳定时执行
regression {
echo '之前成功,现在失败了'
}
}
}
条件执行
When 指令
控制 Stage 的执行条件。
pipeline {
agent any
stages {
stage('Deploy to Dev') {
when {
branch 'develop'
}
steps {
sh 'deploy-to-dev.sh'
}
}
stage('Deploy to Prod') {
when {
allOf {
branch 'main'
environment name: 'DEPLOY_ENV', value: 'production'
}
}
steps {
sh 'deploy-to-prod.sh'
}
}
stage('Release') {
when {
anyOf {
branch 'main'
tag 'release-*'
}
}
steps {
sh 'create-release.sh'
}
}
stage('Manual Approval') {
when {
expression {
params.DEPLOY_PROD == true
}
}
steps {
input message: '确认部署到生产环境?', ok: '部署'
sh 'deploy.sh'
}
}
}
}
参数化构建
pipeline {
agent any
parameters {
string(
name: 'BRANCH',
defaultValue: 'main',
description: '要构建的分支'
)
choice(
name: 'ENVIRONMENT',
choices: ['dev', 'staging', 'prod'],
description: '部署环境'
)
booleanParam(
name: 'RUN_TESTS',
defaultValue: true,
description: '是否运行测试'
)
password(
name: 'SECRET_KEY',
description: '密钥'
)
text(
name: 'DESCRIPTION',
defaultValue: '',
description: '构建描述'
)
}
stages {
stage('Build') {
steps {
echo "Building branch: ${params.BRANCH}"
echo "Environment: ${params.ENVIRONMENT}"
script {
if (params.RUN_TESTS) {
sh 'mvn test'
}
}
}
}
}
}
常用插件集成
Git 集成
stage('Checkout') {
steps {
git branch: 'main',
url: 'https://github.com/example/repo.git',
credentialsId: 'github-credentials'
}
}
Docker 集成
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'registry.example.com'
IMAGE_NAME = 'myapp'
}
stages {
stage('Build Docker Image') {
steps {
script {
docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.BUILD_NUMBER}")
}
}
}
stage('Push Docker Image') {
steps {
script {
docker.withRegistry("https://${DOCKER_REGISTRY}", 'registry-credentials') {
docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.BUILD_NUMBER}").push()
docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.BUILD_NUMBER}").push('latest')
}
}
}
}
}
}
Kubernetes 集成
stage('Deploy to Kubernetes') {
steps {
withKubeConfig([credentialsId: 'kube-config']) {
sh '''
kubectl set image deployment/myapp \
myapp=registry.example.com/myapp:${BUILD_NUMBER}
kubectl rollout status deployment/myapp
'''
}
}
}
SonarQube 集成
stage('Code Quality') {
steps {
withSonarQubeEnv('SonarQube') {
sh 'mvn sonar:sonar'
}
}
}
stage('Quality Gate') {
steps {
timeout(time: 5, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
共享库(Shared Library)
将常用代码封装到共享库中,供多个 Pipeline 使用。
创建共享库
目录结构:
(shared-library)
├── vars/
│ ├── buildMaven.groovy
│ ├── deployKubernetes.groovy
│ └── sendNotification.groovy
├── src/
│ └── org/example/
│ └── Utils.groovy
└── resources/
└── scripts/
└── deploy.sh
定义步骤
// vars/buildMaven.groovy
def call(Map config = [:]) {
def goals = config.goals ?: 'clean install'
def options = config.options ?: ''
sh "mvn ${goals} ${options}"
}
使用共享库
@Library('my-shared-library') _
pipeline {
agent any
stages {
stage('Build') {
steps {
buildMaven(
goals: 'clean package',
options: '-DskipTests'
)
}
}
stage('Deploy') {
steps {
deployKubernetes(
namespace: 'production',
deployment: 'myapp'
)
}
}
}
post {
always {
sendNotification()
}
}
}
最佳实践
1. 使用 Jenkinsfile
将 Pipeline 代码存储在项目根目录的 Jenkinsfile 中:
// Jenkinsfile
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'make build'
}
}
}
}
2. 错误处理
stage('Deploy') {
steps {
script {
try {
sh 'deploy.sh'
} catch (Exception e) {
echo "部署失败: ${e.message}"
currentBuild.result = 'UNSTABLE'
}
}
}
}
3. 超时设置
stage('Long Running Task') {
options {
timeout(time: 10, unit: 'MINUTES')
}
steps {
sh 'long-running-script.sh'
}
}
4. 重试机制
stage('Flaky Test') {
steps {
retry(3) {
sh 'flaky-test.sh'
}
}
}
小结
- 声明式 Pipeline:结构化、易读、适合大多数场景
- 脚本式 Pipeline:灵活、适合复杂逻辑
- 核心概念:Agent、Stages、Steps、Environment、Post
- 条件执行:使用 When 控制 Stage 执行
- 参数化构建:支持多种参数类型
- 共享库:封装常用代码,提高复用性
练习
- 创建一个简单的 Jenkinsfile,实现代码检出、构建、测试流程
- 添加并行 Stage,同时运行单元测试和集成测试
- 配置条件部署,只在 main 分支部署到生产环境
- 创建一个共享库,封装常用的构建步骤