Jenkins ShareLib

Jenkins Shared Library 是一种复用流水线逻辑的机制,允许在多个 Pipeline 中共享常用的 Step、函数和变量,提高代码复用性和一致性。

目录结构


@libs
├── vars/
│   ├── buildApp.groovy          # 全局变量(可直接调用 buildApp())
│   ├── buildApp.txt             # 文档(可选)
│   ├── deployToK8s.groovy
│   ├── sendNotification.groovy
│   └── notifySlack.groovy
├── src/
│   └── com/
│       └── example/
│           ├── ServiceUtils.groovy    # 类(package com.example)
│           ├── DockerUtils.groovy
│           └── K8sUtils.groovy
└── resources/
    └── templates/
        └── email-template.html

全局变量(Vars)

基础全局变量


// vars/buildImage.groovy
def call(String imageName, String tag, String registry = 'registry.example.com') {
    pipeline {
        agent any
    }

    steps {
        script {
            def fullImage = "${registry}/${imageName}:${tag}"
            echo "Building image: ${fullImage}"

            sh '''
                docker build -t ${fullImage} .
                docker push ${fullImage}
            '''

            return fullImage
        }
    }
}

带参数的全流程构建


// vars/buildAndDeploy.groovy
def call(Map config) {
    def imageName = config.imageName ?: 'myapp'
    def registry = config.registry ?: 'registry.example.com'
    def branch = config.branch ?: env.BRANCH_NAME
    def environment = config.environment ?: 'staging'
    def credentialsId = config.credentialsId ?: 'docker-hub'

    pipeline {
        agent { label 'docker' }

        options {
            timeout(time: 30, unit: 'MINUTES')
            buildDiscarder(logRotator(numToKeepStr: '10'))
        }

        stages {
            stage('Checkout') {
                steps {
                    checkout scm
                }
            }

            stage('Build') {
                steps {
                    script {
                        def image = buildImage(imageName, branch, registry)
                        env.IMAGE_FULL = image
                    }
                }
            }

            stage('Test') {
                steps {
                    sh 'make test'
                }
                post {
                    always {
                        junit '**/test-results/*.xml'
                    }
                }
            }

            stage('Deploy') {
                when {
                    branch 'main'
                }
                steps {
                    script {
                        deployToK8s(image: env.IMAGE_FULL, environment: environment)
                    }
                }
            }
        }

        post {
            success {
                notifySlack("✅ Build succeeded: ${env.IMAGE_FULL}")
            }
            failure {
                notifySlack("❌ Build failed: ${env.JOB_NAME} ${env.BUILD_NUMBER}")
            }
        }
    }
}

src 目录下的类

DockerUtils


// src/com/example/DockerUtils.groovy
package com.example

class DockerUtils implements Serializable {

    private static String registry
    private static String credentialsId

    DockerUtils(String registry, String credentialsId) {
        this.registry = registry
        this.credentialsId = credentialsId
    }

    String buildAndPush(String context, String tag) {
        def image = "${registry}/${context}:${tag}"
        sh "docker build -t ${image} ${context}"
        docker.withRegistry("https://${registry}", credentialsId) {
            sh "docker push ${image}"
        }
        return image
    }

    void pull(String image) {
        docker.withRegistry("https://${registry}", credentialsId) {
            sh "docker pull ${image}"
        }
    }

    String generateTag(String branch, String commitSha) {
        def timestamp = new Date().format('yyyyMMddHHmmss', TimeZone.getTimeZone('UTC'))
        if (branch == 'main') {
            return "latest"
        }
        return "${branch}-${commitSha[0..7]}-${timestamp}"
    }
}

K8sUtils


// src/com/example/K8sUtils.groovy
package com.example

import com.cloudbees.groovy.cps.NonCPS

class K8sUtils implements Serializable {

    private String context
    private String namespace

    K8sUtils(String context, String namespace) {
        this.context = context
        this.namespace = namespace
    }

    void applyYaml(String yamlPath) {
        sh "kubectl --context=${context} apply -f ${yamlPath} -n ${namespace}"
    }

    void rolloutStatus(String deployment) {
        sh "kubectl --context=${context} rollout status deployment/${deployment} -n ${namespace}"
    }

    void setImage(String deployment, String container, String image) {
        sh "kubectl --context=${context} set image deployment/${deployment} ${container}=${image} -n ${namespace}"
    }

    void rollback(String deployment) {
        sh "kubectl --context=${context} rollout undo deployment/${deployment} -n ${namespace}"
    }

    Map getPodStatus(String label) {
        def output = sh(
            script: "kubectl --context=${context} get pods -n ${namespace} -l ${label} -o json",
            returnStdout: true
        )
        return new groovy.json.JsonSlurper().parseText(output)
    }
}

在 Pipeline 中使用

Jenkinsfile


@Library('shared-library@main') _

// 使用全局变量
buildImage('myapp', 'v1.0.0')

// 使用类
def dockerUtils = new com.example.DockerUtils('registry.example.com', 'docker-hub')
def image = dockerUtils.buildAndPush('myapp', 'main')

带参数的 Pipeline


@Library('shared-library@main') _

pipeline {
    agent any

    parameters {
        string(name: 'IMAGE_TAG', defaultValue: 'latest', description: 'Docker image tag')
        choice(name: 'ENVIRONMENT', choices: ['dev', 'staging', 'production'], description: 'Deploy environment')
        booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: 'Skip test stage')
    }

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

    stages {
        stage('Build') {
            steps {
                script {
                    def dockerUtils = new com.example.DockerUtils('registry.example.com', 'docker-hub')
                    env.FULL_IMAGE = dockerUtils.buildAndPush('myapp', params.IMAGE_TAG)
                }
            }
        }

        stage('Test') {
            when {
                expression { params.SKIP_TESTS == false }
            }
            steps {
                sh 'make test'
            }
        }

        stage('Deploy') {
            steps {
                script {
                    def k8s = new com.example.K8sUtils('production', params.ENVIRONMENT)
                    k8s.applyYaml('k8s/deployment.yaml')
                    k8s.rolloutStatus('myapp')
                }
            }
        }
    }
}

共享库配置

Global Pipeline Libraries(全局)

Manage Jenkins → Configure System → Global Pipeline Libraries

  • Name: shared-library
  • Default version: main
  • Repository URL: https://github.com/example/jenkins-shared-library
  • Credentials: SSH Key 或 Username/Password

Folder-level Libraries

在文件夹配置中添加共享库,仅对该文件夹内的 Job 可见。

测试共享库

使用 JenkinsPipelineUnit 测试


// test/com/example/DockerUtilsTest.groovy
class DockerUtilsTest extends Specification {

    @Shared
    def jenkinsRule = new JenkinsRule()

    def "test build and push"() {
        setup:
        def pipeline = jenkinsRule.createProject(WorkflowJob)
        pipeline.script = '''
            @Library('shared-library') _
            def dockerUtils = new com.example.DockerUtils('registry.example.com', 'docker-hub')
            def image = dockerUtils.buildAndPush('myapp', 'test')
            echo "Image: ${image}"
        '''

        when:
        def build = pipeline.scheduleBuild2(0).get()

        then:
        build.result == Result.SUCCESS
        build.logFile.text.contains('Image: registry.example.com/myapp:test')
    }
}

文档生成

为全局变量创建 .txt 文件,Jenkins 会自动生成文档:


// vars/buildImage.txt
## buildImage
Builds a Docker image and pushes to registry.

### Parameters
- `imageName` (String): Name of the image to build
- `tag` (String): Image tag
- `registry` (String, optional): Registry URL, defaults to 'registry.example.com'

### Example

buildImage('myapp', 'v1.0.0')



### Notes
- Requires Docker daemon access
- Uses Kaniko for Kubernetes builds

版本管理

使用 Git Tag 管理版本


@Library('shared-library@1.2.0') _

Changelog 策略


# v1.2.0
- Added: `buildImage()` global variable
- Fixed: K8sUtils.rolloutStatus() timeout issue
- Changed: Default registry to registry.example.com

# v1.1.0
- Added: DockerUtils class
- Added: K8sUtils class

最佳实践

  • 版本锁定:生产环境使用固定版本 @Library('shared-library@v1.0.0')
  • 文档完整:为每个全局变量编写 .txt 文档
  • 测试覆盖:使用 JenkinsPipelineUnit 编写单元测试
  • 幂等性:Step 应可重复执行
  • 错误处理:明确异常信息,提供回滚机制
  • 最小权限:共享库不持有敏感信息,通过参数传入

下一步