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/config 的 certificate-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 基础镜像不带 rsync 和 sshpass,但流水线里经常要用。两种方式:

方式 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
              '''
            )]
          )]
        )
      }
    }
  }
}

坑 10:docker-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 build 报 Could 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 构建
主题 Stack 由 Jimmy 设计