本文只保留通用执行逻辑。地址、节点、域名、容量和 Secret 均为占位符,执行时必须按官方版本矩阵和自己的恢复演练结果重新确认。

系列导航:Kubernetes 重建迁移教程系列。本篇用于正式迁移窗口中的逐项执行与验收。

上一篇文章解释了 Nexus2 → Nexus3 → PostgreSQL 的整体设计。这一篇把迁移收敛成一份执行清单,重点回答“每一步什么时候能继续”。

固定设计

迁移前冻结以下决策:

  • Nexus2 已升级到官方支持的最终版本。
  • Nexus3 使用经过验证的中转版本接收 Upgrade Wizard 数据。
  • Nexus3 在 H2 模式升级到最终目标版本。
  • Database Migrator 与目标版本严格匹配。
  • Nexus 和迁移器 Java 版本符合目标版本要求。
  • Blob Store 使用独立持久卷,回收策略为 Retain。
  • PostgreSQL 使用独立数据库、角色和 schema。
  • /nexus 上下文路径保持不变。
  • 旧域名或 VIP 只在最终验证后切换。

不可跳过的顺序

Nexus2 最终版与完整备份
→ Nexus3 中转版 H2
→ Upgrade Wizard + Finalize
→ Nexus3 目标版 H2
→ H2 备份与 Blob Store 恢复点
→ 停止 Nexus
→ Database Migrator
→ PostgreSQL VACUUM
→ Nexus3 目标版 PostgreSQL
→ Browse/Search 完成
→ 真实构建与上传
→ VIP/域名切换

不要在中转版上提前迁移 PostgreSQL,也不要让 Nexus 在数据库迁移期间运行。

一、创建 Kubernetes 基础资源

先创建:

  1. StorageClass、PV、PVC。
  2. 非敏感 ConfigMap。
  3. Nexus Service。
  4. PostgreSQL 数据库和 schema 初始化 Job。
  5. 数据库 Secret。

Secret 使用命令创建,不写 YAML 明文:

kubectl -n repository create secret generic nexus-db \
  --from-literal=username=nexus \
  --from-literal=password="$(openssl rand -base64 36)"

存储验收:

  • PVC Bound。
  • PV 为 Retain。
  • 目标节点目录容量充足。
  • Nexus 容器 UID/GID 有写权限。
  • Blob Store 与数据库临时文件空间需分别评估。

二、以 H2 启动中转版 Nexus3

kubectl -n repository apply -f nexus3-migration.yaml
kubectl -n repository rollout status statefulset/nexus --timeout=15m

curl -fsS \
  http://<migration-address>:8081/nexus/service/rest/v1/status

启动探针建议使用状态接口,并为首次升级预留足够时间:

startupProbe:
  httpGet:
    path: /nexus/service/rest/v1/status
    port: http
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 60

如果探针提示 connection refused,先检查 nexus.log,不要无限增加等待时间。

三、执行 Nexus2 Upgrade Wizard

  1. 备份 Nexus2 程序和工作目录。
  2. 在 Nexus2 上创建 Upgrade Agent。
  3. 在 Nexus3 上创建 Upgrade Capability。
  4. 迁移 repository content。
  5. 迁移 server configuration。
  6. 等待同步队列归零。
  7. 冻结上传并停止 Nexus2。
  8. 执行 Finalize 和 Done。

Finalize 前必须确认:

  • Hosted 最新制品存在。
  • Proxy 能重新拉取。
  • Group 成员和顺序正确。
  • 用户、角色和匿名访问正确。
  • 现代 URL 与需要兼容的旧 URL 均可用。
  • 至少一次真实 Maven 构建通过。

四、升级目标版本,继续使用 H2

先创建 H2 官方备份并记录 SHA256,同时备份 Blob Store:

sha256sum /backup/nexus-h2-* /backup/nexus-blob-* > /backup/SHA256SUMS

再升级 StatefulSet 镜像。启动后等待内部 schema 和 upgrade 任务全部完成,再重新生成 H2 恢复点。

禁止在内部任务运行时重启 Pod。

五、准备 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;

验证应用角色可以:

  • 连接数据库。
  • 在目标 schema 创建和删除临时表。
  • 使用预期的 search_path。

如果只做数据库级授权、不授予 schema 的 USAGE 和 CREATE,迁移器仍会失败。

六、运行 Database Migrator

下载与 Nexus 目标版本匹配的迁移器并校验:

curl -fLO https://download.sonatype.com/nexus/nxrm3-migrator/nexus-db-migrator-<version>.jar
curl -fLO https://download.sonatype.com/nexus/nxrm3-migrator/nexus-db-migrator-<version>.jar.sha256

test "$(sha256sum nexus-db-migrator-<version>.jar | awk '{print $1}')" = \
  "$(cat nexus-db-migrator-<version>.jar.sha256)"

完全停止 Nexus:

kubectl -n repository scale statefulset/nexus --replicas=0
kubectl -n repository wait --for=delete pod/nexus-0 --timeout=180s

迁移器从受控 Secret 中读取连接参数。若密码包含 URL 保留字符,先做 URL 编码,同时不要把 JDBC URL 打到日志。

继续条件:

  • Job 状态为 Complete。
  • 迁移汇总中 skipped 为 0。
  • 没有 failed、constraint、schema permission 或认证错误。
  • PostgreSQL 表、组件、资产和 Blob 引用数量与汇总一致。

迁移失败时,不要直接对已有目标 schema 重跑迁移器。先定位原因,再恢复或重建确认干净的空 schema。

七、整理 PostgreSQL 并启动最终版本

Nexus 仍停止时执行:

VACUUM (FULL, ANALYZE);

然后用最终 StatefulSet 连接 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

启动后等待:

  • Rebuild repository browse
  • Rebuild repository search

这些一次性任务完成前,不要重启实例或切流量。

八、验证矩阵

运行时

  • Pod Ready,Restart 次数不增长。
  • 状态接口正常。
  • 日志无数据库、Blob、索引错误。
  • Nexus 和 Java 版本符合计划。

仓库

  • Hosted releases 可下载。
  • Hosted snapshots 可下载。
  • Third-party 可下载。
  • Proxy 命中和未命中都成功。
  • Group 按成员顺序解析。
  • Search 能找到已知组件。

权限

  • 用户、角色和 Realm 正常。
  • 匿名下载符合预期。
  • Hosted 上传权限正确。

客户端

  • Maven 使用真实 settings 完整构建。
  • Jenkins 可下载依赖。
  • 使用临时版本上传并清理。
  • 需要保留的旧 /content/ URL 已验证或重写。

九、入口切换

切换前:

  • 旧 Nexus2 已停止。
  • 旧节点不再持有 VIP。
  • 新 Nexus3 已通过内部地址验证。
  • 备份和回滚命令仍可用。

切换后立即验证状态、下载、上传和真实构建。任何时刻都不能让两台机器同时持有同一 VIP。

回滚:

  1. 冻结新 Nexus3 上传。
  2. 从新节点移除 VIP。
  3. 确认 ARP 和路由已经收敛。
  4. 启动旧 Nexus2。
  5. 恢复旧入口。

十、凭据与备份收尾

  • 删除只在迁移期使用的 Secret。
  • 如果密码可能进入日志,轮换 PostgreSQL 角色和 Kubernetes Secret。
  • 两边密码必须同时更新。
  • 保留 Nexus2、两个 H2 恢复点、PostgreSQL 备份和 Blob Store 备份。
  • 经过稳定观察和恢复演练后,才能释放旧服务器。

社区版组件数维护

对 Proxy 和 Hosted 分别设置策略:

  • Proxy:删除长期未使用的缓存组件。
  • Hosted:按发布和合规要求保留。
  • 清理前执行 PostgreSQL 备份。
  • 先软删除,观察后再对 Blob Store 执行 Compact。

Usage Center 可能延迟刷新,历史峰值也不会立即下降。不要因为页面短时间内仍显示旧数量而反复执行清理。

完成定义

迁移器无跳过
+ PostgreSQL 数据与 Blob 引用一致
+ Browse/Search 完成
+ 仓库和权限正确
+ Maven/Jenkins 下载上传通过
+ 入口切换成功
+ 回滚和备份仍可用

一份好的 Runbook 不追求“命令越多越专业”,而是让每个不可逆步骤都有清晰的输入、继续条件和回滚边界。