路线图

12-Jenkins

星辉 2026-07-02 阅读 4 min 672 字 路线图
12-Jenkins 封面

Jenkins 是 CI/CD 领域的老牌工具,2005 年(前身 Hudson)诞生至今仍在大量企业中使用。它的核心优势是插件生态极其丰富和Groovy DSL 表达力强。本章简要介绍 Jenkins 在 K8s 时代的用法,以及它与 GitHub Actions/GitLab CI 的对比。

Jenkinsfile:Pipeline as Code#

Jenkins 的流水线配置叫 Jenkinsfile,用 Groovy DSL 写:

groovy
// 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 匹配节点
groovy
// 静态节点
agent { label 'linux && docker' }

// K8s 动态 Pod
agent {
  kubernetes {
    yaml libraryResource('pod-templates/maven-kaniko.yaml')
  }
}

K8s 动态 Pod Agent#

K8s 时代 Jenkins 的最佳实践——每个 job 起一个独立 Pod,用完即销:

groovy
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 复用逻辑的标准方式:

groovy
// 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 变得极简:

groovy
@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 里声明插件版本,避免人工升级导致的不一致:

yaml
controller:
  installPlugins:
    - kubernetes:4225.v7e5058ccb_1c5
    - workflow-aggregator:596.v8c21c963d2d5
    - git:5.2.1
    - credentials-binding:657.v2b_19db_7d6286

版本固定后,升级时整批一起升,能减少"某个插件偷偷升级导致不兼容"的事故。

与 GitHub Actions / GitLab CI 对比#

维度JenkinsGitHub ActionsGitLab CI
部署方式自建SaaS(可自托管)SaaS 或自建
配置文件Jenkinsfile(Groovy)workflow YAML.gitlab-ci.yml(YAML)
表达力最强(Groovy 是完整语言)YAML + 表达式YAML + 表达式
复用机制Shared LibraryReusable Workflow / Composite Actioninclude / extends
K8s 集成Kubernetes Pluginrunner containerkubernetes 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",而是:

  1. Shared Library 的逻辑迁移:Groovy 函数要拆成 composite action / reusable workflow
  2. 插件依赖:某些 Jenkins 插件功能在 SaaS CI 没有对应物,要自己写脚本
  3. 构建环境差异:Jenkins 静态节点上的"预装依赖"在 SaaS CI 里要改成每次装
  4. 凭证迁移: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 焕发第二春的标准方案。