Jenkins Helm 升级 2.516→2.568 后流水线失败与 Agent 无法连接的问题排查

Jenkins Helm 升级 2.516→2.568 后流水线失败与 Agent 无法连接的问题排查

记录时间:2026-07-20
环境:Kubernetes 1.28.15(arm64)/ Jenkins Helm chart jenkins/jenkins 5.8.83 → 5.9.39(core 2.516.2 → 2.568.1)/ kubernetes 插件动态 agent / 私有 Harbor reg-hub.gzeport.com

一、问题现象

Helm chart 从 5.8.83(core 2.516.2)升级到 5.9.39(core 2.568.1)后,所有共享库流水线(pipelinePublicLibrary)构建失败。表面报错出现在 post { failure } 阶段:

org.jenkinsci.plugins.credentialsbinding.impl.CredentialNotFoundException:
Could not find credentials entry with ID 'null'

这个报错具有迷惑性——凭证 ID 是字面量 null,说明 environment {} 块根本没有生效,真实故障点在 stages 开始之前,post 阶段的凭证错误只是次生现象。

本次升级实际上是一条链上的三个独立问题,每修复一个才暴露下一个:

  1. Agent 镜像 JDK 版本与新 controller 不兼容(UnsupportedClassVersionError
  2. JCasC 在 helm upgrade 时重置了 cloud 配置(工作空间卷丢失)
  3. UI 手工重建 Pod 模板时,jnlp 容器被误填了 sleep 9999999 保活命令,agent 进程永远不启动

二、排查过程

2.0 前置:升级触发与 helm upgrade 未生效

触发原因:Jenkins 控制台报告”部分插件由于缺少依赖无法加载”。当前核心 2.516.2 不满足已安装插件的最低要求(Favorite 需 2.562+、Plugin Utilities API/Font Awesome/Bootstrap5 需 2.555.1+、Credentials 需 2.541.1+ 等),Pipeline/Credentials/Git/Kubernetes/Blue Ocean 等数十个插件连锁加载失败。

升级决策:Helm chart 5.8.835.9.39(appVersion 2.568.1,满足所有插件最低核心要求),controller 镜像本地化到 Harbor reg-hub.gzeport.com/library/jenkins/jenkins:2.568.1-lts-jdk21

helm upgrade 执行后镜像未变

helm upgrade jenkins ./jenkins-5.9.39.tgz -n devops -f values.yaml

命令执行成功(helm list 显示 chart 版本已变为 5.9.39),但 kubectl get sts jenkins -n devops -o jsonpath='{.spec.template.spec.containers[0].image}' 仍显示旧镜像 2.516.2

原因:Jenkins chart 使用 StatefulSet 部署 controller。helm upgrade 会更新 StatefulSet 的 Pod 模板,但 StatefulSet 不会像 Deployment 那样自动触发滚动重建——模板变更后需要手动删除 Pod,StatefulSet 才会按新模板重建。

delete pod 卡在优雅关闭

kubectl delete pod -n devops jenkins-0

命令卡住不返回。Jenkins controller 的 terminationGracePeriodSeconds 默认较长,且 preStop 钩子会等待正在执行的构建安全结束、将队列和构建状态持久化到 PVC。在日志确认当前无关键运行中构建后,强制跳过优雅关闭:

kubectl delete pod -n devops jenkins-0 --grace-period=0 --force

注意--grace-period=0 --force 立即 SIGKILL 容器进程,跳过队列持久化。已落盘到 PVC 的 Job 配置和构建历史不会丢失,仅 in-flight(正在运行但尚未写盘)的构建状态可能丢失。执行前确认不在构建窗口期,且已备份 PVC 数据

新 Pod 重建后,controller 正常启动,插件全部加载成功,依赖错误消失。但随后流水线构建暴露 agent 相关问题,进入后续排查。

2.1 定位次生错误的真实层级

失败日志只有 post 阶段的 CredentialNotFoundException。对比升级前同一 Job 的成功日志,确认:

  • WEBHOOK_URL 等变量定义在流水线骨架的全局 environment {}
  • post 阶段取到 null,说明流水线在 environment 生效前就已失败
  • 结论:先找升级后日志里第一个报错,不要被 post 阶段的报错带偏

2.2 第一个真实报错:agent 端类加载失败

Pod 能拉起、jnlp 能连接,但 agent 端抛出:

Caused by: java.lang.UnsupportedClassVersionError:
hudson/slaves/SlaveComputer$SlaveVersion has been compiled by ...

controller(2.568.1,JDK 21 编译)通过 remoting 下发的类,agent 侧的旧 JVM(JDK 17)拒绝加载。chart 5.9.x 起 controller 基线是 JDK 21,而共享库 podTemplate 固定的 inbound-agent:3327.v868139a_d00e0-6 是 5.8.x 时代的 JDK 17 运行时镜像。

2.3 试错 1(引入回归):升级镜像时在 YAML 块里写了 Groovy 注释

将 4 个流水线文件的 jnlp 镜像升级为 3383.vc8881d4b_0e76-1 时,在 podTemplate 的 yaml """...""" 多行字符串内加了 // 注释,导致 snakeyaml 解析失败:

could not find expected ':'
 in 'reader', line 14, column 21:
                    // JDK 21 运行时,适配 controller 2.56 ...

注意yaml """...""" 块内是纯 YAML 语境,注释必须用 #;Groovy 的 // 注释只能写在字符串外。与「全角引号」同类——嵌入式语言块的语法跟宿主 Groovy 不同。

2.4 第二个问题:Pod 卡在 ContainerCreating,agent offline

YAML 修复后,Pod 创建但四个容器全部卡住,Jenkins 日志停在 is offline。检查 Jenkins 的 kubernetes cloud 配置发现:「工作空间卷」被重置为默认的 Generic Ephemeral Volume,而升级前用的是 PVC(jenkins-data-pvc)。

原因:该 chart 默认由 Configuration as Code(JCasC)管理配置,每次 helm upgrade 都会重新应用 values 里的 JCasC 配置,UI 手工修改的 cloud 配置会被覆盖重置

2.5 第三个问题:卷改回后 agent 仍连不上——jnlp 被 sleep 占用

工作空间卷改回 PVC 后 Pod 变为 4/4 Running,但 controller 日志持续:

Waiting for agent to connect (270/300): dyck-jar-gzeport-anchorage-17-...
java.lang.IllegalStateException: Agent is not connected after 300 seconds, status: Running

用 kubectl 直查 Pod 的 jnlp 容器定义,发现:

command: ["sleep"]
args: ["9999999"]

再查 Jenkins 存活配置 config.xml,确认 UI 重建的 Pod 模板里 jnlp 容器被填了 sleep / 9999999Jenkins UI 新增 Container Template 时,「运行的命令 / 命令参数」输入框会预填 sleep 9999999(这是给 maven、buildkit 等工具容器用的保活默认值)。jnlp 容器被它覆盖了镜像自带的 jenkins-agent 入口命令,容器只在睡觉,remoting 永远不会发起连接,5 分钟超时后 Pod 被杀重建,无限循环。

三、根因分析

# 根因 触发条件 表象
1 chart 5.9.x 的 controller 为 JDK 21 基线,agent 镜像仍是 JDK 17 Helm chart 跨 5.8→5.9 升级 agent 端 UnsupportedClassVersionError
2 JCasC 在 helm upgrade 时重新套用 values,UI 手工配置被重置 cloud 配置只在 UI 里改过、未固化到 values 工作空间卷变回默认值,Pod 卡 ContainerCreating
3 UI 新增容器模板预填 sleep 9999999,覆盖了 jnlp 入口命令 在 UI 手工重建 Pod 模板 Pod Running 但 agent 300 秒连接超时

三者环环相扣:升级触发 1;修 1 需要动配置,暴露 2;在 UI 修 2 时引入 3。

四、解决方案

4.0 升级 chart 及本地化 controller 镜像

仓库配置改动广州电子口岸-k8s部署yaml/k8s-jenkins/helm-jenkins/):

values.yaml 中 controller 镜像启用本地 Harbor 仓库:

controller:
  image:
    registry: "reg-hub.gzeport.com"
    repository: "library/jenkins/jenkins"
    tag: 2.568.1-lts-jdk21
参数 说明
registry reg-hub.gzeport.com 避免信创节点直连 docker.io 拉取受限
repository library/jenkins/jenkins Harbor project=library,repo=jenkins/jenkins
tag 2.568.1-lts-jdk21 满足所有插件最低核心要求(最高需 2.562+),chart 5.9.39 配套版本

run.sh 中 chart 包引用由 5.8.83 改为 5.9.39,tag 注释同步更新。

新 chart 包 jenkins-5.9.39.tgz 通过 helm pull jenkins/jenkins --version 5.9.39 下载。执行前需 helm lint + helm template 渲染验证镜像地址和 values 兼容性:

helm lint ./jenkins-5.9.39.tgz -f values.yaml
# 预期:1 chart(s) linted, 0 chart(s) failed

helm template jenkins ./jenkins-5.9.39.tgz -f values.yaml -n devops \
  | grep -E "reg-hub|jenkins/jenkins" | grep -v inbound-agent | sort -u
# 预期输出含 reg-hub.gzeport.com/library/jenkins/jenkins:2.568.1-lts-jdk21

旧 chart 包 jenkins-5.8.83.tgz 保留作回滚备份。

4.1 同步 JDK 21 的 agent 镜像到 Harbor

skopeo copy --insecure-policy docker://d.ajunyunwei.xyz/jenkins/inbound-agent:3383.vc8881d4b_0e76-1 docker://reg-hub.gzeport.com/cicd/jenkins/inbound-agent:3383.vc8881d4b_0e76-1

注意3383.vc8881d4b_0e76-1 无 JDK 后缀的 tag 默认即 JDK 21 运行时;JDK 17 变体带显式 -jdk17 后缀。跨架构同步优先用 skopeo copy --all 保留完整多架构 manifest。

4.2 升级共享库 4 处 jnlp 镜像引用

改动文件(注释使用 YAML 的 #,不能用 Groovy 的 //):

文件 说明
vars/helmDevopsMultByk8sPipeline.groovy Helm JAR/WAR 统一骨架
vars/vueDevopsByk8s.groovy Vue 前端流水线
vars/jarDevopsMultbyDockerCompose.groovy JAR Docker Compose 流水线
vars/warDevopsMultbyDockerCompose.groovy WAR Docker Compose 流水线
# JDK 21 运行时,适配 controller 2.568.1(chart 5.9.x 起 controller 为 JDK 21 基线,旧 JDK 17 agent 会报 UnsupportedClassVersionError)
image: reg-hub.gzeport.com/cicd/jenkins/inbound-agent:3383.vc8881d4b_0e76-1

maven 容器的 openjdk-11 不需要动——它只是构建工具,业务代码继续用 JDK 11 编译;需要 JDK 21 的只有 jnlp(agent 本体运行时)。

4.3 恢复 kubernetes cloud 配置

Manage Jenkins → Clouds → kubernetes 中确认/恢复以下项:

配置项 说明
工作空间卷 Persistent Volume Claim Workspace Volume,claim jenkins-data-pvc,Read Only 不勾 升级后被 JCasC 重置为 Generic Ephemeral Volume,集群无默认 StorageClass 时 Pod 会永远 Pending
凭据 – 无 – Jenkins 在集群内运行,走 Pod 自身 ServiceAccount,连接测试显示 Connected to Kubernetes v1.28.15 即正常
jnlp 容器「运行的命令 / 命令参数」 全部清空 UI 预填的 sleep 9999999 会覆盖镜像入口命令;只有 maven/buildkit 等工具容器才需要 sleep 保活
jnlp 容器「总是拉取镜像」 取消勾选 tag 是固定版本号,IfNotPresent 即可,与升级前一致

五、验证

# 1. Pod 事件确认镜像拉取与挂卷正常
kubectl -n devops describe pod <agent-pod> | sed -n '/Events:/,$p'
# 预期:Scheduled → Pulling → Pulled → Created → Started,无 FailedMount/ErrImagePull

# 2. 确认 jnlp 容器未被 sleep 覆盖
kubectl -n devops get pod <agent-pod> \
  -o jsonpath='{.spec.containers[?(@.name=="jnlp")].command}'
# 预期:空输出(使用镜像默认入口 jenkins-agent)

# 3. controller 日志确认 agent 连接
kubectl -n devops logs jenkins-0 -c jenkins --tail=100 | grep -i "launch\|connect"
# 预期:不再出现 "Waiting for agent to connect (N/300)" 循环

验证结论:修复后构建 #18 的 agent Pod 正常连接,构建全流程(Maven 编译 → BuildKit 镜像推送 → ArgoCD 更新与同步)恢复,Pod 在构建完成后正常回收;后续其他 Job(Vue 流水线)也已恢复调度。

六、注意事项

  • 排查顺序post.failurecredentialsId 'null' 类报错 = 全局 environment {} 未生效 = 故障在 stages 之前,先去找日志里第一个真实报错,不要修表象。
  • Controller 与 agent 的 JDK 基线必须配套:升级 chart 大版本前,先查 controller 镜像的 JDK 基线(5.9.x 起为 JDK 21),agent 的 inbound-agent 镜像随之升级。业务构建容器(maven 等)的 JDK 与此无关。
  • JCasC 会覆盖 UI 配置:chart 默认每次 helm upgrade 重新套用 JCasC。cloud 配置(Pod 模板、工作空间卷、tunnel 地址)应固化到 chart values 的 controller.JCasC.configScripts,只在 UI 里改的配置迟早会丢。
  • UI 新建容器模板的 sleep 9999999 预填值:只适用于工具容器;jnlp 容器的命令/参数必须留空,否则 agent 永远 offline 且 Pod 状态是正常的 Running,极具迷惑性。
  • 镜像同步用 tag 不用 digestdocker pull image@sha256:... 拿到的是单架构 manifest,arm64 集群从 Harbor 拉取时可能架构不匹配;用 skopeo copy --all 或按 tag 同步保留多架构索引。
  • yaml """...""" 块内注释只能用 #:Groovy // 会被 snakeyaml 当作语法错误,podTemplate 步骤直接失败。
  • StatefulSet 升级不自动滚动重建helm upgrade 更新 StatefulSet 模板后,Pod 不会自动重建——需手动 kubectl delete pod 触发 StatefulSet 按新模板拉起新 Pod。这与 Deployment 的自动滚动更新行为不同,是 StatefulSet 的设计特性。升级完成后务必 kubectl get pod -o jsonpath='{.spec.containers[0].image}' 确认镜像已更新。
  • Jenkins controller Pod 的优雅关闭可能卡很久terminationGracePeriodSeconds + preStop 钩子会等待运行中构建结束。日常重启可用 kubectl delete pod --grace-period=0 --force 跳过等待(仅丢失 in-flight 构建状态,已落盘数据不受影响),但执行前需确认 PVC 已备份、不在构建窗口期。删除卡住时按 Ctrl+C 中断本地 kubectl 等待不会影响 server 端删除操作。

七、参考资料

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇