12-Jenkins
Jenkins 是 CI/CD 领域的老牌工具,2005 年(前身 Hudson)诞生至今仍在大量企业中使用。它的核心优势是插件生态极其丰富和Groovy DSL 表达力强。本章简要介绍 Jenkins 在 K8s 时代的用法,以及它与 GitHub Actions/GitLab CI 的对比。
Jenkinsfile:Pipeline as Code#
Jenkins 的流水线配置叫 Jenkinsfile,用 Groovy DSL 写:
// Jenkinsfile
pipeline {
agent {
kubernetes {
yaml """
apiVersion: v1
kind: Pod
metadata:
labels:
app: jenkins-agent
spec:
containers:
- name: maven
image: maven:3.9-eclipse-temurin-17
command: ["sleep", "infinity"]
- name: kaniko
image: gcr.io/kaniko-project/executor:v1.21.0-debug
command: ["sleep", "infinity"]
- name: kubectl
image: alpine/kubectl:1.29 # bitnami/* 已于 2025-08 停更/转 Broadcom 订阅,改用维护中的 alpine/kubectl
command: ["sleep", "infinity"]
"""
}
}
environment {
IMAGE_TAG = "${env.GIT_COMMIT[0..7]}"
SONAR_TOKEN = credentials('sonar-token')
}
stages {
stage('Test') {
steps {
container('maven') {
sh 'mvn test'
}
}
}
stage('Build') {
when { branch 'main' }
steps {
container('kaniko') {
sh """
/kaniko/executor --context . \\
--dockerfile Dockerfile \\
--destination registry/myapp:${IMAGE_TAG}
"""
}
}
}
stage('Deploy Production') {
when { branch 'main' }
input {
message "确认部署到生产?"
}
steps {
container('kubectl') {
sh 'kubectl set image deployment/myapp myapp=registry/myapp:${IMAGE_TAG}'
}
}
}
}
post {
failure {
emailext subject: "[FAILED] ${env.JOB_NAME}",
to: 'team@example.com'
}
}
}agent / node / label#
Jenkins 的执行器概念:
- agent:执行流水线的机器或 Pod
- node:传统静态节点的概念
- label:节点标签,job 通过 label 匹配节点
// 静态节点
agent { label 'linux && docker' }
// K8s 动态 Pod
agent {
kubernetes {
yaml libraryResource('pod-templates/maven-kaniko.yaml')
}
}K8s 动态 Pod Agent#
K8s 时代 Jenkins 的最佳实践——每个 job 起一个独立 Pod,用完即销:
agent {
kubernetes {
yaml """
spec:
containers:
- name: maven
image: maven:3.9
- name: kaniko
image: gcr.io/kaniko-project/executor:debug
securityContext:
runAsUser: 0
- name: kubectl
image: alpine/kubectl:1.29 # bitnami/* 已于 2025-08 停更/转 Broadcom 订阅,改用维护中的 alpine/kubectl
"""
}
}多容器 Pod 是 Jenkins K8s 方案最强大的特性——maven 容器负责编译,kaniko 容器负责构建镜像,kubectl 容器负责部署,它们共享 workspace volume。
注意(2026):Google 已于 2025-06 归档 kaniko 仓库(只读、不再新增功能,Chainguard 接手仅做维护与安全补丁)。上面的
kaniko容器仅作多容器 Pod 示例,新流水线的镜像构建容器优先考虑 BuildKit rootless 或 Buildah,详见 14 章。
Shared Library#
跨 Jenkinsfile 复用逻辑的标准方式:
// vars/standardPipeline.groovy
def call(Map config = [:]) {
pipeline {
agent { label 'docker' }
stages {
stage('Test') {
steps {
sh "${config.test_cmd ?: 'make test'}"
}
}
stage('Build') {
when { branch 'main' }
steps {
sh "docker build -t myapp:${env.GIT_COMMIT[0..7]} ."
}
}
}
}
}业务项目的 Jenkinsfile 变得极简:
@Library('jenkins-shared-lib') _
standardPipeline(
service: 'user-service',
test_cmd: 'mvn test'
)插件生态#
Jenkins 最大的优势——1800+ 插件覆盖几乎所有场景:
- kubernetes plugin:K8s 动态 Pod Agent
- git plugin / gitlab plugin / github plugin:代码源集成
- credentials-binding plugin:密钥管理
- pipeline plugin:Pipeline as Code
- blue ocean:现代化 UI
- sonar plugin:SonarQube 集成
- docker plugin / docker-workflow plugin:Docker 集成
- job-dsl plugin:用代码声明式定义 job(配置即代码)
- configuration-as-code plugin:Jenkins 本身配置也代码化
劣势:插件质量参差不齐、插件间依赖冲突是 Jenkins 运维的主要痛点。升级 Jenkins 时经常因为插件不兼容出问题。建议用 installPlugins 在 Helm values 里声明插件版本,避免人工升级导致的不一致:
controller:
installPlugins:
- kubernetes:4225.v7e5058ccb_1c5
- workflow-aggregator:596.v8c21c963d2d5
- git:5.2.1
- credentials-binding:657.v2b_19db_7d6286版本固定后,升级时整批一起升,能减少"某个插件偷偷升级导致不兼容"的事故。
与 GitHub Actions / GitLab CI 对比#
| 维度 | Jenkins | GitHub Actions | GitLab CI |
|---|---|---|---|
| 部署方式 | 自建 | SaaS(可自托管) | SaaS 或自建 |
| 配置文件 | Jenkinsfile(Groovy) | workflow YAML | .gitlab-ci.yml(YAML) |
| 表达力 | 最强(Groovy 是完整语言) | YAML + 表达式 | YAML + 表达式 |
| 复用机制 | Shared Library | Reusable Workflow / Composite Action | include / extends |
| K8s 集成 | Kubernetes Plugin | runner container | kubernetes executor |
| 插件生态 | 1800+ 插件 | marketplace 数万 Action | 内置为主 |
| 运维成本 | 高(插件管理、Master 维护) | 低(SaaS) | 中 |
| 学习曲线 | 陡(Groovy + 插件依赖) | 平缓 | 平缓 |
| 适合场景 | 复杂流水线、已有 Jenkins 投资 | GitHub 生态、开源项目 | GitLab 生态、私有化 |
什么时候还该选 Jenkins#
- 已有大量 Jenkins 投资的团队:迁移成本高,留着继续用
- 流水线逻辑极复杂:Groovy 是完整编程语言,能写 GitHub Actions 写不了的复杂逻辑(条件嵌套、循环、异常处理、动态生成 stage)
- 需要极强的插件生态:某些冷门集成(特定硬件、特定协议、老旧系统)只有 Jenkins 插件
- 混合多云、跨代码平台:Jenkins 可以同时接 GitHub、GitLab、Bitbucket,统一在一个 UI 管理
- 需要精细的节点调度:Jenkins 的 label 体系和 node 调度比 SaaS CI 更灵活,能做"GPU job 只跑在带 GPU 标签的节点"这类精细控制
什么时候该换掉 Jenkins#
- 新团队、没有历史包袱:直接上 GitHub Actions 或 GitLab CI,省运维
- Jenkins Master 维护成本高:升级一次插件冲突要一周,是时候迁了
- 团队对 Groovy 不熟:YAML 配置门槛低,新员工 onboarding 快
- 追求 SaaS 化:不想运维 CI 服务器
- 安全审计压力大:Jenkins Master 被攻破影响面大(1800+ 插件的攻击面),SaaS CI 由云厂商兜底
迁移注意事项#
从 Jenkins 迁到 SaaS CI 时,最大的坑不是"翻译 Jenkinsfile 到 YAML",而是:
- Shared Library 的逻辑迁移:Groovy 函数要拆成 composite action / reusable workflow
- 插件依赖:某些 Jenkins 插件功能在 SaaS CI 没有对应物,要自己写脚本
- 构建环境差异:Jenkins 静态节点上的"预装依赖"在 SaaS CI 里要改成每次装
- 凭证迁移:Jenkins Credentials → GitHub Secrets / GitLab Variables,注意 masked 字符限制
建议先迁一个最简单的服务试点,跑通后再批量迁。
实战要点#
- K8s 动态 Pod Agent 是 Jenkins 现代化的关键。静态 Slave 模式已经过时,每个 job 起独立 Pod 才是云原生姿势。
- Shared Library 是 Jenkins 的复用神器。把所有项目共用的流水线逻辑抽到 Shared Library,业务 Jenkinsfile 一两行调用。
- 插件管理是 Jenkins 运维的核心痛点。建议用
installPlugins在 Helm values 里声明插件版本,避免人工升级导致的不一致。 - Pipeline 脚本用 declarative,不用 scripted。declarative pipeline 更结构化、更易读、工具支持更好。
input步骤放agent none的 stage。等待人工审批时不占用 Pod,避免资源浪费。
小结#
Jenkins 在 K8s 时代仍然是强大的 CI 工具——Groovy DSL 的表达力、1800+ 插件的生态、Shared Library 的复用机制,是 GitHub Actions 和 GitLab CI 短期难以完全替代的。
但它的运维成本(Master 维护、插件冲突)和学习曲线(Groovy)让很多新团队望而却步。如果你没有历史包袱,直接上 GitHub Actions 或 GitLab CI 通常更省心;如果有 Jenkins 投资且流水线逻辑复杂,K8s 动态 Pod Agent + Shared Library 是让 Jenkins 焕发第二春的标准方案。