JumpServer 升级到 v5:Lion 合并与 Chen 探针踩坑
2026-09-24 凌晨,把 Kubernetes 上的 JumpServer 社区版从 v4.10.16-ce 升到了 v5.0.0-ce。先升级到 v4.10.19 LTS,完成备份和验收,再继续 v5。最后运行的是五个组件、每个两副本,Helm revision 8。
中间遇到两个与 v5 直接相关的问题:官方发布的 Helm Chart 仍然引用不存在的 lion:v5.0.0-ce;Chen 沿用旧健康检查路径,导致容器反复重启。
这篇记录使用的来源、实际执行顺序,以及最后留下的两项 values 覆盖。文中的版本和状态以这次维护窗口为准,命令中的配置文件名已泛化,不包含生产凭据。
起点:先确认运行版本
原来有六个 Deployment:Core、Celery、Chen、KoKo、Lion、Web,每个都是 2/2 Ready。Core 和 Celery 使用同一个 Core 镜像,其他组件使用各自的镜像,标签统一为 v4.10.16-ce。
数据库是外置 PostgreSQL,缓存是 Redis;应用数据卷使用 CephFS。这次只升级 JumpServer,没有同时升级数据库、Redis 或存储集群。
我先交叉检查了三处信息:
helm list -n jumpserver -o json
kubectl -n jumpserver get deployments \ -o custom-columns='NAME:.metadata.name,READY:.status.readyReplicas,DESIRED:.spec.replicas,IMAGES:.spec.template.spec.containers[*].image'
kubectl -n jumpserver get pods \ -o custom-columns='NAME:.metadata.name,IMAGES:.spec.containers[*].image,IMAGE_IDS:.status.containerStatuses[*].imageID'本地 Chart 的 version、appVersion,Helm release 元数据,以及实际 Pod 的镜像都指向 v4.10.16。只读 Chart.yaml 不足以确认线上版本:覆盖文件、上一次发布失败或未完成的滚动更新,都可能让它与运行状态不同。
这次选择先升 LTS,还有一个明确原因:官方 9 月 9 日的安全公告指出,v4.0.0 至 v4.10.19 之前的版本受到 Access Key 越权泄露问题影响,社区版也在范围内,v4.10.19 是修复版本。官方安全公告
先 LTS、再 v5 是本次维护安排,并不是所有 v4 升级都必须经过这一站。官方的 v5 升级说明支持从 v4 迁移到 v5。
官方发布包与 main 有差异
Chart 来自 jumpserver/helm-charts,但实际部署使用的是 jumpserver-v5.0.0 固定发布包。
当时核对到的来源关系如下:
| 来源 | 对应版本或提交 | 本次用途 |
|---|---|---|
| v4.10.19 Chart 发布包 | jumpserver-v4.10.19 | 第一阶段升级 |
| v5.0.0 Chart 发布包 | ef64070cca6ba77e72f49ec915060b3f5311cbf0 | 实际部署的 v5 模板 |
| 复盘时核对的 main | 6b4f34ecbdb92247e17214f4cd2f712e38165cfc | 比较尚未进入上述发布包的修复 |
v5 包的 SHA256 与官方 Helm 仓库索引一致:
c6bc889c21642c05542952d69660b416badf456c724dc036aae29e8851c00866这个包相对 v4.10.19 只修改了 Chart 版本和应用版本。升级完成后再检查 main,发现它早在 09-20 就多了一个 适配 KoKo 图形会话的提交:删除 Lion 模板及旧代理路由,把字体平滑配置移到 KoKo,并调整 video PVC 模板。
因此,浏览 main 看到的内容,不能直接当成下载到的 v5.0.0 发布包内容。本次没有切换到该 main 快照,而是保留发布包的官方模板,通过独立 values 文件修正实际遇到的问题。
停机前,把镜像和恢复点准备好
升级前重新检查了 Kubernetes API、节点、etcd、Ceph、PostgreSQL 复制和 Redis 主从状态,也确认没有活跃 JumpServer 会话。
新镜像先拉到三个节点,再逐项比较运行时缓存中的 digest 与官方镜像 digest。这个步骤在本次确实起了作用:一个节点访问 Harbor 时出现过 TLS handshake timeout,重试后成功。如果把下载留到停机之后,维护窗口就会包含这段不确定的等待。
备份按用途分开保存:
| 备份 | 保护的内容 | 本次校验 |
|---|---|---|
| etcd 快照 | Kubernetes API 状态 | 快照状态、大小、revision、双份 SHA256 |
| 原 Chart 和生产覆盖文件 | 版本及部署配置 | 两份归档 SHA256 一致 |
| PostgreSQL custom dump | JumpServer 数据库 | 目录检查、完整解析、双份 SHA256 |
| PostgreSQL globals | 角色等全局对象 | 使用 --no-role-passwords,不导出角色密码 |
| Redis RDB | 缓存及运行状态快照 | 原生导出、文件头检查、双份 SHA256 |
| 五个应用卷归档 | Core、Chen、KoKo、Lion 数据及 Web 日志 | 完整读取归档、双份 SHA256 |
etcd 快照不能代替数据库和文件备份。备份文件保存在两台节点的受限目录中,正文只保留方法;原始 values、渲染结果、日志和归档没有放进博客仓库。
最后一轮数据库和文件备份是在停止业务组件之后进行的。原部署的 Core、Celery selector 存在重叠,停止和验收按明确的 Deployment 名称、Pod 所属关系处理,不能把一个宽泛标签选择器当成组件边界。
pg_restore 结束了,客户端却还在等
第一阶段备份校验时,还碰到一次容易误判为数据库卡住的问题:
kubectl -n jumpserver exec -i "$postgres_pod" -c postgres -- \ pg_restore --list < jumpserver.dump > archive-list.txt容器里的 pg_restore 已经结束,目录清单也已生成,但本地 kubectl exec -i 没有退出。检查发现,--list 只读取归档目录,输入流还有剩余内容。这次把剩余 stdin 消费完后,命令正常返回:
# postgres_pod 指向已核对的 PostgreSQL 实例kubectl -n jumpserver exec -i "$postgres_pod" -c postgres -- \ sh -c 'pg_restore --list; result=$?; cat >/dev/null; exit "$result"' \ < jumpserver.dump > archive-list.txt
kubectl -n jumpserver exec -i "$postgres_pod" -c postgres -- \ pg_restore --file=/dev/null < jumpserver.dump第一条保留了 pg_restore 的退出状态,第二条用于完整解析归档。这两项都不是数据库恢复演练。
这次等待发生在迁移前,保护流程先恢复了原组件的副本数。修正校验方式后,再停机生成最终备份,没有在状态不明时继续执行迁移。
第一阶段:升级到 v4.10.19 LTS
第一阶段使用官方 v4.10.19 Chart 和原生产覆盖文件。新 Chart 渲染结果与现网比较后,确认副本数、selector、环境变量、卷挂载和 PVC 规格保持一致。
正式发布前做过 helm lint、使用占位凭据的离线渲染和 Helm server dry-run。这里的 server dry-run 仍不会运行数据库迁移 hook,也不能证明新镜像里的每个 HTTP 路径都可用。
对生产配置渲染时还要注意:--hide-secret 隐藏的是 Secret 对象,Deployment 环境变量等位置仍可能含敏感值。这次完整结果留在受限文件中,只输出字段比较结果。
LTS 阶段新增 24 项 Django 迁移,迁移记录从 138 增到 162。Helm revision 6 部署成功,六个组件恢复 2/2 Ready,随后完成入口和稳定性检查。
这个恢复点保留下来。进入 v5 阶段时又重新备份了一次,避免把升级前的 v4.10.16 数据当成 v4.10.19 的恢复基线。
v5 的第一个问题:Lion 镜像不存在
预拉 v5 镜像时,Core、Chen、KoKo、Web 都能找到对应标签,唯独 jumpserver/lion:v5.0.0-ce 不存在。
继续核对官方安装器、镜像列表和项目 issue,维护者已经明确说明:Lion 的代码合并进了 KoKo,v5 不再有独立 Lion 组件。官方 issue #17554
这也能在 v5 KoKo 源码中交叉验证:图形连接路由位于 /koko/lion/;容器内还包含 guacd,监听本机 4822 端口。
因此没有继续尝试拉取一个不存在的镜像,也没有把旧 Lion 镜像重新打成 v5 标签。针对这个发布包,先加了一个覆盖:
lion: enabled: false渲染比较确认,独立 Lion 的 Deployment、Service 和 PVC 声明被移除,其余五个 Deployment 仍各两副本。旧 Lion PVC 已有 helm.sh/resource-policy: keep,升级后保留下来,没有删除数据卷。
停机前也检查了旧卷的数据目录:sessions、drive、replays、ftp_files、certs 均没有文件,所以这次没有录制或上传文件需要搬迁。这个结论只适用于本次现场;已有数据的环境还需要单独处理迁移,不能只保留旧卷就宣称数据路径完全兼容。
组件数量从六个变为五个,是 Lion 合并的结果。Windows 图形连接的验收重点,也随之变成了 KoKo 内置的图形服务。
v5 的第二个问题:Chen 被旧探针反复重启
第一轮 v5 发布后,Helm revision 7 显示 deployed,28 项数据库迁移完成,迁移记录从 162 增到 190。
但逐个请求 Pod 的健康路径时,Chen 出现了这样的响应链:
GET /chen → 302,Location: /chen/GET /chen/ → 404原 livenessProbe 仍检查 /chen。两个 Chen 实例随后各发生了至少两次重启。Chart 渲染正确、Helm 已完成,都没有覆盖到这个运行时变化。
查阅 v5 官方安装器的 Chen 配置,实际使用的检查地址是:
http://localhost:8082/chen/healthy直接请求两个 Pod 的该路径,均返回 200。于是把修正加入同一份覆盖文件,重新渲染后确认只有 Chen 的 liveness 路径发生变化,再发布 revision 8。
这次补发使用了 --no-hooks:数据库已经完成迁移,差异也限定在探针,不需要再次执行 pre-upgrade 数据库 Job。首次 v5 升级仍正常执行 hook,不能把这个参数当成通用升级选项。
Chen 还有一个验收细节:当前模板没有给它配置 readinessProbe。新 Pod 刚变成 Kubernetes Ready 时,HTTP 服务可能尚未监听。补发后的第一次 HTTP 检查就遇到过短暂的连接拒绝,等待启动后才通过。因此后续以真实健康接口和持续观察为准,没有把 Deployment Ready 当成全部业务验收。
最后保留的覆盖文件
官方模板没有直接修改。最终的 values-v5-upgrade.yaml 如下:
# v5 的图形连接已合并到 KoKolion: enabled: false
# v5 Chen 的实际健康检查路径chen: livenessProbe: httpGet: path: /chen/healthy生产覆盖文件继续保存原有数据库、Redis、凭据、镜像仓库和存储设置。这份非敏感覆盖单独保存,两层 values 一起使用:
# ./jumpserver 为校验过的官方 v5.0.0 Chart 解包目录# values-prod.yaml 为自己环境的生产配置,不使用上游默认值代替helm upgrade jumpserver ./jumpserver \ -n jumpserver \ --reset-values \ -f ./values-prod.yaml \ -f ./values-v5-upgrade.yaml \ --wait --wait-for-jobs --timeout 15m这条命令展示的是完成备份、差异核对之后的正式发布方式。上面两项覆盖针对本次固定发布包;以后换到已修复的 Chart,要重新比较渲染结果,再决定哪些覆盖可以移除。
本次没有使用自动 Helm rollback。数据库迁移已经改变持久数据时,启动旧镜像不等于恢复旧状态:需要结合升级前数据库、文件和配置恢复,而不是只看 Helm revision 是否退回去了。
最终状态与验证范围
两阶段的关键记录如下,时间均为 2026-09-24,UTC+8:
| 阶段 | 时间 | Helm revision | 数据库迁移 | 结果 |
|---|---|---|---|---|
| v4.10.16 → v4.10.19 LTS | 00:45:21 | 6 | 138 → 162,新增 24 项 | 六组件各 2/2 |
| v4.10.19 → v5.0.0 | 01:11:49 | 7 | 162 → 190,新增 28 项 | 随后发现 Chen 探针问题 |
| 修正 Chen liveness | 01:15:11 发起发布 | 8 | 未重复执行迁移 hook | 新 Chen 副本恢复 |
| 最终稳定性观察 | 01:16:10–01:19:13 | 8 | 无新增迁移 | 约 183 秒、7 次采样通过 |
最终检查到了这些结果:
- Core、Celery、Chen、KoKo、Web 五个 Deployment 均为
2/2 Ready,十个应用 Pod 的镜像 digest 与官方 v5 镜像一致;Core 实际代码版本为v5.0.0。 - 八个 HTTP 组件实例健康检查通过。两只 KoKo 的
/koko/lion/health/返回 200,guacd 均监听 4822。 - 登录页、
/api/health/和图形服务健康入口持续返回 200;管理页/ui/、终端页/luna/及抽查的静态资源可正常加载。 - 原九个 PVC 的 UID 和 PV 绑定保持,原凭据 Secret 的 UID/resourceVersion 不变。
- 普通用户、资产、资产账号、资产授权数量保持。组件启动会新增服务账号,因此总用户数有增长,没有把它写成“所有表行数完全不变”。
- PostgreSQL 两实例健康,复制追平;Redis 主从正常。Kubernetes API、etcd、Ceph 与维护前基线一致。
- 一个 Web 容器启动时重启过一次;Chen 修复后的观察窗口内没有新增重启,近 90 秒日志抽查没有 ERROR、FATAL、CRITICAL 或 Traceback。
这里仍有明确的验证边界:没有执行完整数据库隔离恢复演练,也没有用真实授权账号逐一完成 SSH、RDP 和数据库交互登录。三分钟观察证明的是这段窗口内的运行状态,不能替代长时间负载观察或完整业务回归。
这次最有价值的差异,都出现在 Chart 以外:镜像是否实际发布、组件是否合并,以及新进程究竟提供了哪个健康接口。把这些与发布包、运行镜像和真实请求放在一起核对,才能解释为什么 Helm 已经 deployed,维护工作却还没有结束。