Nexus Repository:从 hosted/proxy/group 到可恢复 Blob Store
企业制品入口同时承担两种相反责任:内部版本一旦发布就不应被同坐标覆盖,外部依赖却需要被缓存、失效和重新拉取。Nexus Repository 用 hosted、proxy 与 group 把写入、上游代理和统一消费拆开,再由数据库保存元数据、Blob Store 保存二进制。仓库能返回 HTTP 200,只说明当前请求成功,不代表元数据和 blob 能在故障后共同恢复。
固定实验版本和部署形态
实验基线采用 Nexus Repository 3.93.1。官方下载安装页与目标版本 release notes 决定支持的 Java、数据库、平台和迁移路径;版本号只约束实验。当前发行对数据库和运行时要求变化较快,不能把旧版 OrientDB、任意 H2 容器或旧 Java 经验直接带入新部署。
开发实验可以使用官方镜像,但先记录精确版本与摘要。共享服务优先使用外部 PostgreSQL 和受支持 Blob Store;Community、Pro、HA、云存储与迁移能力按目标许可核对。
export NEXUS_IMAGE='sonatype/nexus3:3.93.1'
docker pull "$NEXUS_IMAGE"
docker image inspect "$NEXUS_IMAGE" --format '{{index .RepoDigests 0}}'
docker volume create nexus-data-lab
docker run -d --name nexus-lab -p 127.0.0.1:8081:8081 \
-v nexus-data-lab:/nexus-data "$NEXUS_IMAGE"
curl -fsS http://127.0.0.1:8081/service/rest/v1/status首次凭证、管理 Token 和测试账号不进入仓库。实验结束前导出仓库清单、Blob Store、任务和版本信息,避免只留下一个无法解释的 volume。
hosted、proxy 和 group 不是三个别名
Hosted 接受组织自己生产的制品,发布身份应只写指定格式和命名空间。Release 仓库禁止 redeploy,同坐标第二次上传必须由服务端拒绝;客户端先查询再上传存在竞态,不能替代服务端不可变策略。
Proxy 面向一个明确 upstream,缓存制品与元数据。远端 URL、企业代理、认证、negative cache、metadata max age、content max age 和 blocked/online 状态共同决定行为。上游故障时,已缓存对象可能继续可用,未缓存对象应明确失败;首页能打开不等于离线覆盖完整。
Group 按成员顺序提供统一读取入口。内部 hosted 应位于允许的内部命名空间,公共 proxy 不应有机会用同名组件抢先命中。Routing Rule、Content Selector、仓库成员顺序和命名策略共同防止 dependency confusion。
| 对象 | 写入者 | 状态所有者 | 主要失败证据 |
|---|---|---|---|
| hosted | 内部 CI | 数据库与 Blob Store | redeploy、权限、配额、blob 写失败 |
| proxy | Nexus 出站客户端 | 缓存元数据与 blob | upstream、负缓存、超时、路由规则 |
| group | 无直接写入 | 成员顺序与格式 facet | 错误命中、成员离线、元数据合并 |
用 Raw Hosted 证明不可变写入
创建 proof-release Raw Hosted,关闭 redeploy。实验账号只授予该仓库的 add/read 权限,不给 delete 与管理权限。
set -euo pipefail
BASE='https://repo.example.test/repository/proof-release'
PATH_KEY='demo/1.0/proof.txt'
printf 'proof-content-v1\n' > proof.txt
sha256sum proof.txt > proof.txt.sha256
curl --fail-with-body -u "$NEXUS_USER:$NEXUS_TOKEN" \
--upload-file proof.txt "$BASE/$PATH_KEY"
curl --fail-with-body -u "$NEXUS_USER:$NEXUS_TOKEN" \
"$BASE/$PATH_KEY" -o downloaded.txt
cmp proof.txt downloaded.txt随后修改本地字节并上传同一路径。预期是 4xx 和服务端拒绝记录;若返回 2xx,说明部署策略仍允许覆盖,应立即隔离实验仓库、恢复原字节并修正规则。curl 必须使用 --fail-with-body,否则 HTTP 403 也可能返回进程成功。
再用没有 delete 权限的账号调用删除接口,确认最小权限有效。成功上传、拒绝覆盖和拒绝删除三项证据一起保存,单看 UI 中的角色名不够。
Proxy 缓存要做断网反例
创建一个仅用于实验的 Maven 或 Raw Proxy,并加入 Group。联网时下载两个对象,只让一个进入缓存;随后把 Proxy 切到 blocked 或在隔离网络阻断上游。已缓存对象应继续按目标配置可读,未缓存对象必须失败并留下 upstream/blocked 证据。
Negative cache 会保存“上游不存在”的判断。对象后来被发布到上游,Nexus 仍可能在缓存期限内返回 404。排障要检查 negative cache、metadata age、component age 与 invalidate 行为,不能无限重启服务。
Routing Rule 对上游请求路径生效,可阻止未批准 namespace 或主机范围。规则变更先在允许与拒绝样本上验证;错误规则会同时造成依赖不可用和安全放行。
数据库和 Blob Store 必须同点恢复
数据库保存组件、asset、仓库配置、权限、任务和 blob 引用;Blob Store 保存二进制及相关属性。只备份其中一侧会得到悬空 asset 或孤儿 blob。文件 Blob Store 要监控容量、inode、句柄和延迟,对象存储则增加 API 费用、网络、IAM 与兼容性责任。
Soft Quota 只告警,不阻止写入。真实磁盘耗尽仍会让上传、元数据提交和后台任务失败,因此容量保护需要宿主告警、增长预测和写入止损。数据库连接池、节点数与 PostgreSQL max_connections 一起计算,不能每个节点独立调大。
备份窗口记录 Nexus 版本、数据库一致性点、Blob Store 快照、配置与 Secret 恢复方式。恢复到隔离实例后,检查仓库清单、代表 hosted/proxy/group 读取、随机摘要、权限拒绝和任务状态。搜索或 browse 可重建不代表业务元数据可以丢失。
Cleanup 与 Compact 分开理解
Cleanup Policy 选择候选并绑定具体仓库,Admin - Cleanup repositories using their associated policies 先删除逻辑引用。Admin - Cleanup unused asset blobs 处理未使用 asset blob,Admin - Compact blob store 才回收 Blob Store 中可释放空间。任务名相似,不是一个动作。
策略先 Preview,导出坐标清单并保护仍被生产、回滚、法务或漏洞调查引用的版本。删除后保留软删除窗口,再 Compact。回收完成比较逻辑 asset 数、Blob Store 字节、任务日志和随机下载;只看到任务 Success 不能证明空间或依赖仍正确。
Proxy 缓存和 Hosted Release 的保留承诺不同。缓存可按重新获取成本淘汰,内部不可变制品必须服从发布与审计策略。不要把统一的“最后下载时间”规则套在全部仓库。
权限、HA、升级和退出
角色按格式、仓库、动作和 Content Selector 收窄。生产发布身份不能管理仓库,消费者不能上传,匿名访问只在明确公共范围开放。Proxy 出站需要 SSRF 防护、允许上游、企业代理和 TLS 信任。管理 API、Support ZIP、审计日志和 Blob Store 凭证属于高敏感资产。
Nexus HA 需要目标许可、外部 PostgreSQL 和受支持共享 Blob Store,不是把容器副本数改大。节点版本、配置和网络延迟要一致,后台任务并发与故障转移按目标版本验证。跨站点优先单一发布源和受控复制,双向写入会制造同坐标不同字节与删除冲突。
升级先审查跨版本路径、Java/数据库变化、插件兼容和修复任务风险。在数据库与 Blob Store 同点副本上演练迁移,验证代表格式和不可变策略。Schema 已迁移后只回退镜像通常无效;失败时从升级前恢复点重建。
退出 Nexus 时冻结发布,导出仓库、权限、任务和配置,按坐标与摘要迁移 hosted 制品,再为 proxy 明确“迁移缓存还是重新预热”。新仓库验证读取、发布拒绝覆盖、权限和回滚版本后再切换。若目标是 Artifactory,参考 JFrog Artifactory 主文,不能只把 Group URL 换成 Virtual URL。
