第四章:Pipeline 基础

理解 Jenkins Pipeline 的核心概念、类型和使用场景。

最后更新: 2024-01-15
页面目录

Jenkins Pipeline 基础

Pipeline(流水线)是 Jenkins 2.0 引入的核心功能,通过代码定义构建流程,实现「Pipeline as Code」理念。

为什么需要 Pipeline?

传统 Freestyle 的局限性

  • 配置分散在 Web 界面,难以追踪
  • 不便于版本控制和代码审查
  • 难以复用和模块化
  • 复杂的条件逻辑难以实现

Pipeline 的优势

┌─────────────────────────────────────────────────────────────────┐
│                      Pipeline 优势                              │
├─────────────────────────────────────────────────────────────────┤
│  ✅ 版本控制    Pipeline 配置存储在代码仓库中                       │
│  ✅ 代码审查    通过 PR/MR 审查构建流程                            │
│  ✅ 持久化      构建过程状态自动保存                                │
│  ✅ 可复用      提取公共逻辑为共享库                                │
│  ✅ 可见性      完整的阶段视图和日志                                 │
│  ✅ 增量执行    支持从失败点恢复构建                                │
└─────────────────────────────────────────────────────────────────┘

Pipeline 类型

1. Declarative Pipeline(声明式)

声明式 Pipeline 是推荐的编写方式,语法更简洁、结构更清晰:

// 声明式 Pipeline 示例
pipeline {
    agent any
    
    parameters {
        string(name: 'VERSION', defaultValue: '1.0.0')
    }
    
    environment {
        APP_NAME = 'my-app'
    }
    
    stages {
        stage('Build') {
            steps {
                echo 'Building...'
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
    }
}

2. Scripted Pipeline(脚本式)

脚本式 Pipeline 使用 Groovy 语法,更灵活但结构较复杂:

// 脚本式 Pipeline 示例
node('docker') {
    stage('Checkout') {
        checkout scm
    }
    
    stage('Build') {
        sh 'mvn clean package'
    }
    
    stage('Test') {
        try {
            sh 'mvn test'
        } catch (Exception e) {
            currentBuild.result = 'UNSTABLE'
        }
    }
    
    stage('Deploy') {
        if (env.BRANCH_NAME == 'main') {
            sh './deploy.sh'
        }
    }
}

两种语法对比

特性 Declarative Scripted
语法风格 YAML-like Groovy
学习曲线 平缓 较陡
灵活性 固定结构 完全自由
错误恢复 支持 不支持
推荐度 ⭐⭐⭐⭐⭐ ⭐⭐⭐

Pipeline 核心概念

1. Agent

定义 Pipeline 或 stage 在哪里执行:

// 在任意可用节点执行
agent any

// 在指定标签的节点执行
agent { label 'docker' }

// Docker 容器中执行
agent {
    docker {
        image 'maven:3.8-openjdk-11'
        reuseNode true  // 复用主节点工作区
    }
}

// Kubernetes Pod 模板
agent {
    kubernetes {
        label 'pod-template'
        yaml '''
apiVersion: v1
kind: Pod
metadata:
  labels:
    jenkins: agent
spec:
  containers:
  - name: jnlp
    image: jenkins/inbound-agent:latest
  - name: maven
    image: maven:3.8-openjdk-11
    command: sleep
    args: infinity
'''
    }
}

// 不分配节点(用于声明阶段)
agent none

2. Stages 和 Steps

pipeline {
    agent any
    
    stages {
        stage('阶段名称') {
            steps {
                // 具体步骤
                echo 'Hello World'
                sh 'mvn --version'
                bat 'dir'  // Windows
            }
        }
    }
}

3. Post

定义 Pipeline 执行完成后的操作:

post {
    always {
        // 无论构建结果如何都执行
        echo '清理资源'
    }
    
    success {
        // 构建成功时执行
        echo '🎉 构建成功!'
    }
    
    failure {
        // 构建失败时执行
        echo '❌ 构建失败!'
        slackSend message: "构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
    }
    
    unstable {
        // 构建不稳定时执行
        echo '⚠️ 构建不稳定'
    }
    
    aborted {
        // 构建被中止时执行
        echo '⏹️ 构建已中止'
    }
    
    cleanup {
        // 无论如何最后执行(总是最后)
        echo '最终清理'
    }
}

4. Environment

定义环境变量:

pipeline {
    agent any
    
    environment {
        APP_NAME = 'my-service'
        VERSION = '1.2.3'
        JAVA_OPTS = '-Xmx512m'
        CC = 'clang'
    }
    
    stages {
        stage('Build') {
            environment {
                BUILD_MODE = 'release'
            }
            steps {
                echo "构建应用: ${env.APP_NAME}"
                sh 'printenv | grep -E "APP_|BUILD_"'
            }
        }
    }
}

5. Parameters

定义构建参数:

pipeline {
    agent any
    
    parameters {
        string(
            name: 'VERSION',
            defaultValue: '1.0.0',
            description: '版本号'
        )
        
        choice(
            name: 'ENVIRONMENT',
            choices: ['dev', 'staging', 'prod'],
            description: '部署环境'
        )
        
        booleanParam(
            name: 'SKIP_TESTS',
            defaultValue: false,
            description: '跳过测试'
        )
        
        password(
            name: 'API_TOKEN',
            defaultValue: '',
            description: 'API Token'
        )
        
        file(
            name: 'CONFIG_FILE',
            description: '配置文件'
        )
        
        text(
            name: 'BUILD_NOTES',
            defaultValue: '',
            description: '构建备注'
        )
    }
    
    stages {
        stage('Parameters') {
            steps {
                echo "版本: ${params.VERSION}"
                echo "环境: ${params.ENVIRONMENT}"
            }
        }
    }
}

6. When 条件

控制阶段的执行条件:

stages {
    stage('Deploy') {
        when {
            branch 'main'
        }
        steps {
            echo '部署到生产环境'
        }
    }
    
    stage('Deploy Staging') {
        when {
            anyOf {
                branch 'main'
                branch 'release/*'
            }
        }
        steps {
            echo '部署到预发布环境'
        }
    }
    
    stage('Integration Tests') {
        when {
            expression { params.RUN_TESTS == true }
            not { branch 'feature/*' }
        }
        steps {
            echo '运行集成测试'
        }
    }
}

7. Parallel 并行执行

stages {
    stage('Test in Parallel') {
        parallel {
            stage('Unit Tests') {
                steps {
                    echo '运行单元测试'
                    sh 'mvn test -Dtest=*Test'
                }
            }
            
            stage('Integration Tests') {
                steps {
                    echo '运行集成测试'
                    sh 'mvn verify -Dintegration'
                }
            }
            
            stage('Code Analysis') {
                steps {
                    echo '代码分析'
                    sh 'mvn sonar:sonar'
                }
            }
        }
    }
}

8. Matrix 矩阵执行

stages {
    stage('Build Matrix') {
        matrix {
            axes {
                axis {
                    name 'PLATFORM'
                    values 'linux', 'windows', 'macos'
                }
                axis {
                    name 'ARCH'
                    values 'x64', 'arm64'
                }
            }
            
            stages {
                stage('Build') {
                    steps {
                        echo "构建平台: ${PLATFORM}, 架构: ${ARCH}"
                        sh "./build.sh --platform=${PLATFORM} --arch=${ARCH}"
                    }
                }
            }
            
            excludedAgents 'docker'  // 排除特定节点
        }
    }
}

完整的 Pipeline 示例

// Jenkinsfile - 完整的声明式 Pipeline
@Library('shared-library') _

pipeline {
    agent {
        kubernetes {
            defaultContainer 'jnlp'
            yaml '''
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: maven
    image: maven:3.8-eclipse-temurin-11
    command: sleep
    args: infinity
  - name: docker
    image: docker:20.10-dind
    securityContext:
      privileged: true
    volumeMounts:
    - name: docker-graph-storage
      mountPath: /var/lib/docker
  volumes:
  - name: docker-graph-storage
    emptyDir: {}
'''
        }
    }
    
    options {
        timestamps()
        timeout(time: 1, unit: 'HOURS')
        buildDiscarder(logRotator(numToKeepStr: '30'))
        disableConcurrentBuilds()
    }
    
    parameters {
        choice(name: 'BRANCH', choices: ['main', 'develop', 'release/*'], description: '分支')
        booleanParam(name: 'DEPLOY', defaultValue: false, description: '部署')
    }
    
    environment {
        REGISTRY = 'registry.example.com'
        IMAGE_NAME = 'myapp'
        CREDENTIALS = credentials('docker-registry')
    }
    
    stages {
        stage('Checkout') {
            steps {
                script {
                    env.GIT_COMMIT_SHORT = sh(
                        script: 'git rev-parse --short HEAD',
                        returnStdout: true
                    ).trim()
                }
                checkout scm
            }
        }
        
        stage('Build') {
            steps {
                container('maven') {
                    sh '''
                        mvn clean package -DskipTests
                        echo "Build completed: ${GIT_COMMIT_SHORT}"
                    '''
                }
            }
            post {
                success {
                    archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
                }
            }
        }
        
        stage('Test') {
            steps {
                container('maven') {
                    sh 'mvn test'
                }
            }
            post {
                always {
                    junit 'target/surefire-reports/*.xml'
                    jacoco execPattern: 'target/jacoco.exec'
                }
            }
        }
        
        stage('Security Scan') {
            steps {
                container('maven') {
                    sh 'mvn dependency:tree -DoutputFile=dependencies.txt'
                }
                echo 'OWASP dependency check...'
            }
        }
        
        stage('Docker Build') {
            when {
                anyOf { branch 'main'; branch 'release/*' }
            }
            steps {
                container('docker') {
                    sh '''
                        echo $CREDENTIALS_PSW | docker login -u $CREDENTIALS_USR --password-stdin ${REGISTRY}
                        docker build -t ${REGISTRY}/${IMAGE_NAME}:${GIT_COMMIT_SHORT} .
                        docker push ${REGISTRY}/${IMAGE_NAME}:${GIT_COMMIT_SHORT}
                        docker tag ${REGISTRY}/${IMAGE_NAME}:${GIT_COMMIT_SHORT} ${REGISTRY}/${IMAGE_NAME}:latest
                        docker push ${REGISTRY}/${IMAGE_NAME}:latest
                    '''
                }
            }
        }
        
        stage('Deploy') {
            when {
                allOf {
                    branch 'main'
                    expression { params.DEPLOY == true }
                }
            }
            steps {
                sh '''
                    kubectl set image deployment/myapp \
                    app=${REGISTRY}/${IMAGE_NAME}:${GIT_COMMIT_SHORT}
                '''
            }
        }
    }
    
    post {
        success {
            echo 'Pipeline 执行成功!'
        }
        failure {
            echo 'Pipeline 执行失败!'
        }
        always {
            cleanWs()
        }
    }
}

下一步

现在您已经掌握了 Pipeline 的核心概念。接下来让我们深入学习 Pipeline 的详细语法。

👉 Pipeline 语法详解


📖 参考文档