本文来自一次真实迁移。服务器、地址、域名、仓库数量、组件数量和企业配置均已脱敏。示例 Secret 和密码均为占位符。
系列导航:Kubernetes 重建迁移教程系列。建议先完成“在 Kubernetes 从零部署 Nexus Repository 3”教程。
一套运行多年的 Nexus Repository 2,通常已被 Maven、Jenkins、开发机和发布脚本深度依赖。真正困难的不是启动 Nexus3,而是在不丢制品、不破坏权限、不过度修改客户端的前提下迁移。
这次迁移的最终目标是:
- Nexus2 的仓库、用户、角色和配置进入 Nexus3。
- Nexus3 以单副本 StatefulSet 运行在 Kubernetes。
- Blob Store 使用独立持久卷。
- 元数据从 H2 迁移到 PostgreSQL。
- 保留原有域名、VIP、端口和
/nexus上下文路径。 - 旧 Maven URL 在过渡期继续兼容。
- 迁移失败时能够快速切回 Nexus2。
Sonatype 已在 2025 年 6 月 30 日停止维护 Nexus Repository 2,不再提供新功能和缺陷修复,并建议尽快迁移到 Nexus3。参见 Nexus Repository 2 Version Status。
最终迁移链路
flowchart LR
A["Nexus2 最终版<br/>旧服务器"] -->|"官方 Upgrade"| B["Nexus3 中转版<br/>H2 / Kubernetes"]
B -->|"原地版本升级"| C["Nexus3 目标版<br/>H2 / Kubernetes"]
C -->|"Database Migrator"| D["Nexus3 目标版<br/>PostgreSQL / Kubernetes"]
E["Maven / Jenkins"] --> F["原域名或 VIP"]
F --> D
D --> G["Blob Store PV"]
D --> H["PostgreSQL"]
最关键的设计是把迁移拆成两个阶段:
- Nexus2 → Nexus3,并继续使用 H2。
- Nexus3 升到目标版本后,再做 H2 → PostgreSQL。
不要把仓库转换、版本升级、数据库迁移和流量切换混成一个步骤。
为什么 Nexus2 数据目录不能直接挂给 Nexus3
Nexus3 重新设计了组件元数据和 Blob Store。Nexus2 的工作目录不是 Nexus3 可直接读取的数据卷。
正确路径是:
- 把 Nexus2 升到官方支持的最终版本。
- 使用 Nexus2 Upgrade Agent 和 Nexus3 Upgrade Capability。
- 迁移仓库内容和服务配置。
- 等同步完成后执行 Finalize。
官方流程见 Upgrade from Nexus Repository 2。中转版本与目标版本必须根据执行时的官方升级矩阵确认,不能把某次成功组合当作永久规则。
迁移前盘点
| 类别 | 需要记录 |
|---|---|
| 运行时 | Nexus、Java、操作系统、安装目录和工作目录 |
| 仓库 | Hosted、Proxy、Group、格式、远程地址和成员顺序 |
| 数据 | 组件、资产、Blob 容量和 checksum 抽样 |
| 权限 | 用户、角色、Realm、匿名访问 |
| 客户端 | Maven settings、Jenkins Credential、上传 URL |
| 网络 | 域名、VIP、端口、上下文路径、代理规则 |
| 任务 | 清理、索引、备份任务 |
| 扩展 | 插件、脚本、自定义 Realm |
尤其要扫描 Nexus2 旧路径:
/nexus/content/repositories/releases/
/nexus/content/groups/public/
Nexus3 的现代路径通常是:
/nexus/repository/releases/
/nexus/repository/public/
客户端无法一次性全部修改时,应在外层代理配置明确的重写规则,并为旧路径设置移除日期。
完整备份 Nexus2
至少保留:
- Nexus2 程序目录。
- Nexus2 工作目录。
- 仓库存储和校验记录。
在写入冻结后执行文件系统备份:
systemctl stop nexus
tar -C /opt -czf /backup/nexus2-program.tar.gz nexus-2
tar -C /data -czf /backup/nexus2-work.tar.gz sonatype-work
sha256sum /backup/nexus2-*.tar.gz > /backup/SHA256SUMS
运行中直接复制目录,可能产生元数据与制品不一致的备份。
Kubernetes 中的存储设计
Nexus 的 Blob Store 读写频繁。本次使用独立本地 PV,并设置:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nexus-data
spec:
capacity:
storage: <capacity>
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
storageClassName: nexus-local
local:
path: /data/nexus
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values: [repo-node]
Retain 很重要:误删 StatefulSet 或 PVC 时,历史制品不会随之被自动销毁。
本地 PV 会把 Pod 固定在特定节点,因此它不是高可用方案。对单副本社区版而言,这个取舍是用更清晰的故障边界换取更低的存储复杂度。
保持上下文路径和健康检查
为减少客户端变更,继续使用 /nexus:
application-port=8081
application-host=0.0.0.0
nexus-context-path=/nexus
nexus.datastore.enabled=true
探针使用状态接口,而不是首页 HTML:
startupProbe:
httpGet:
path: /nexus/service/rest/v1/status
port: http
failureThreshold: 60
periodSeconds: 10
timeoutSeconds: 5
首次升级和索引重建可能持续数分钟。connection refused 只表示进程尚未监听端口,真正原因要看 nexus.log。
Nexus2 → Nexus3 的正确顺序
先让 Nexus3 使用 H2:
- 创建 Upgrade Agent / Upgrade Capability。
- 建立连接并执行预检查。
- 迁移 repository content。
- 迁移 server configuration。
- 等同步队列归零。
- 冻结 Nexus2 上传。
- 执行 Finalize 和 Done。
Finalize 前至少验证 Hosted 最新版本、Proxy 拉取、Group 顺序、匿名访问和真实 Maven 构建。
Upgrade Wizard 不是双向同步系统。迁移开始后,不要继续修改 Nexus2 的用户、角色和仓库结构。
先升级 H2,再迁 PostgreSQL
完成 Nexus2 迁移后,仍使用 H2 将 Nexus3 升到目标版本。升级前同时备份 H2 和 Blob Store,确保两者来自同一恢复点。
启动新版本后等待内部 schema、upgrade、browse 和 search 任务结束。Pod Ready 不等于升级任务完成,任务运行期间不要反复重启实例。
准备 PostgreSQL
CREATE ROLE nexus LOGIN PASSWORD '<random-password>';
CREATE DATABASE nexus OWNER nexus;
\connect nexus
CREATE SCHEMA nexus AUTHORIZATION nexus;
GRANT USAGE, CREATE ON SCHEMA nexus TO nexus;
ALTER ROLE nexus SET search_path TO nexus;
密码仅保存在 Kubernetes Secret 中:
kubectl -n repository create secret generic nexus-db \
--from-literal=username=nexus \
--from-literal=password="$(openssl rand -base64 36)"
Base64 是编码,不是加密。Secret 的明文和输出不能进入 Git 或 CI 日志。
使用 Database Migrator
迁移器版本必须与 Nexus 目标版本匹配,并校验 SHA256。官方说明见 Migrating to a New Database。
sha256sum nexus-db-migrator-<version>.jar
从 Nexus Repository 3.93.0 起,自建部署至少需要 Java 25,执行迁移器前也要确认运行时要求。参见 2026 Release Notes。
迁移前完全停止 Nexus:
kubectl -n repository scale statefulset/nexus --replicas=0
kubectl -n repository wait --for=delete pod/nexus-0 --timeout=180s
若密码包含 +、&、%、?、# 等字符,写入 JDBC URL 前必须编码,而且禁止打印完整 URL。
迁移 Job Complete 不是唯一标准,还要检查最终汇总是否存在 skipped 或 failed 记录。失败后不能直接对已有数据的 schema 重跑,应先定位原因并恢复到干净的目标库。
迁移完成后,在 Nexus 仍停止时执行:
VACUUM (FULL, ANALYZE);
连接 PostgreSQL 启动最终版本
env:
- name: NEXUS_DATASTORE_NEXUS_JDBCURL
value: jdbc:postgresql://postgresql.repository.svc.cluster.local:5432/nexus?currentSchema=nexus
- name: NEXUS_DATASTORE_NEXUS_USERNAME
value: nexus
- name: NEXUS_DATASTORE_NEXUS_PASSWORD
valueFrom:
secretKeyRef:
name: nexus-db
key: password
启动后等待 Browse/Search 重建任务完成,再验证搜索、下载和上传。
五层验收
- Pod 与日志:Ready、重启稳定、无数据库和 Blob 错误。
- 状态接口:
/nexus/service/rest/v1/status正常。 - 仓库:Hosted、Proxy、Group 各自验证。
- 真实构建:执行 Maven 构建,覆盖 POM、metadata、checksum 和认证。
- 上传:用临时版本验证 Hosted 上传并清理。
只用浏览器打开管理页面,无法证明 Maven 构建链路正常。
VIP 无感切换与回滚
大量历史客户端依赖固定地址时,可以把旧地址作为 VIP 切到新节点。
切换前必须满足:
- 旧 Nexus2 已停止。
- 旧节点不再持有 VIP。
- 新 Nexus3 已通过内部地址验证。
- 添加 VIP 后重新执行下载、上传和构建。
任何时刻都不能让两台机器同时持有同一个 IP,否则会出现 ARP 抖动和随机落点。
回滚顺序:冻结新上传、移除新节点 VIP、确认网络收敛、启动旧 Nexus2、恢复旧入口。新环境一旦接受写入,回滚还必须处理两端数据差异。
社区版用量治理
截至本文发布时,Nexus Repository Community Edition 的使用限制为 40,000 个组件或每天 100,000 个请求;超限时不能新增组件,指标更新可能延迟约一小时。参见 Usage Center。
治理原则:
- 优先清理长期未使用的 Proxy 缓存。
- Hosted releases、snapshots 和内部 third-party 使用独立保留策略。
- 清理前备份 PostgreSQL。
- 先软删除,过观察期后再 Compact Blob Store。
- 不要仅凭页面短时间未刷新就判断清理失败。
Proxy 可从上游重新拉取,Hosted 很可能是企业唯一来源,二者不能使用同一清理策略。
最容易踩的坑
- 在错误版本上迁数据库。
- Nexus 未停止就运行 Migrator。
- 只授权数据库,没有授权 schema。
- JDBC 密码未编码。
- Job Complete 后立即重启,忽略索引任务。
- 只备份数据库,不备份 Blob Store。
- 只验证管理页面。
- 提前销毁旧服务器和恢复点。
总结
Nexus2 升级 Nexus3 的核心不是“换镜像”,而是控制四个边界:
- 内容和元数据通过支持的路径转换。
- 版本升级和数据库迁移分阶段完成。
- 数据库、Blob Store 与客户端访问路径共同验证。
- 每个不可逆步骤之前都有独立恢复点。
坚持先备份、分阶段、逐层验证、最后切流量,才能把停机集中在最终冻结、数据库迁移和入口切换窗口,而不是让整个项目长期处于不可用状态。
DISCUSSION
讨论与反馈