跳转到主要内容

Jenkins Pipeline as Code:构建现代化的 CI/CD 流水线

博主
2 分钟
358 字
--

虽然 GitLab CI 和 GitHub Actions 势头正劲,但 Jenkins 凭借其强大的插件生态和灵活性,依然是很多中大型企业 CI/CD 的中流砥柱。

传统的 Jenkins 使用方式是在 Web UI 上点击配置(Freestyle Job),这种方式维护难、不可复用、无法版本控制。Pipeline as Code(流水线即代码)是现代 Jenkins 的正确打开方式。

1. 声明式流水线 (Declarative Pipeline)

Jenkins 2.0 引入了 Pipeline,推荐使用声明式语法(Declarative),结构更清晰,错误处理更好。

以下是一个典型的 CI/CD 流水线交互时序图:

sequenceDiagram
    participant Dev as Developer
    participant Git as GitLab/GitHub
    participant Jenkins as Jenkins Master
    participant Slave as K8s Agent (Slave)
    participant Registry as Docker Registry
    participant K8s as Kubernetes Cluster

    Dev->>Git: Push Code (git push)
    Git->>Jenkins: Webhook Trigger
    Jenkins->>K8s: Launch Pod (jnlp-slave)
    K8s-->>Slave: Pod Running
    Jenkins->>Slave: Checkout SCM
    Slave->>Slave: Build & Test (Maven/Gradle)
    Slave->>Slave: Docker Build
    Slave->>Registry: Docker Push
    Slave->>K8s: Helm Upgrade / Kubectl Apply
    K8s-->>Dev: Deployment Success
    Slave->>Jenkins: Task Finished
    Jenkins->>K8s: Terminate Pod

Jenkinsfile 示例:

pipeline {
    agent any
    
    options {
        buildDiscarder(logRotator(numToKeepStr: '10'))
        disableConcurrentBuilds()
        timestamps()
    }

    environment {
        DOCKER_REGISTRY = 'registry.example.com'
        IMAGE_NAME = 'myapp'
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Build & Test') {
            steps {
                sh 'mvn clean package'
                junit 'target/surefire-reports/*.xml'
            }
        }
        
        stage('Docker Build') {
            steps {
                script {
                    docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.BUILD_NUMBER}")
                }
            }
        }
        
        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                sh 'kubectl apply -f k8s/deployment.yaml'
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
        failure {
            mail to: 'team@example.com', subject: 'Build Failed'
        }
    }
}

将这个 Jenkinsfile 放入项目源码仓库根目录,Jenkins 会自动读取并执行。

2. Shared Libraries:消除重复代码

当你有几十个微服务时,如果每个项目都写一遍上面的 Jenkinsfile,维护将是一场灾难(例如要修改 Docker Registry 地址,得改几十个仓库)。

Shared Libraries 允许我们将通用的流水线逻辑封装成 Groovy 函数,供所有项目调用。

封装后 (vars/standardPipeline.groovy):

def call(Map config) {
    pipeline {
        agent any
        stages {
            stage('Build') {
                steps {
                    sh "${config.buildCmd}"
                }
            }
            // ... 统一的构建、扫描、部署逻辑
        }
    }
}

项目中使用:

@Library('my-shared-lib') _

standardPipeline {
    type = 'maven'
    buildCmd = 'mvn clean package'
}

这样,业务方只需要配置少量参数,底层逻辑由 DevOps 团队统一维护。

3. Jenkins on Kubernetes

传统的 Jenkins Slave(节点)是静态虚拟机,资源利用率低且维护麻烦。 通过 Kubernetes Plugin,我们可以让 Jenkins Master 运行在 K8s 中,并在构建任务触发时,动态启动 Pod 作为 Slave 节点,任务结束后自动销毁。

优势:

  • 弹性伸缩: 并发构建多少任务就启动多少 Pod,资源按需分配。
  • 环境隔离: 每个任务可以在不同的 Docker 镜像中运行(Java 8, Java 17, Nodejs, Go),互不干扰。

Pod Template 定义:

agent {
    kubernetes {
        yaml """
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: maven
    image: maven:3.8.6-jdk-11
    command: ['cat']
    tty: true
  - name: docker
    image: docker:20.10
    command: ['cat']
    tty: true
    volumeMounts:
    - name: docker-sock
      mountPath: /var/run/docker.sock
  volumes:
  - name: docker-sock
    hostPath:
      path: /var/run/docker.sock
"""
    }
}

4. 总结

现代化的 Jenkins 不再是那个 UI 丑陋、配置繁琐的工具。通过 Pipeline as Code 实现版本控制,通过 Shared Libraries 实现标准化和复用,通过 Kubernetes 实现弹性构建能力,Jenkins 依然是企业级 CI/CD 的瑞士军刀。

分享文章