阅读信息

  • 预计阅读时间:15-20分钟
  • 主要内容
    • Jenkins Pipeline 核心概念与基本架构
    • 常用指令与全局变量详解
    • 生产环境 K8s 流水线完整示例
    • 现代 CI/CD 环境集成方案
    • 最佳实践与性能优化建议

关注公众号 键盘下的小宇宙 并回复关键字 “视频资料”,即可获取我们整理的 Kubernetes、Docker 容器、Python 编程、Linux 运维等教学视频合集(总计 548GB)。本资源仅面向学习交流使用,请遵守版权和使用规范,严禁商用、转售或违规传播。


Jenkins Pipeline 完整指南

1. 概述

本文提供了 Jenkins Pipeline 的全面指南,涵盖核心概念、常用指令、最佳实践以及现代 CI/CD 环境的集成方案。

2. 核心概念

2.1 Agent(代理)

Agent 指令指定 Jenkins Pipeline 在哪里执行,支持以下参数:

  • any:在任何可用节点上执行 pipeline
  • none:未指定 agent 时的默认值
  • label:在指定标签的节点上运行 pipeline
agent {
    node {
        label "master" // 指定运行节点的名称或者标签
        customWorkspace "${workspace}" // 执行运行的工作目录(可选)
    }
}

2.2 Post

Post 代码块定义了流水线运行完成后的操作,其执行取决于流水线的完成状态:

  • always:无论流水线或阶段完成状态如何都运行
  • changed:只有当流水线或阶段完成状态与之前不同时才运行
  • failure:只有当流水线状态为 failure 时才运行
  • success:只有当流水线状态为 success 时才运行
  • aborted:只有当流水线状态为 aborted(手动取消)时才运行
post {
    always {
        script {
            println("总是执行脚本片段")
        }
    }
    success {
        script {
            currentBuild.description += "\n 构建成功"
        }
    }
    failure {
        script {
            currentBuild.description += "\n 构建失败"
        }
    }
    aborted {
        script {
            currentBuild.description += "\n 构建取消"
        }
    }
}

2.3 Stages

Stages 包含一系列一个或多个 stage 指令,用于定义连续交付流程中的各个阶段,如构建、测试和部署。每个 pipeline 必须至少包含一个 stage。

stages {
    // 下载代码
    stage("GetCode") {
        steps {
            timeout(time: 5, unit: "MINUTES") {
                script {
                    println('获取代码')
                }
            }
        }
    }
}

3. 指令与全局变量

3.1 Environment

Environment 指令指定键值对序列,这些键值对将被定义为所有步骤的环境变量,或特定于阶段的步骤,具体取决于 environment 指令在流水线内的位置。

该指令支持一个特殊的 credentials() 方法,用于在 Jenkins 环境中通过标识符访问预定义的凭证:

  • 对于类型为 “Secret Text” 的凭证,credentials() 将确保指定的环境变量包含秘密文本内容
  • 对于类型为 “Standard username and password” 的凭证,指定的环境变量格式为 username:password,并自动定义两个额外的环境变量:MYVARNAME_USRMYVARNAME_PSW
pipeline {
    agent any
    environment {
        CC = 'clang'
    }
    stages {
        stage('Example') {
            environment {
                AN_ACCESS_KEY = credentials('my-prefined-secret-text')
            }
            steps {
                sh 'printenv'
            }
        }
    }
}

3.2 Options

Options 指令允许从流水线内部配置特定于流水线的选项:

  • buildDiscarder:为最近的流水线运行的特定数量保存组件和控制台输出
  • disableConcurrentBuilds:不允许同时执行流水线,可用于防止同时访问共享资源等
  • overrideIndexTriggers:允许覆盖分支索引触发器的默认处理
  • skipDefaultCheckout:在 agent 指令中,跳过从源代码控制中检出代码的默认情况
  • skipStagesAfterUnstable:一旦构建状态变得 UNSTABLE,跳过该阶段
  • checkoutToSubdirectory:在工作空间的子目录中自动执行源代码控制检出
  • timeout:设置流水线运行的超时时间,在此之后 Jenkins 将中止流水线
  • retry:在失败时,重新尝试整个流水线的指定次数
  • timestamps:为所有由流水线生成的控制台输出添加时间戳
pipeline {
    agent any
    options {
        timeout(time: 1, unit: 'HOURS')
    }
    stages {
        stage('Example') {
            steps {
                echo 'hello world'
            }
        }
    }
}

3.3 Parameters

Parameters 指令为流水线运行时添加相关的参数:

parameters {
    string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '部署环境')
}

3.4 Triggers

Triggers 指令定义了流水线的触发方式:

  • cron:计划任务定期执行构建

    triggers { cron('H */4 * * 1-5') }
    
  • pollSCM:与 cron 定义类似,但是由 Jenkins 定期检测源码变化

    triggers { pollSCM('H */4 * * 1-5') }
    
  • upstream:接受逗号分隔的任务字符串和阈值,当字符串中的任何作业以最小阈值结束时,流水线被重新触发

    triggers { upstream(upstreamProjects: 'job1, job2', threshold: hudson.model.Result.SUCCESS) }
    

3.5 Tool

Tool 指令获取通过自动安装或手动放置的工具的环境变量,支持 Maven、JDK 等。工具的名称必须在系统设置 -> 全局工具配置中定义:

pipeline {
    agent any
    tools {
        maven 'mvn391'
    }
    stages {
        stage('Example') {
            steps {
                sh 'mvn --version'
            }
        }
    }
}

3.6 Input

Input 指令允许在执行各个阶段时,由人工确认是否继续进行:

  • message:呈现给用户的提示信息
  • id:可选,默认为 stage 名称
  • ok:默认表单上的 “OK” 文本
  • submitter:可选的,以逗号分隔的用户列表或允许提交的外部组名,默认允许任何用户
  • submitterParameter:环境变量的可选名称,如果存在则用 submitter 名称设置
  • parameters:提示提交者提供的一个可选的参数列表
input id: 'Test', 
      message: '是否要继续', 
      ok: '是,继续吧!', 
      parameters: [choice(choices: ['A', 'B'], name: 'test1')], 
      submitter: 'wangwl'

3.7 When

When 指令允许流水线根据给定的条件决定是否应该执行阶段。When 指令至少包含一个条件,如果包含多个条件,所有子条件必须返回 True,阶段才能执行。

内置条件
  • branch:当正在构建的分支与给定模式匹配时,执行这个阶段(仅适用于多分支流水线)

    when { branch 'master' }
    
  • environment:当指定的环境变量是给定的值时,执行这个阶段

    when { environment name: 'DEPLOY_TO', value: 'production' }
    
  • expression:当指定的 Groovy 表达式评估为 true 时,执行这个阶段

    when { expression { return params.DEBUG_BUILD } }
    
  • not:当嵌套条件为 false 时,执行这个阶段

    when { not { branch 'master' } }
    
  • allOf:当所有的嵌套条件都为 true 时,执行这个阶段

    when { 
        allOf { 
            branch 'master'; 
            environment name: 'DEPLOY_TO', value: 'production' 
        } 
    }
    
  • anyOf:当至少有一个嵌套条件为 true 时,执行这个阶段

    when { 
        anyOf { 
            branch 'master'; 
            branch 'staging' 
        } 
    }
    

3.8 Parallel

Parallel 指令允许在一个阶段内并行执行多个嵌套阶段。注意:

  • 一个阶段必须只有一个 steps 或 parallel 块
  • 嵌套阶段本身不能包含进一步的 parallel 阶段
  • Parallel 的阶段不能包含 agent 或 tools 指令,因为它们没有相关的 steps
  • 通过添加 failFast true 到包含 parallel 的 stage 中,当其中一个进程失败时,可以强制所有的 parallel 阶段都被终止
stage('Parallel Stage') {
    when {
        branch 'master'
    }
    failFast true // 第一个进程失败时,后面的也会失败
    parallel {
        stage('Branch A') {
            agent {
                label "for-branch-a"
            }
            steps {
                echo "On Branch A"
            }
        }
        stage('Branch B') {
            agent {
                label "for-branch-b"
            }
            steps {
                echo "On Branch B"
            }
        }
    }
}

3.9 Script 步骤

Script 步骤允许在声明式流水线中执行脚本化流水线代码。对于大多数用例,声明式流水线中的 “脚本” 步骤是不必要的,但它可以提供一个有用的 “逃生出口”。非平凡规模和/或复杂性的 script 块应该被转移到共享库。

pipeline {
    agent any
    stages {
        stage("Example") {
            steps {
                echo 'Hello World'
                script {
                    def browsers = ['chrome', 'firefox']
                    for (int i = 0; i < browsers.size(); ++i) {
                        echo "Testing the ${browsers[i]} browser"
                    }
                }
            }
        }
    }
}

4. 完整示例

4.2 生产环境 K8s 流水线示例

生产环境中与 Kubernetes 集成的 CI/CD 流水线通常包含以下核心步骤,以下是完整的示例代码。
根据自己环境情况修改即可,构建的pod使用到镜像可能需要根据自己环境自定义。

#!groovy

pipeline {
    // 使用 Kubernetes 作为执行环境,创建包含多个容器的 Pod
    agent {
        kubernetes {
            yaml """
apiVersion: v1
kind: Pod
spec:
  containers:
  # 构建容器,用于执行 Maven 构建
  - name: build
    image: maven:3.8.4-jdk-11
    command: ['cat']  # 保持容器运行
    tty: true  # 启用终端
  # Docker 容器,用于构建和推送镜像
  - name: docker
    image: docker:20.10.16
    command: ['cat']
    tty: true
    volumeMounts:
    - name: docker-sock  # 挂载 Docker 套接字,实现与主机 Docker 通信
      mountPath: /var/run/docker.sock
  # kubectl 容器,用于执行 Kubernetes 命令
  - name: kubectl
    image: bitnami/kubectl:1.23.5
    command: ['cat']
    tty: true
  # 卷定义
  volumes:
  - name: docker-sock
    hostPath:
      path: /var/run/docker.sock  # 主机上的 Docker 套接字路径
"""
        }
    }

    // 流水线选项配置
    options {
        timestamps()  // 为日志添加时间戳
        disableConcurrentBuilds()  // 禁止并行构建,避免冲突
        timeout(time: 2, unit: 'HOURS')  // 流水线超时时间
        buildDiscarder(logRotator(numToKeepStr: '10'))  // 保留最近10次构建日志
    }

    // 环境变量定义
    environment {
        // 镜像仓库地址
        REGISTRY = 'my-registry.example.com'
        // 应用名称
        APP_NAME = 'my-application'
        // Kubernetes 命名空间
        K8S_NAMESPACE = 'production'
        // 凭证 ID,用于访问镜像仓库
        DOCKER_CREDENTIALS_ID = 'docker-registry-credentials'
        // 凭证 ID,用于访问 Kubernetes 集群
        K8S_CREDENTIALS_ID = 'kubernetes-credentials'
    }

    // 流水线阶段定义
    stages {
        // 1. 代码获取阶段
        stage("1. Get Code") {
            steps {
                echo "正在获取代码..."
                // 从与流水线关联的代码仓库检出代码
                checkout scm
                // 或指定具体仓库:
                // git url: 'https://github.com/example/app.git', branch: 'main'
            }
        }

        // 2. 依赖安装与构建阶段
        stage("2. Build") {
            steps {
                echo "正在构建应用..."
                // 在 build 容器中执行 Maven 构建
                container('build') {
                    // 清理并打包应用,跳过测试(测试在专门的测试阶段执行)
                    sh 'mvn clean package -DskipTests'
                }
            }
        }

        // 3. 自动化测试阶段
        stage("3. Test") {
            steps {
                echo "正在执行测试..."
                // 在 build 容器中执行 Maven 测试
                container('build') {
                    sh 'mvn test'
                    // 收集测试报告,便于在 Jenkins 中查看
                    junit 'target/surefire-reports/**/*.xml'
                }
            }
        }

        // 4. 代码扫描阶段
        stage("4. Code Scan") {
            steps {
                echo "正在执行代码扫描..."
                // 在 build 容器中执行 SonarQube 扫描
                container('build') {
                    sh '''
                    sonar-scanner \
                    -Dsonar.projectKey=${APP_NAME} \
                    -Dsonar.sources=. \
                    -Dsonar.java.binaries=target/classes \
                    -Dsonar.junit.reportsPath=target/surefire-reports \
                    -Dsonar.qualitygate.wait=true  // 等待质量 gate 结果
                    '''
                }
            }
        }

        // 5. 镜像构建阶段
        stage("5. Image Build") {
            steps {
                echo "正在构建 Docker 镜像..."
                // 在 docker 容器中执行镜像构建
                container('docker') {
                    script {
                        // 生成镜像标签,使用构建号和 Git 提交哈希
                        def gitCommit = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
                        def imageTag = "${BUILD_NUMBER}-${gitCommit}"
                        
                        // 构建镜像
                        sh "docker build -t ${REGISTRY}/${APP_NAME}:${imageTag} ."
                        
                        // 保存镜像标签到环境变量,供后续阶段使用
                        env.IMAGE_TAG = imageTag
                    }
                }
            }
        }

        // 6. 镜像扫描与推送阶段
        stage("6. Image Push") {
            steps {
                echo "正在扫描并推送 Docker 镜像..."
                // 在 docker 容器中执行操作
                container('docker') {
                    script {
                        // 使用之前生成的镜像标签
                        def imageTag = env.IMAGE_TAG
                        
                        // 使用 Trivy 扫描镜像漏洞
                        sh "trivy image ${REGISTRY}/${APP_NAME}:${imageTag}"
                        
                        // 使用凭证登录镜像仓库
                        withCredentials([usernamePassword(credentialsId: env.DOCKER_CREDENTIALS_ID, usernameVariable: 'USER', passwordVariable: 'PASS')]) {
                            sh "echo $PASS | docker login ${REGISTRY} -u $USER --password-stdin"
                            // 推送镜像到仓库
                            sh "docker push ${REGISTRY}/${APP_NAME}:${imageTag}"
                        }
                    }
                }
            }
        }

        // 7. Kubernetes 部署阶段
        stage("7. K8s Deploy") {
            steps {
                echo "正在部署到 Kubernetes 集群..."
                // 在 kubectl 容器中执行部署操作
                container('kubectl') {
                    script {
                        // 使用之前生成的镜像标签
                        def imageTag = env.IMAGE_TAG
                        
                        // 更新 Kubernetes Deployment 中的镜像版本
                        sh "kubectl set image deployment/${APP_NAME} ${APP_NAME}=${REGISTRY}/${APP_NAME}:${imageTag} -n ${K8S_NAMESPACE}"
                        
                        // 验证部署状态,等待部署完成
                        sh "kubectl rollout status deployment/${APP_NAME} -n ${K8S_NAMESPACE} --timeout=120s"
                    }
                }
            }
        }

        // 8. 部署验证阶段
        stage("8. Verification") {
            steps {
                echo "正在验证部署结果..."
                // 在 kubectl 容器中执行验证操作
                container('kubectl') {
                    // 查看 Pod 状态
                    sh "kubectl get pods -n ${K8S_NAMESPACE} -l app=${APP_NAME}"
                    
                    // 查看服务状态
                    sh "kubectl get svc -n ${K8S_NAMESPACE} ${APP_NAME}"
                    
                    // 执行健康检查(假设应用提供 /health 端点)
                    sh "kubectl port-forward svc/${APP_NAME} 8080:80 -n ${K8S_NAMESPACE} & sleep 5"
                    sh "curl -f http://localhost:8080/health || (pkill -f 'kubectl port-forward' && exit 1)"
                    sh "pkill -f 'kubectl port-forward'"
                }
            }
        }
    }

    // 流水线完成后的操作
    post {
        // 成功时的操作
        success {
            echo "流水线执行成功!"
            // 发送 Slack 通知
            slackSend 
                channel: '#deployments', 
                color: 'good', 
                message: "应用 ${APP_NAME} 部署成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}\n镜像标签: ${env.IMAGE_TAG}"
        }
        
        // 失败时的操作
        failure {
            echo "流水线执行失败!"
            // 发送 Slack 通知
            slackSend 
                channel: '#deployments', 
                color: 'danger', 
                message: "应用 ${APP_NAME} 部署失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
            
            // 在 kubectl 容器中执行回滚操作
            container('kubectl') {
                echo "正在执行回滚操作..."
                sh "kubectl rollout undo deployment/${APP_NAME} -n ${K8S_NAMESPACE}"
            }
        }
        
        // 无论成功失败都执行的操作
        always {
            echo "流水线执行完成!"
            // 清理工作空间
            cleanWs()
        }
    }
}

4.2.1 生产环境流水线步骤说明

生产环境中的 Kubernetes 流水线通常包含以下关键步骤:

  1. 代码获取 (Get Code)

    • 从版本控制系统拉取最新代码
    • 支持指定分支、标签或提交哈希
  2. 依赖安装与构建 (Build)

    • 安装项目依赖
    • 构建应用,生成可部署的制品
  3. 自动化测试 (Test)

    • 执行单元测试、集成测试等
    • 收集测试报告,便于问题分析
  4. 代码扫描 (Code Scan)

    • 检测代码中的安全漏洞
    • 分析代码质量,确保符合最佳实践
  5. 镜像构建 (Image Build)

    • 将构建产物打包为 Docker 镜像
    • 为镜像添加版本标签(如构建号、Git 提交哈希)
  6. 镜像扫描与推送 (Image Push)

    • 扫描 Docker 镜像中的安全漏洞
    • 将镜像推送到镜像仓库(如 Docker Hub、Harbor)
  7. Kubernetes 部署 (K8s Deploy)

    • 更新 Kubernetes 中的应用版本
    • 支持滚动更新、金丝雀发布等策略
  8. 部署验证 (Verification)

    • 验证部署是否成功
    • 检查应用健康状态和可用性
  9. 通知与回滚 (Post Actions)

    • 发送部署结果通知(如 Slack、邮件)
    • 在失败时自动回滚到上一版本

4.2.2 K8s 相关最佳实践

  • 镜像仓库集成:使用私有的镜像仓库,并配置 Kubernetes 的 imagePullSecrets 以拉取私有镜像
  • 资源配置:明确指定 CPU/内存资源限制,避免资源竞争
  • 健康检查:为 Pod 配置 livenessProbereadinessProbestartupProbe
  • 滚动更新策略:设置合理的 rollingUpdate 参数,确保部署过程中服务不中断
  • 命名空间隔离:将不同环境的应用部署到独立命名空间
  • Secrets 管理:使用 K8s Secrets 或外部密钥管理服务存储敏感信息
  • 监控集成:集成 Prometheus、Grafana 等监控工具,实时监控应用状态
  • 日志管理:配置集中式日志收集,便于问题排查

5. 现代 CI/CD 环境集成

5.1 容器化集成

5.1.1 使用 Docker 执行环境
pipeline {
    agent {
        docker {
            image 'node:14-alpine'
            args '-p 3000:3000'
        }
    }
    stages {
        stage('构建') {
            steps {
                sh 'npm install'
                sh 'npm run build'
            }
        }
        stage('测试') {
            steps {
                sh 'npm test'
            }
        }
    }
}
5.1.2 Kubernetes 集成
pipeline {
    agent {
        kubernetes {
            yaml """
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: build
    image: maven:3.8.4-jdk-11
    command: ['cat']
    tty: true
  - name: test
    image: node:14-alpine
    command: ['cat']
    tty: true
"""
        }
    }
    stages {
        stage('构建') {
            steps {
                container('build') {
                    sh 'mvn clean package'
                }
            }
        }
        stage('测试') {
            steps {
                container('test') {
                    sh 'npm install && npm test'
                }
            }
        }
    }
}

5.2 云服务集成

5.2.1 AWS 集成
pipeline {
    agent any
    environment {
        AWS_REGION = 'us-east-1'
        AWS_ACCOUNT_ID = credentials('aws-account-id')
        AWS_ACCESS_KEY_ID = credentials('aws-access-key')
        AWS_SECRET_ACCESS_KEY = credentials('aws-secret-key')
    }
    stages {
        stage('部署到 S3') {
            steps {
                sh 'aws s3 sync ./build s3://my-bucket'
            }
        }
        stage('部署到 ECS') {
            steps {
                sh 'aws ecs update-service --cluster my-cluster --service my-service --force-new-deployment'
            }
        }
    }
}

5.3 监控与通知

5.3.1 集成 Slack 通知
pipeline {
    agent any
    stages {
        // 构建阶段...
    }
    post {
        success {
            slackSend 
                channel: '#jenkins-builds', 
                color: 'good', 
                message: "构建成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
        }
        failure {
            slackSend 
                channel: '#jenkins-builds', 
                color: 'danger', 
                message: "构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
        }
    }
}
5.3.2 集成 Prometheus 监控
pipeline {
    agent any
    stages {
        // 构建阶段...
    }
    post {
        always {
            script {
                // 推送构建指标到 Prometheus
                sh '''
                curl -X POST "http://prometheus-pushgateway:9091/metrics/job/jenkins/builds"
                -d "jenkins_build_duration_seconds $BUILD_DURATION"
                -d "jenkins_build_result{result=\"$BUILD_RESULT\"} 1"
                '''
            }
        }
    }
}

6. 最佳实践

6.1 代码组织

  • 使用共享库:将重复的逻辑提取到 Jenkins 共享库中,提高代码复用性
  • 模块化:将复杂的流水线分解为多个小的、可管理的阶段
  • 版本控制:将 Jenkinsfile 存储在源代码仓库中,与应用代码一起版本控制

6.2 安全性

  • 使用凭证绑定:避免在代码中硬编码敏感信息
  • 最小权限原则:为 Jenkins 代理和流水线设置最小必要权限
  • 定期更新:及时更新 Jenkins 和插件,修复安全漏洞

6.3 性能优化

  • 并行执行:使用 parallel 指令并行执行独立任务
  • 缓存依赖:缓存构建依赖,减少构建时间
  • 合理设置超时:为各个阶段设置合理的超时时间,避免无限等待

6.4 可维护性

  • 添加注释:为复杂的流水线逻辑添加注释,提高可维护性
  • 使用参数化构建:通过参数化构建,使流水线更加灵活
  • 添加日志:在关键步骤添加日志,便于问题排查

7. 总结

Jenkins Pipeline 是实现 CI/CD 自动化的强大工具,通过代码定义构建、测试和部署流程,提高了流程的可重复性和可维护性。本文涵盖了 Jenkins Pipeline 的核心概念、常用指令、完整示例以及现代 CI/CD 环境的集成方案,希望能帮助您构建更加高效、可靠的 CI/CD 流水线。


关注微信公众号 Linux容器运维 并回复关键字 “视频资料”,即可获取我们整理的 Kubernetes、Docker 容器、Python 编程、Linux 运维等教学视频合集(总计 548GB)。本资源仅面向学习交流使用,请遵守版权和使用规范,严禁商用、转售或违规传播。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐