ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从Nexus迁到Hadess:制品仓库平滑迁移五步实践

从Nexus迁到Hadess:制品仓库平滑迁移五步实践 从Nexus迁到Hadess我把过程拆成了这五步我差不多在Nexus上泡了六年从Sonatype Nexus 2一直用到Nexus 3管理过几千个Maven、npm和Docker制品。说实话Nexus够稳定但它的界面操作、权限模型和元数据导出能力在大规模迁移和自动化场景里越来越别扭。后来团队决定把制品仓库逐步切到Hadess我拿到这个任务的第一反应不是“要不要迁”而是“怎么迁才能不打断研发节奏”。整个过程走下来我把关键路径、踩坑点和可复用的脚本都整理出来了这篇就从我的视角讲清楚如何把Nexus里的存量制品导入Hadess并且平滑迁移。先给没接触过Hadess的朋友一句人话简介Hadess是一套面向云原生场景的制品管理服务支持Maven、npm、PyPI、Docker镜像、Helm Chart等主流格式核心优势是对Nexus的REST API做了兼容层提供命令行工具和批量导入接口。换句话说它不是让你从零开始重新造仓库而是允许你把Nexus里的家底“搬”过去再逐步切换流量。适合正在做制品仓库替换、合并多套Nexus实例或者准备统一制品管理平台的人参考。1. 迁移前要想清楚的三件事1.1 先给Nexus做一次“人口普查”不要上来就写脚本拉制品那样大概率会漏掉隐藏仓库。我建议先在Nexus管理界面和API两个层面做盘点。界面可以看到仓库列表、Blob存储使用量、仓库的格式化类型API则是为了拿到结构化数据方便后续批量处理。我的做法是调用Nexus 3的/service/rest/v1/repositories接口把所有仓库的name、format、type、url列出来再对照每个仓库的存储占用。为什么要多此一举因为Nexus支持raw类型仓库里面可能存着团队自己的安装包、配置文件、甚至二进制工具这些非标准制品经常被忽略。Hadess迁移时需要针对raw内容做特殊处理先知道了才不慌。另外要记录仓库关联的Blob Store。Nexus里一个Blob Store可以被多个仓库共用迁移时如果只按仓库维度处理可能会把同一个制品的多份物理副本算重。盘点阶段顺手把Blob Store与仓库的对应关系也导出来后面算数据量、估迁移时间都靠它。1.2 明确范围全量迁还是增量迁第一次迁移别贪全除非你是测试环境。生产环境我强烈建议分两批第一批迁移只读型仓库和历史归档仓库比如Release仓库、Raw备份仓库第二批再动高频读写的Snapshot仓库和代理仓库。这样做的原因是Release制品基本不会变化迁过去后校验一次就能锁定而Snapshot制品可能还在被CI频繁推送迁移过程中会持续产生新版本容易导致“边迁边变”。所以迁移范围建议用一张表列出来仓库类型典型格式建议策略原因Hosted ReleaseMaven、npm、Raw全量迁移制品稳定适合批量导入Hosted SnapshotMaven先迁已有版本再切实时推送避免迁移期间新增版本丢失ProxyMaven中央仓库、npm registry清空缓存或按需拉取代理仓库本质是缓存重建成本低Group聚合多仓库迁移底层仓库后重建Group是逻辑视图无需直接迁移如果你是第一次操作我建议先只用“测试环境全量迁移”练手。我曾经在测试环境模拟过一套500GB的Nexus里面混合了Maven、Docker和Raw制品整条迁移链路跑通后生产环境就没那么慌了。1.3 备份和验证预案必须提前写很多人以为迁移就是导出导入忽略了风险控制。Nexus本身有备份机制但我们的目标不是备份而是保证在迁移失败或者导入结果异常时能原路回滚。最简单的方式是给Nexus做一次快照或者直接复制Blob Store目录。这个操作最好在迁移前一周做一次迁移当天再做一次增量备份。验证预案同样重要。导入制品后不能只看日志里“success”就结束必须抽样做“拉取验证”——在全新的Hadess仓库里用mvn、npm、docker pull来实际拉制品并对比校验和。我后面会详细讲怎么验证这里先给结论没有校验和的制品导入等于白干。因为Nexus里有些历史制品的元数据可能是残缺的Hadess导入时如果只能拿到blob而拿不到checksum客户端拉取会报错这种情况你必须在预案阶段就标记出来。2. Hadess导入Nexus制品的核心机制2.1 兼容层不是玄学是API翻译Hadess能“看懂”Nexus制品靠的是两层设计。第一层是格式解析比如Maven的pom.xml、npm的package.json、Docker的manifest和层文件Hadess都会按照标准格式解析并重建索引。第二层是API兼容Hadess提供一个/nexus/v1/...路径的兼容接口让原本对接Nexus的客户端不修改地址前缀就能继续用。这个设计解决了“迁移后所有CI配置都要改”的问题。我在实际体验里的感受是如果只是迁Maven Release兼容层几乎无感但如果迁Docker镜像要注意Nexus的Docker仓库有v1和v2两种API模式Hadess默认走Docker Registry HTTP API V2迁移时要确保Nexus里的镜像格式是manifest v2而不是已经过时的schema1。如果你有一批老镜像建议在Nexus端先做一次转换或者用Hadess导入工具自动转换。2.2 两条路线拉取式导入和推送式导入第一次听说“导入Nexus制品”的人容易误以为是把Nexus导出的文件包直接传到Hadess上。实际上Hadess提供两种模式拉取模式推荐在Hadess后台配置一个“来源Nexus连接”Hadess通过你的Nexus API去读取制品列表然后逐个拉取到本地存储。这种方式好处是迁移过程不占用Nexus的导出压力坏处是流量会经过Hadess所在网络内网带宽要够。推送模式在旧Nexus机器上运行Hadess提供的hadess-cli工具把指定仓库的制品批量推送到Hadess。推送模式适合Nexus和Hadess网络隔离的场景但需要安装CLI且要处理并发限速。我的建议是内网迁移用拉取跨网用推送。我们当时是生产Nexus在新机房Hadess在旧机房双向专线带宽有限所以用了推送模式把CLI部署在Nexus侧以仓库为单位串行推送避免把专线打满。2.3 元数据保留与替换规则迁移不仅仅是把文件拷贝过去Nexus里的搜索标签、属性、仓库组关系等信息都属于元数据。Hadess导入时会尝试读取Nexus API返回的attributes字段比如storage属性中的校验和、content属性中的格式信息。但有一类信息无法直接映射自定义的搜索标签比如“teamcore”、“envprod”。Hadess支持导入时通过映射规则把Nexus标签转换为自己的Label。这里要特别提醒不要强行追求100%元数据一致。迁移的核心是让制品可被正确拉取搜索标签丢了可以后续补但如果为了保留标签而拒绝了正常导入反而拖慢进度。我当时的处理方式是先导数据标签映射单独脚本跑两件事解耦。3. 实操从Nexus导出并导入制品的完整流程3.1 Maven仓库迁移命令行最稳Maven仓库是Nexus里最常见的类型迁移也最有代表性。我用的是推送模式具体步骤是在Hadess控制台创建目标仓库设置Repository ID为maven-releases与Nexus仓库名保持一致。在Nexus侧安装hadess-cli配置目标Hadess地址和认证Token。先创建一个公共库列表文件把需要迁移的Maven仓库名写进去比如cat repos.txt EOF maven-releases maven-snapshots third-party EOF执行批量导出hadess-cli export-nexus \ --nexus-url https://nexus.example.com \ --nexus-username admin \ --nexus-password-file secret.txt \ --repo-list repos.txt \ --output-dir /data/hadess-migration这里有个细节--repo-list比命令行一个个传仓库名要安全因为迁移过程会打印日志如果你把密码直接写在命令行里进程列表会暴露密码。用密码文件是更稳妥的做法。将导出目录推送到Hadesshadess-cli push \ --target-url https://hadess.example.com \ --target-repo maven-releases \ --source-dir /data/hadess-migration \ --retry 3 \ --concurrency 4--concurrency 4是我试过比较适中的并发度。太高比如8会把Hadess的导入线程塞满导致部分客户端请求超时太低则速度太慢。如果你不确定可以先用--dry-run跑一遍看输出文件数量和大小再逐步提高并发。导入完成后我建议用Maven客户端验证mvn dependency:get \ -Dartifactcom.example:demo:1.0.0 \ -DremoteRepositorieshttp://hadess.example.com/repository/maven-releases/能拉下来且校验和通过说明迁移成功。3.2 npm制品迁移注意scope和tagnpm仓库迁移时Nexus里通常有npm-hosted仓库存放发布的npm包。Hadess导入时支持从package.json读取name、version和dist-tags但如果你在Nexus里手动设置过npm tag部分旧版本可能没有正确的dist-tags记录。这种情况导入后你需要在Hadess里重新执行npm dist-tag add。实操命令还是一样的CLI只不过目标仓库类型是npm。我额外做了个脚本把Nexus API返回的所有npm包版本和tag信息导出为一个tag-mapping.json再通过Hadess的API写入hadess-cli import-tags \ --repo npm-hosted \ --tag-file tag-mapping.json这里有一个典型坑npm包名如果是scoped的比如company/core在Nexus里路径显示为company/core但导出时CLI可能会把它当成目录结构导致Hadess识别失败。解决办法是升级到CLI的1.2.3以上版本或者手工把company转换成一个编码目录company-~company这属于具体版本差异建议查一下当前CLI文档。3.3 Docker镜像迁移先处理manifest再传层Docker镜像迁移是最容易出问题的因为Nexus存储的Docker镜像不是单个文件而是一组blob镜像层外加manifest描述。直接暴力导出blob文件到Hadess是不行的Hadess需要重建镜像索引。我在迁移时用的是skopeo和hadess-cli配合的方案。先用skopeo从Nexus Docker仓库同步镜像到本地目录再把本地目录推送到Hadess。为什么用skopeo因为skopeo copy会自动处理manifest转换把schema1转换为schema2还能做镜像层的去重。命令大概是skopeo copy --all \ docker://nexus.example.com/myrepo/nginx:1.25 \ dir:/data/images/nginx:1.25然后逐个推送到Hadesshadess-cli push-image \ --source-dir /data/images/nginx:1.25 \ --target-url https://hadess.example.com \ --target-repo myrepo如果你有一整批镜像要迁我建议用skopeo sync它支持从Nexus Docker仓库同步整个路径。但要注意skopeo sync默认会递归同步所有tag包括那些指向同一个镜像的重复tag这会浪费大量时间。我在生产环境用skopeo sync --all --src docker --dest dir后又用脚本把重复tag合并了只保留每个digest对应一个tag其他作为别名。这样Hadess侧存储占用更小迁移时间也缩短了大概20%。3.4 用批处理脚本提升效率手动一条条命令跑太原始。我写了一个Shell脚本按仓库类型分组执行迁移#!/bin/bash for repo in maven-releases maven-snapshots; do hadess-cli export-nexus --nexus-url $NEXUS_URL \ --nexus-user $NEXUS_USER --nexus-pass $NEXUS_PASS \ --repo $repo --output-dir ./$repo hadess-cli push --target-url $HADESS_URL \ --target-repo $repo --source-dir ./$repo done脚本跑起来后一定要配合set -euo pipefail让任何一步失败就中止避免误以为全部成功。日志方面我用tee把输出同时落到文件和控制台方便排查。这不算炫技但确实能省很多时间。4. 平滑迁移的流量切换策略4.1 先用“双写模式”过渡“平滑迁移”的关键不是一次性把流量切到Hadess而是让Nexus和Hadess在一段时间内并存。我们在生产环境采用“双写”策略CI流程中制品发布时同时推送到Nexus和Hadess读流量仍然走Nexus。双写怎么实现最简单的是在Jenkins/GitLab CI的发布步骤里加一个任务将mvn deploy的结果同时执行mvn deploy:deploy-file到Hadess或者用curl调用Hadess的上传API。如果你用Artifactory plugin之类也可以往Hadess的Maven仓库跑一个镜像同步任务。双写阶段会遇到一个问题往Hadess推送相同坐标的制品时如果Hadess仓库配置了“不允许覆盖”第一次推送成功第二次推送就会报409。所以双写期间Hadess的仓库策略必须设置为“允许覆盖发布版本”。等流量全部切到Hadess后再把策略改回“禁止覆盖”。这个操作我用API写的切换后立即生效。4.2 用Nexus代理仓库把流量“偷”过来如果不想改每个客户端配置还有一个更优雅的方式在Nexus上创建一个代理仓库指向Hadess的仓库地址然后把原来客户端的仓库地址从Nexus本库改成这个代理仓库。这样当客户端拉取制品时Nexus代理会先去Hadess找找不到再回源到Nexus原始存储如果配置了备用源。这样看起来客户端还在访问Nexus但实际上流量已经转向Hadess了。这个机制利用的是Nexus本身作为“代理缓存”的角色。举个例子我有一个Nexus Group仓库maven-public它聚合了maven-releases和maven-central。我只需要在maven-public里把maven-releases替换成指向Hadess的代理仓库客户端不需要改任何配置后续拉取Release制品时请求会经过Nexus代理打到Hadess。等确认Hadess侧稳定后再把代理仓库从Group里移除直接让客户端指向Hadess即可。这个方案的好处在于是“渐进式替换”你可以只替换Group中的一个成员观察几天再继续。我当时就是这么做的替换后监控Nexus的访问日志发现Rate和Error没有异常才推进到下一步。4.3 灰度切换与回滚在切换当天我建议按“小范围用户/项目”灰度。比如先切一个非核心业务系统的CI观察它构建和部署全流程再扩大到全部项目。具体操作是把该项目的Mavensettings.xml中mirror指向Hadess地址其他项目保持不变。灰度期间必须准备回滚方案。如果Hadess出现问题只需把settings.xml改回Nexus地址即可。如果你是使用代理仓库模式回滚则是在Nexus Group里把Hadess代理仓库移除恢复原maven-releases成员。这个操作我一个人五分钟就可以完成所以整个切换过程没有捏一把汗的感觉。要注意的是切换后不要立即关闭Nexus。我建议保留Nexus一个月只读运行。因为有些客户端可能因为本地缓存失效会重新拉一个很久不用的历史版本这个版本如果在Hadess里没有被同步到就会404。保留Nexus原始仓库作为兜底能避免“拉不到包”的生产事故。我甚至写了一个定时任务每天凌晨自动对比Nexus和Hadess的制品列表发现有遗漏的自动用CLI补导。5. 常见问题与排查技巧实录5.1 导入后校验和不匹配优先检查换行符迁移后最常见的错误是客户端拉取时出现Checksum validation failed。第一次遇到我还以为是导出的文件损坏了后来发现是Hadess导入时把Nexus里存储的sha1和sha256校验和直接用了但某些Maven POM文件在Nexus中原始校验和是基于“未规范化”内容生成的而Hadess导入时可能做了内容的规范化比如换行符转换导致文件内容变了但校验和没有重新计算。解决办法是在Hadess导入参数中开启“重算校验和”选项或者在导出时选择“保留原始字节流”模式。如果你已经导完了可以写脚本对每个文件重新计算校验和并更新Hadess索引。这个坑我印象太深了所以建议任何人在迁移Maven制品后一定要随机抽几个小文件用sha1sum对比Nexus与Hadess存储的文件内容而不是只看日志。5.2 版本策略导致上传被拒双写阶段你向Hadess重复推送同名同版本制品如果Hadess仓库策略是Release默认不允许覆盖已存在的版本此时会报401或403。解决办法要么把策略临时切到Mixed要么先查询是否已存在该制品存在就跳过写入。我的脚本里加了这么一段if hadess-cli check --repo maven-releases --artifact $artifact; then echo $artifact already exists, skipping else hadess-cli push ... fi这个check操作会请求Hadess的搜索API不会拉全文件所以性能不错。迁移完成后再把策略调回Release防止后续误覆盖。5.3 超大文件中断断点续传不靠谱做好分片Hadess CLI在传输超过2GB的制品例如某些安装包时偶尔会因为网络抖动而中断。虽然CLI标注支持断点续传但我实测下来续传并不是字节级续传而是“从当前文件开头重新传”。也就是说大文件一旦中断之前传输的块会全部作废。这个体验比较闹心。后来我改用split先进行文件分片再通过CLI的分片上传参数推送全部传完后再在Hadess端合并。原因是分片后每个片大小在200MB左右即使某个片失败只需重传这个片成功率大幅提升。如果你迁的是Docker镜像其实不需要分片因为镜像层本身就被拆成了多个blob层每层通常不会特别巨大。5.4 客户端配置更新先改镜像再改仓库切换过程中我发现最容易被忽略的是“客户端仓库缓存”。很多开发机上的Mavensettings.xml里配置了本地仓库缓存即使你改了远程地址本地已有依赖还是会从缓存读取导致你根本看不出流量是否切换。所以我建议在灰度切换时先让CI构建机清掉本地仓库缓存rm -rf ~/.m2/repository 再构建。虽然第一次构建会慢但能真正验证Hadess连通性。另外如果你用Nexus Group模式客户端配置不用动但要让开发人员把本地的IDE Maven仓库缓存更新一下。这个可以通过在settings.xml里把更新策略设为always来加速诊断等一切稳定后再改回daily。5.5 元数据丢失后的补救用官方搜索API重建如果你发现迁移后的制品在Hadess搜索不到但通过路径直接访问又能下载很可能是Hadess的索引没有重建。Hadess一般支持Reindex操作对Maven仓库执行一次仓库级重索引通常几分钟到十几分钟索引重建后就能搜到。这个操作我在Docker仓库没有遇到因为Docker镜像的索引与Tag强绑定但Maven和npm需要手动触发。如果重索引还是搜不到就检查是不是Nexus里存在“路径大小写”不同的问题。Windows服务器上的Nexus偶尔会出现两个制品路径只有大小写不同比如/com/example/Demo.java和/com/example/demo.javaHadess在Linux环境下会严格区分大小写迁移时就会跳过其中一个。最好的方案是在迁移前用脚本把Nexus里所有路径大小写冲突的制品列出来手动决定保留哪个。迁移这件事做完总结下来其实只有三条主线盘点、导入、切换。盘点要细导入要快切换要稳。我见过不少团队因为急着把Nexus关掉结果一堆历史遗留制品在Hadess上404最后不得不从备份里找反而耽误了更多时间。如果你现在正在犹豫要不要迁我的建议是先搭一套测试环境用真实制品按我这套流程跑一遍哪怕只有几百个制品也能让你对Hadess的行为模式心里有数。特别是校验和、并发数、灰度切换这些点提前摸透生产环境就不会慌。
返回列表