Sonatype Nexus 3 从 H2 版本(latest) 降级至 OrientDB 版本(3.68.1) 的完整技术方案
# 实战指南:Sonatype Nexus 3 从 H2 版本(latest) 降级至 OrientDB 版本(3.68.1) 的完整技术方案
# 1. 背景与挑战
在容器化部署 Sonatype Nexus Repository Manager 3 时,许多运维人员习惯使用 sonatype/nexus3:latest 标签。然而,Nexus 3 在近期(3.69.0+ 及 3.70.0+)进行了底层的重大架构变革,将默认数据库从 OrientDB 迁移到了 H2。
这就导致了一个严峻的问题:一旦容器自动升级到 latest 版本(H2架构),数据文件结构将被修改。此时若直接将镜像标签回滚至旧版本(如 3.68.1),服务将因无法识别新版数据库 Schema 而启动失败。
本文将详细介绍如何在无法直接回滚的情况下,通过 双实例并行迁移(Side-by-Side Migration) 的方式,安全地将数据“降级”迁移回长期稳定版 3.68.1。
# 2. 核心原理:为什么不能原地降级?
Nexus 3 的版本分水岭在于数据库架构:
- 3.68.1 及以下:使用 OrientDB(嵌入式图数据库)。
- 3.69.0 及以上:默认使用 H2(嵌入式关系型数据库),并支持迁移至 PostgreSQL。
升级过程包含不可逆的数据库 Schema 变更。因此,唯一的“降级”路径是:部署一个新的 3.68.1 实例,通过 API 或客户端工具将数据从高版本实例“搬运”到低版本实例。
# 3. 环境准备:部署目标实例 (Target)
我们需要启动一个新的 Nexus 3.68.1 容器。假设源实例(Source, latest版)运行在端口 15581,我们将新实例部署在 15582。
# 3.1 目录权限修正(关键)
Nexus 容器内部使用 UID 200 运行,宿主机挂载目录必须赋予对应权限,否则会报 Permission Denied。
mkdir -p /opt/nexus3.68/data
chown -R 200:200 /opt/nexus3.68/data
2
3
# 3.2 Docker Compose 配置
创建 docker-compose.yml,锁定版本为 3.68.1(OrientDB 时代的最终稳定版):
version: '3'
services:
nexus:
image: sonatype/nexus3:3.68.1
container_name: nexus3-target
restart: always
mem_limit: 4g
environment:
- "INSTALL4J_ADD_VM_PARAMS=-Xms2g -Xmx2g -XX:MaxDirectMemorySize=2g"
ports:
- "15582:8081"
volumes:
- ./data:/nexus-data
ulimits:
nofile:
soft: 65536
hard: 65536
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 4. 数据迁移方案
针对不同类型的仓库,需采用不同的迁移策略。
# 4.1 Maven / NPM / Raw / PyPI 仓库 (Shell 流式脚本)
对于文件型资产,我们使用 Shell 脚本结合 curl 和 jq 进行流式传输(无需下载到本地磁盘)。
脚本痛点与修正:
在编写 Shell 脚本时,若密码包含特殊字符(如 $, !, @),必须使用单引号包裹,否则 Shell 会尝试进行变量替换导致认证失败(HTTP 401)。
通用迁移脚本 (migrate_assets.sh):
#!/bin/bash
# === 配置区域 ===
# 必须使用单引号 '' 包裹密码,防止特殊字符被 Shell 解析
SOURCE_HOST="http://192.168.1.10:15581"
SOURCE_USER="admin"
SOURCE_PASS='Your$Complex!Pass'
SOURCE_REPO="maven-releases"
TARGET_HOST="http://192.168.1.10:15582"
TARGET_USER="admin"
TARGET_PASS='Your$Complex!Pass'
TARGET_REPO="maven-releases"
# ===============
if ! command -v jq &> /dev/null; then
echo "Error: jq is required."
exit 1
fi
CONTINUATION_TOKEN=""
while true; do
# 构造分页 API 请求
API_URL="$SOURCE_HOST/service/rest/v1/assets?repository=$SOURCE_REPO"
if [ -n "$CONTINUATION_TOKEN" ] && [ "$CONTINUATION_TOKEN" != "null" ]; then
API_URL="${API_URL}&continuationToken=${CONTINUATION_TOKEN}"
fi
echo "Fetching list from: $API_URL"
# 获取资产列表 JSON
RESPONSE=$(curl -s -f -u "$SOURCE_USER:$SOURCE_PASS" "$API_URL")
if [ $? -ne 0 ]; then
echo "❌ Authentication Failed or Network Error."
exit 1
fi
# 解析 JSON 并执行流式传输
echo "$RESPONSE" | jq -r '.items[] | "\(.path)|\(.downloadUrl)"' | while IFS='|' read -r ASSET_PATH DOWNLOAD_URL; do
if [[ "$ASSET_PATH" == *".index"* ]] || [[ "$ASSET_PATH" == *"maven-metadata"* ]]; then
continue # 跳过元数据,建议迁移后重建
fi
echo "Migrating: $ASSET_PATH"
TARGET_UPLOAD_URL="$TARGET_HOST/repository/$TARGET_REPO/$ASSET_PATH"
# 管道操作:Source下载流 -> Target上传流
curl -s -f -u "$SOURCE_USER:$SOURCE_PASS" "$DOWNLOAD_URL" | \
curl -s -o /dev/null -w "%{http_code}" -u "$TARGET_USER:$TARGET_PASS" -T - "$TARGET_UPLOAD_URL"
echo "" # 换行
done
# 处理分页
CONTINUATION_TOKEN=$(echo "$RESPONSE" | jq -r '.continuationToken')
if [ -z "$CONTINUATION_TOKEN" ] || [ "$CONTINUATION_TOKEN" == "null" ]; then
break
fi
done
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
56
57
58
59
60
61
# 4.2 Docker 镜像仓库
Docker 镜像无法通过上述文件方式迁移,必须保留 Layer 及其元数据结构。需使用客户端脚本:
- Login: 同时登录 Source 和 Target 仓库。
- Pull: 从 Source 拉取镜像。
- Tag: 修改 Tag 指向 Target 地址。
- Push: 推送到 Target。
# 5. 常见故障排查 (Troubleshooting)
在实施过程中,以下三个问题最为高频:
# Q1: 执行脚本提示“无法获取资产列表”或 401 错误
- 现象:确认账号密码正确,但脚本一直报错。
- 原因:Shell 脚本中密码使用了双引号
SOURCE_PASS="Pass$word"。Bash 会试图解释$word变量,导致发送的密码错误。 - 解决:将脚本中的密码定义改为单引号:
SOURCE_PASS='Pass$word'。
# Q2: 浏览器访问一直卡在 "Loading Nexus..." 或 "Loading baseapp-prod.js"
- 现象:切换版本后,Web 界面无限加载,F12 控制台报错
Uncaught SyntaxError。 - 原因:Nexus 是单页应用 (SPA)。浏览器缓存了
latest版本的前端 JS 文件,但请求的是3.68.1的后端 API,版本不匹配导致前端崩溃。 - 解决:
- 强制刷新:
Ctrl + F5。 - 无痕模式:使用浏览器无痕模式访问(最推荐)。
- 等待启动:确保
docker logs显示Started Sonatype Nexus OSS后再刷新。
# Q3: 界面提示 "100,000 Components Threshold" 警告
- 现象:Usage 页面显示红色的阈值进度条。
- 解读:在 OSS 免费版中,这只是性能建议(Soft Limit)。它提示在使用嵌入式数据库(OrientDB/H2)时,建议组件数控制在 10万以内以保证最佳性能。
- 结论:这不是硬性限制,不会阻断使用,忽略即可。
# 6. 最佳实践总结
- **生产环境禁用
:latest**:在docker-compose.yml中始终锁定具体的版本号(如3.68.1或3.70.1-java17),避免意外升级。 - 数据备份:在执行任何升级或迁移操作前,通过 Nexus 自带的 Task ("Admin - Export databases for backup") 进行备份。
- 版本选择:
- 追求稳定且不想折腾数据库:选择 3.68.1 (OrientDB 最终版)。
- 新项目启动:选择 3.70+ (H2 数据库),性能更好,面向未来。