虽然 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 的瑞士军刀。