Featured image of post Jenkins 镜像实战:JDK21 升级、Agent 集群、钉钉/企微通知、Kubernetes 动态 Agent 与流水线部署 Java/Golang/Vue

Jenkins 镜像实战:JDK21 升级、Agent 集群、钉钉/企微通知、Kubernetes 动态 Agent 与流水线部署 Java/Golang/Vue

把零散的 Jenkins 实战笔记整理成"从镜像拉取到一条完整 CI/CD 流水线"——覆盖官方 LTS 镜像、Agent 集群、Generic Webhook Trigger、Active Choices 级联参数、钉钉/企业微信通知、Jenkins on K8s、sshpass/rsync 自定义 Agent、流水线部署 Java/Golang/Vue。

Jenkins 是 CI/CD 界的"老大哥"——2004 年从 Hudson 分支出来后,已经走了 20 年。2024 年 9 月它发布了基于 JDK21 的 LTS(2.504.x 系列),而 2024-09-24 正是 Jenkins 官方对 JDK11 镜像停止安全更新的日期。这篇文章不是"Jenkins 是什么"的科普文,而是把零散的实战笔记整理成"从镜像拉取到一条完整流水线"的路径:拉哪个镜像、Agent 怎么挂、钉钉/企微通知怎么接、Kubernetes 上的动态 Agent 怎么配、流水线部署 Java/Golang/Vue 项目分别怎么写。

阅读对象:需要从零搭一套 Jenkins CI/CD,或正在做 JDK8→JDK11→JDK21 升级的开发者、运维
覆盖范围:官方 LTS 镜像选择、Agent 节点(JNLP)、钉钉/企业微信 webhook 通知、Active Choices 级联参数、Generic Webhook Trigger 触发、SSH/Credentials 凭据、Kubernetes 动态 Agent(jnlp-slave)、自定义 sshpass/rsync Agent、流水线部署 Java/Golang/Vue 模板、常见排错

一、Jenkins 镜像选择:JDK8 / JDK11 / JDK21 LTS

Jenkins 官方镜像 jenkins/jenkins 有几个常用 tag,先把它们的关系理清楚:

Tag含义备注
2.346.3-lts-jdk8JDK8 + Jenkins 2.346.3 LTS2022 年主流选择,但 JDK8 已停止安全更新
2.440.3-lts-jdk11JDK11 + Jenkins 2.440.3 LTS2024 年过渡选择,2024-09-24 停止安全更新
2.504.2-lts-jdk21JDK21 + Jenkins 2.504.2 LTS当前推荐(LTS 路线)

When to use:JDK21 LTS(2.504.x 系列)已是新装 Jenkins 的默认推荐。JDK11 LTS 在 2024-09-24 之后不再收安全补丁——还在跑 JDK11 的同学需要规划升级。

1.1 最小启动:JDK8 老镜像

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 拉镜像
docker pull jenkins/jenkins:2.346.3-lts-jdk8

# 创建数据目录
mkdir -p /data/jenkins/home
chown -R 1000:1000 /data/jenkins/home

# 启动
docker run --name jenkins -d \
  -p 8090:8080 \
  -p 50000:50000 \
  -v /data/jenkins/apache-maven-3.8.6:/data/jenkins/apache-maven-3.8.6 \
  -v /data/jenkins/.nvm:/data/jenkins/.nvm \
  -v /data/jenkins/home:/var/jenkins_home \
  -v /etc/localtime:/etc/localtime \
  --restart=on-failure \
  jenkins/jenkins:2.346.3-lts-jdk8

坑 1:第一个启动慢是因为它要执行 init.groovy.d 下的初始化脚本,并下载默认插件。耐心等几分钟,初始密码可以通过 docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword 拿到。

1.2 JDK11 升级路径(已被 JDK21 取代)

如果已经在线上跑着 JDK8 或 JDK11,可以保留数据卷 jenkins_home,直接换镜像 tag 升级。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# docker-compose.yaml
version: '3'
services:
  jenkins:
    image: jenkins/jenkins:2.440.3-lts-jdk11
    container_name: jenkins11
    restart: always
    privileged: true
    environment:
      - TZ=Asia/Shanghai
      - JAVA_OPTS=-Duser.timezone=Asia/Shanghai
    ports:
      - "8091:8080"
      - "50001:50000"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"   # 让 Jenkins 在容器内调 docker
      - "/usr/bin/docker:/usr/bin/docker"
      - "/home/docker/jenkins11/work:/var/jenkins_home"
      - "/usr/local/maven/apache-maven-3.9.3:/usr/local/maven/apache-maven-3.9.3"
      - "/etc/localtime:/etc/localtime:ro"

升级完后第一次启动会提示"java11 将在 2024-09-24 结束支持"——这是 Jenkins 内置的提示,不是 bug。

坑 2:挂 docker.sock 是为了在 Jenkins 容器内构建镜像(docker build)。但这等同于把宿主机的 docker 权限交给 Jenkins,多用户场景需要谨慎。

1.3 JDK21 升级路径(当前推荐)

1
2
3
docker pull jenkins/jenkins:2.504.2-lts-jdk21
docker tag jenkins/jenkins:2.504.2-lts-jdk21 <private-registry>/base/jenkins/jenkins:2.504.2-lts-jdk21
docker push <private-registry>/base/jenkins/jenkins:2.504.2-lts-jdk21

把启动脚本里的镜像 tag 换成 2.504.2-lts-jdk21 即可,数据卷 jenkins_home 完全兼容——Jenkins 2.504.x 会自动迁移配置。

坑 3:容器时区是对的,但 Jenkins 日志时间格式仍是 UTC。解决:在 系统管理 → 脚本命令行 执行 System.setProperty('org.apache.commons.jelly.tags.fmt.timeZone', 'Asia/Shanghai'),或启动时加 -e JAVA_OPTS=-Duser.timezone=Asia/Shanghai。新版本还支持在 用户列表 → admin → 设置 → 用户自定义时区 里改 UI 时区,但日志里那行仍是 UTC。

二、必备插件清单

Jenkins 的 90% 价值在插件。一份覆盖 90% 场景的最小集合:

插件作用
git / gitlabGit 源码拉取、GitLab 集成
SSH pluginSSH 远程执行命令
Publish Over SSH把构建产物 SCP 到远端并执行命令
SSH Pipeline Steps (ssh-steps)流水线里用 sshCommand / sshPut
Maven Integration构建 Maven 项目
NodeJS构建 Node 项目
Go构建 Golang 项目
git-parameter构建时选择 Git 分支
Active Choices (uno-choice)多级参数级联
Generic Webhook Trigger接收 Git 服务的 webhook
Qy Wechat Notification构建后发企业微信
DingTalk构建后发钉钉
Naginator构建失败后自动重试
Environment Injector注入运行时环境变量
Pipeline / Workflow Aggregator流水线基础
Kubernetes在 K8s 上跑动态 Agent
JDK Parameter多 JDK 项目里选择 jdk 版本
Config File Provider集中管理 settings.xml / npmrc 等配置文件

坑 4:插件安装慢?Jenkins 4.x 之后插件源会从 updates.jenkins-ci.org 拉。国内网络下经常卡死,建议:

  1. *.hpi 文件手动下载:https://updates.jenkins-ci.org/download/plugins/
  2. 或在 系统管理 → 插件管理 → 高级 里把 升级站点 换成国内镜像(如 https://mirror.tuna.tsinghua.edu.cn/jenkins/updates/

三、钉钉 / 企业微信通知:3 行就能接

3.1 钉钉机器人

  1. 群设置 → 智能群助手 → 添加机器人 → 自定义
  2. 安全设置选"自定义关键词"或"加签"
  3. 拿到 webhook(形如 https://oapi.dingtalk.com/robot/send?access_token=xxxx
  4. Jenkins 安装 DingTalk 插件,在 Job 配置里填 webhook

curl 直接测试:

1
2
3
curl 'https://oapi.dingtalk.com/robot/send?access_token=<YOUR_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"msgtype":"markdown","markdown":{"title":"构建通知","text":"## ${JOB_NAME} 构建成功"}}'

3.2 企业微信机器人

  1. 群右键 → 群机器人 → 添加 → 选"群机器人"
  2. 拿到 webhook(形如 https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxx
  3. Jenkins 安装 Qy Wechat Notification 插件
1
2
3
4
5
6
7
8
curl "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=<YOUR_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "msgtype": "markdown",
    "markdown": {
      "content": "## ${JOB_NAME} 构建信息\n**构建ID**: ${BUILD_ID}\n**状态**: ${currentBuild.currentResult}\n[查看日志](${BUILD_URL}/console)"
    }
  }'

3.3 在 Pipeline 里推 commit 信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
sh '''
  git config --global log.date format:'%Y-%m-%d %H:%M:%S'
  commit_info=$(git log -1 --oneline --pretty=format:'%s by %an at %cd' --no-merges)
'''
def commitInfo = sh(script: "git log -1 --oneline --pretty=format:'%s by %an at %cd' --no-merges", returnStdout: true).trim()

httpRequest(
  url: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=<YOUR_KEY>',
  httpMode: 'POST',
  contentType: 'APPLICATION_JSON',
  requestBody: """
    {"msgtype":"markdown","markdown":{"content":"<font color=\"warning\">**${env.JOB_NAME} 构建**</font>\n>提交:${commitInfo}\n>状态:${currentBuild.currentResult}\n[日志](${env.BUILD_URL}/console)"}}
  """
)

坑 5:把 webhook 凭据硬编码在脚本里?不要这样做。把 webhook 放进 Jenkins 凭据(Credentials)→ Secret text 里,再通过 withCredentials([string(credentialsId: 'wechat-webhook', variable: 'WEBHOOK')]) 引用。

四、Agent 集群:JNLP 节点 + K8s 动态 Agent

4.1 JNLP Agent(传统固定节点)

适合:物理机 / 虚拟机 / 容器化的固定 Agent。

前置:在 Agent 机器上装好 java、maven、git。

启动方式 A:java web 启动代理

1
2
3
4
5
nohup java -jar /usr/local/jenkins/agent.jar \
  -jnlpUrl http://<JENKINS_URL>/computer/<NODE_NAME>/jenkins-agent.jnlp \
  -secret <SECRET_FROM_JENKINS> \
  -workDir "/usr/local/jenkins/ws" \
  > nohup.log &

启动方式 B:Kubernetes 上的 JNLP 静态 Agent

1
2
# jenkins-master 上的 SSH remote hosts / Publish over SSH
# 用 Publish Over SSH 把 jar 推到远端, 然后 java -jar 启动

4.2 Kubernetes 动态 Agent(推荐)

Jenkins 装 Kubernetes 插件,每次 Job 触发时在 K8s 里拉一个临时 Pod,构建完销毁。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
pipeline {
  agent {
    kubernetes {
      cloud 'k8s'              # 配在 系统管理  节点管理  Configure Clouds
      inheritFrom 'jnlp-slave' # 复用 Pod Template
      namespace 'kube-ops'
    }
  }
  stages {
    stage('build') {
      steps {
        container('jnlp') {
          sh 'mvn clean package -P prod'
        }
      }
    }
  }
}

前置

  1. 制作 Jenkins 连接 K8s 的证书(K8s ~/.kube/configcertificate-authority-data / client-certificate-data / client-key-data 三个字段,base64 解码后保存)
  2. kube-ca.crt + kube-cert.pfx 配到 Jenkins 凭据里
  3. 创建一个 jnlp-slave Pod Template(基础镜像 jenkins/inbound-agent:latest-jdk21

K8s 集群配置

1
2
Kubernetes Cloud URL: https://<K8S_API_SERVER>:6443
Kubernetes Cloud Credentials: <上面做的 pfx 凭据>

坑 6:Pod 启动失败?用 kubectl logs -f --tail=10 查看 Jenkins 动态创建的 Pod。80% 的情况是 jnlp-slave Pod Template 里的镜像拉不到(私有仓库没配 imagePullSecrets)或 Jenkins 容器内没装 rsync

4.3 自定义 Agent 镜像(装 rsync + sshpass)

jenkins/inbound-agent 基础镜像不带 rsyncsshpass,但流水线里经常要用。两种方式:

方式 A:临时容器安装 + commit

1
2
3
4
5
6
7
8
docker run -u root -it --name jenkins-agent-temp \
  jenkins/inbound-agent:latest-jdk21 /bin/bash
# 容器内
apt update && apt install -y rsync sshpass
exit
# 宿主机
docker commit jenkins-agent-temp <private-registry>/base/jenkins/inbound-agent:latest-jdk21-rsync
docker push <private-registry>/base/jenkins/inbound-agent:latest-jdk21-rsync

方式 B:Dockerfile(推荐,可追溯)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ARG version=latest-jdk21
FROM jenkins/inbound-agent:$version
ARG version
LABEL Description="Custom Jenkins Agent with rsync and sshpass" Vendor="某安全公司" Version="$version"
ARG user=jenkins
USER root
RUN apt-get update && \
    apt-get install -y rsync sshpass && \
    rm -rf /var/lib/apt/lists/*
USER ${user}
ENTRYPOINT ["/usr/local/bin/jenkins-agent"]
1
2
docker build -t <private-registry>/base/jenkins/inbound-agent:latest-jdk21-rsync .
docker push <private-registry>/base/jenkins/inbound-agent:latest-jdk21-rsync

五、流水线:声明式 vs 脚本式

5.1 声明式(推荐)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
pipeline {
  agent any
  options {
    buildDiscarder logRotator(daysToKeepStr: '10', numToKeepStr: '10')
    timeout(time: 30, unit: 'MINUTES')
  }
  parameters {
    choice(name: 'profiles', choices: ['dev', 'test', 'prod'], description: '请选择构建环境')
  }
  triggers {
    GenericTrigger(
      genericRequestVariables: [[key: 'profile', regexpFilter: '$']],
      token: 'test'
    )
  }
  stages {
    stage('git checkout') {
      steps {
        git branch: 'master', credentialsId: 'gitlab-cred', url: 'http://<GITLAB>/<your-org>/gdsTmqtt.git'
      }
    }
    stage('build') {
      steps {
        sh 'mvn clean package -P prod -DskipTests'
      }
    }
    stage('publish') {
      steps {
        sshPublisher(
          publishers: [sshPublisherDesc(
            configName: 'prod-server',
            transfers: [sshTransfer(
              cleanRemote: false,
              execCommand: '''
                mkdir -p /home/project/safety/gdsTmq/jar
                docker rm -f gdsTomq
                mv /home/tmp/safety-gdsTomq/*.jar /home/project/safety/gdsTmq/jar/
                docker run -d --restart=always --net=host --name=gdsTomq \
                  -v /home/project/safety/gdsTmq/jar:/jar \
                  <private-registry>/base/jdk:8u381-2 \
                  java -jar /jar/gds-0.0.1-SNAPSHOT.jar
              ''',
              remoteDirectory: '/home/tmp/safety-gdsTomq',
              sourceFiles: 'target/gds-0.0.1-SNAPSHOT.jar'
            )]
          )]
        )
      }
    }
  }
  post {
    success { echo 'deploy success' }
    failure { echo 'deploy failed' }
  }
}

5.2 脚本式(灵活但难维护)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
node {
  stage('Remote SSH') {
    def remote = [:]
    remote.name = 'test'
    remote.host = '<INTERNAL_HOST>'
    remote.user = 'root'
    remote.password = '<REDACTED>'
    remote.allowAnyHosts = true
    sshCommand remote: remote, command: "ls -lrt"
  }
}

坑 7:声明式是 2017 年之后的官方推荐。新项目用声明式;老项目如果是脚本式,不要一次性重写,逐步迁移。

六、Active Choices:多级参数级联

真实场景:先选 节点(master/dev/联创),再选 项目(不同节点不同项目),再选 主机(不同节点不同主机),再选 分支(不同项目不同分支)。

步骤

  1. Job 选"参数化构建"
  2. Active Choices Parameter,name=agent,Choice Type=Single
  3. Script:
1
return ["master", "dev", "liangchuang"]
  1. Active Choices Reactive Parameter,name=project,Referenced parameters=agent
1
2
3
4
5
if (agent.equals("master")) {
  return ["safetyBackend", "safetyAppBackend", "safetyFrontend", "safetyAuth", "safetyAppH5", "safetyGds"]
} else if (agent.equals("dev")) {
  return ["safetyBackend", "safetyAuth"]
}
  1. 同样做 host(referenced=agent)、branch(referenced=project)

坑 8:低版本 Jenkins(2.346.x)装 Active Choices 时提示依赖缺失,要先装 script-security 1.x 版本,再装 uno-choice

七、流水线部署三类项目:Java / Golang / Vue

7.1 部署 Java 项目

参考第五章 5.1 的完整 pipeline。核心点

  • mvn clean package -P <profile> 打 jar
  • sshPublisher 把 jar 传到目标机器
  • 远端 docker rm -f <container> + docker run -d 重启

钉钉/企微通知 + commit 信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
stage('notify') {
  steps {
    sh '''
      git config --global log.date format:'%Y-%m-%d %H:%M:%S'
      commit_info=$(git log -1 --oneline --pretty=format:'%s by %an at %cd' --no-merges)
    '''
    script {
      def commitInfo = sh(script: "git log -1 --oneline --pretty=format:'%s by %an at %cd' --no-merges", returnStdout: true).trim()
      httpRequest(
        url: "<WEBHOOK_URL>",
        httpMode: 'POST',
        contentType: 'APPLICATION_JSON',
        requestBody: "{\"msgtype\":\"markdown\",\"markdown\":{\"content\":\"**${env.JOB_NAME} 构建成功**\\n>提交:${commitInfo}\"}}"
      )
    }
  }
}

7.2 部署 Golang 项目

Golang 项目的特殊点:Go 安装路径GOPATH 设置交叉编译

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pipeline {
  agent any
  environment {
    GOPATH = "${JENKINS_HOME}/golang_workspace"
    GOPROXY = "https://goproxy.cn"
  }
  stages {
    stage('build') {
      steps {
        sh '''
          export PATH=$PATH:$GOPATH/bin
          mkdir -p $GOPATH
          CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main main.go
        '''
      }
    }
    stage('publish') {
      steps {
        sshCommand remote: [...], command: '''
          rm -f /home/project/tailings/main
          mv /home/docker/jenkins/data/workspace/tailings-backend/main /home/project/tailings/
          chmod +x /home/project/tailings/main
          docker-compose -f /home/docker-compose.yml restart tailings-backend
        '''
      }
    }
  }
}

坑 9:Jenkins 装 Go 插件后,Go 路径会被装到 $JENKINS_HOME/tools/org.jenkinsci.plugins.golang.GolangInstallation/go/<version>/。如果手动 tar -C 解压到 /home/docker/jenkins/data/go/路径对不上会找不到——解决方法是用 tar -C直接建 go1.21.5 子目录,让最终路径是 /home/docker/jenkins/data/go/go1.21.5/

7.3 部署 Vue 项目

Vue 项目用 npm 构建,产物在 dist/

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
pipeline {
  agent any
  tools {
    nodejs 'node14.17.3'    #  全局工具配置 里先加 NodeJS 安装
  }
  stages {
    stage('build') {
      steps {
        sh '''
          npx browserslist@latest --update-db
          npm install
          npm run build:test
          cd dist
          tar -zcvf dist.tar.gz *
        '''
      }
    }
    stage('publish') {
      steps {
        sshPublisher(
          publishers: [sshPublisherDesc(
            configName: 'frontend-server',
            transfers: [sshTransfer(
              sourceFiles: 'dist/dist.tar.gz',
              removePrefix: 'dist/',
              remoteDirectory: '/project/frontend/dist',
              execCommand: '''
                cd /home/project/frontend/dist
                shopt -s extglob
                rm -rf !(dist.tar.gz)
                tar -zxvf dist.tar.gz -C ./
                rm -rf dist.tar.gz
                cd /home
                docker-compose restart frontend
              '''
            )]
          )]
        )
      }
    }
  }
}

坑 10docker-compose restart frontend 只重启容器,不会重新读 docker-compose.yaml 的端口/环境变量。改了 compose 文件必须 docker-compose up -d

八、定时构建 + 自动合并代码

轮询 SCM(在 Job 的"构建触发器"里勾选"轮询 SCM"):

1
2
3
4
5
TZ=Asia/Shanghai
0 9,13 * * *         # 每天 9 点和 13 点
0 14,20 * * *        # 每天 14 点和 20 点
*/5 16-17 * * *      # 每天 16-17 点,每 5 分钟
H/5 * * * *          # 每 5 分钟(H 是 hash 分散)

坑 11:5 个星分别是"分 时 日 月 周",别写反。H/5*/5 好——Jenkins 会在集群内做 hash 分散,多个 Job 不会同时触发。

自动合并代码(在 SCM 的 Additional Behaviours 加):

  • Clean before checkout(构建前清空工作区)
  • Prune stale remote-tracking branches(同步远程分支)
  • Merge before build(构建前 merge 目标分支)

坑 12:Name of repository 一定要写 origin,否则 Merge before buildCould not find remote ref

九、常见排错

9.1 “Waiting for Jenkins to finish collecting data” 等待时间长

Jenkins 通过 Maven 构建 Java 项目后,会做 指纹验证——给每个 jar 算一个 SHA-1,写入数据库。Java 项目越大、依赖越多,等待时间越长。

解决:关掉指纹验证 → Job 配置 → 构建环境 → 勾选 “Disable deferred wipeout and improve performance”

9.2 中文乱码

启动加:

1
2
LANG=zh_CN.UTF-8
JAVA_OPTS=-Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai

9.3 IllegalArgumentException: No enum constant io.jenkins.plugins.localization_zh_cn.UserCommunityProperty.ShowConditions

中文插件版本不兼容。解决:删除中文插件 → 重装匹配 Jenkins 版本的 localization-zh-cn 插件 → 重启

9.4 Publish Over SSH 连接超时

检查:

  1. ~/.ssh/known_hosts 里有没有目标机器的公钥(StrictHostKeyChecking=no 可绕过)
  2. 远端 sshd_config 是否允许 PasswordAuthentication yes
  3. 网络是否可达(telnet <INTERNAL_HOST> 22

9.5 流水线里 Git 拉取失败

1
2
3
4
5
git branch: 'master', 
    credentialsId: 'gitlab-cred', 
    url: 'http://<INTERNAL_HOST>:13000/<your-org>/gdsTmqtt.git',
    // 新版要加 poll: false 否则每分钟检查一次
    poll: false

9.6 Kubernetes 插件连接集群失败

1
2
3
4
# 查看 Jenkins 创建的 agent pod 日志
kubectl logs -f --tail=10 \
  $(kubectl get pods -n jenkins | grep <job-name> | awk '{print $1}' | head -1) \
  -n jenkins

80% 是这几个原因

  1. 证书过期(K8s 证书默认 1 年)
  2. 镜像拉取失败(私有仓库没配 imagePullSecrets)
  3. jnlp-slave Pod Template 里 command / args 写错

十、写在最后

Jenkins 的"全"是把双刃剑——插件生态丰富意味着学习曲线陡。从 2004 年走到 2024 年,Jenkins 仍是大量企业 CI/CD 的"事实标准"(不是"应该",是"事实上")。如果你正在做新项目选型,可以看看 GitHub Actions(云原生、YAML 优先)或 Drone(容器化、声明式),但对存量 Jenkins 集群,升级到 JDK21 LTS、关闭指纹验证、清理无用插件,就能让 80% 的项目跑得更稳。

下一步建议:

  • Jenkins on K8s 的完整部署:Jenkins Master 也跑在 K8s 里 + dynamic Agent
  • Jenkins Configuration as Code (JCasC):用 jenkins.yaml 管理所有插件、凭据、节点
  • Pipeline as Code:把 Jenkinsfile 放进 Git 仓库,Job 通过 Multibranch Pipeline 自动发现

2024+ 视角补充

本文写于 2024-09,2025-2026 期间 Jenkins 关键演进:

  • Jenkins 2.516.x LTS(2025-04 起):JDK 17 / 21 双轨 LTS,JDK 11 LTS 已于 2024-09-24 停止安全补丁——本文内容完全契合
  • Jenkins Evergreen 持续完善:CD-as-a-Service 模式,与 Kubernetes Operator 深度集成
  • Jenkins on K8s Operator(jenkinsci/kubernetes-operator):Jenkins Master 本身也跑在 K8s 上,生产级高可用 + 自动备份
  • Pipeline as Code 主导:Jenkinsfile + Multibranch Pipeline 成为 2025+ 事实标准,老的 FreeStyle Job 模式逐步淘汰
  • AI 辅助插件(2025+):Jenkins 接入 LLM 做"日志摘要 + 失败原因诊断",但仅作为辅助——核心编排仍是声明式流水线

实战建议(2025-2026 视角):Jenkins 仍是 Java 后端 CI/CD 事实标准;GitHub Actions / GitLab CI 在新项目是强力竞争者,但存量 Jenkins 集群升级到 JDK 21 LTS + 跑在 K8s 上仍是 2025+ 主路线。

参考资料

使用 Hugo 构建
主题 StackJimmy 设计