☰
Nexus 3.24.0-02 私有制品库落地与Maven/NPM缓存避坑
2026/10/30 12:59:13 网站建设 项目流程

简介:nexus-3.24.0-02-unix.tar.gz 是 Sonatype Nexus Repository Manager 3.24.0-02 的 Unix/Linux 官方安装包,面向 Java 后端、前端 NPM 使用者及需要搭建私服或制品仓库的运维与开发团队,解决 Maven、NPM 等依赖集中代理、缓存、私有托管与权限管理问题。压缩包约 149.69MB,文件总数与类型明细上游暂未提供;解压后主要得到 nexus-3.24.0-02 服务目录和 sonatype-work 运行数据目录,分别承载可执行文件、配置、库文件以及制品、索引、日志和数据库内容。已有 212 人浏览学习。它可作为 Nexus 3.24 版本部署与配置的原始安装材料,便于读者实践 Maven 代理仓库、NPM 代理与私有仓库、仓库组和权限配置,理解 conf/nexus.properties 及服务目录、工作目录分离的目录结构,适合搭建内网制品库和排查依赖下载效率问题。

1. 私有制品库这件事,别再用本地缓存硬扛了

前阵子帮一个小组收拾构建环境,十来个项目共用一台构建机,本地.m2攒到三十多 G,还是天天有人抱怨 Maven 拉包卡在 Downloading,NPM 那边和 registry 的连接偶尔直接超时中断,CI 一跑就是十几分钟一动不动。这类问题的根子往往不是带宽,而是没有本地制品缓存层:每次构建都要跟外部仓库重新协商。nexus-3.24.0-02-unix.tar.gz就是能用上的东西——一个解压即跑的仓库管理服务,把 Maven 的 jar、NPM 的 tarball 缓到内网,团队只认一个地址。这篇讲清三件事:这个版本包在 Unix 上怎么落地、Maven 和 NPM 两类仓库怎么规划串联、以及上线后最容易踩的坑。

2. 解包与首次启动:3.24.0-02 这个 tar.gz 里装了什么

2.1 为什么挑 unix tar.gz 而不是镜像或其他包

同一个版本,常见的分发形态有三类:unix 的.tar.gz、容器镜像、Windows 的 zip。三个东西功能上是一套代码,差别全在部署边界上。我一般优先选 tar.gz,原因很实在:目标机器往往在内网,没有镜像仓库可用,镜像还得先解决传输和导入问题;而 tar.gz 就是一个压缩包,scp过去解压就能跑。

形态前置条件参数调整升级方式适合场景
unix tar.gz一台有 JDK 的 Linux,一个可写目录直接改bin/nexus.vmoptions换目录、改软链、重启内网离线、单机部署、需要抠内存参数
容器镜像有可用的镜像仓库或能导入离线镜像改环境变量或挂载配置换 tag已有编排平台、要求快速重建
Windows zipWindows Server + JDK改bin\nexus.vmoptions换目录少量遗留的 Windows 构建机

选 tar.gz 还有一层:3.24.0-02 这个版本号是固定的,包一旦落地,下次迁移或重建复现的是同一个版本,不会因为拉了个latest就莫名多出一堆行为差异。我见过太多“上周还好好的这周就起不来”的事故,最后追下去都是镜像 tag 被人覆盖了。

需要提前确认一件事:这个版本对 JDK 的要求偏保守,长期验证过的组合是 JDK 8。机器上如果默认装的是更高的版本,别指望它能自动兼容,先在测试机上用java -version对一下,把JAVA_HOME指到 8 再启动,能省掉一轮没有意义的排查。

2.2 目录结构、运行用户与 JVM 参数

包解开后是两个平级目录:一个是程序目录,名字带版本号;另一个是数据目录,默认叫sonatype-work,里面nexus3/才是真正的数据根,包含 blob 存储、数据库、日志、配置。日常操作里程序目录基本不用碰,升级时整个替换掉即可,数据目录必须单独保住。

第一件事是把目录固定成一个稳定路径,别让版本号进到启动脚本里,否则以后每次升级都要改一堆配置:

# 解包到统一父目录,程序目录重命名为 current,数据目录单独挪出来 sudo mkdir -p /opt/nexus sudo tar -xzf nexus-3.24.0-02-unix.tar.gz -C /opt/nexus sudo mv /opt/nexus/nexus-3.24.0-02 /opt/nexus/current sudo mv /opt/nexus/sonatype-work /opt/nexus/data # 建一个不可登录的服务账号,别用 root 跑 sudo useradd -r -s /bin/false nexus sudo chown -R nexus:nexus /opt/nexus

这段的关键点有三个。current这个软路径是给 systemd 和运维脚本用的,升级时只需要重新指向新目录;/opt/nexus/data单独放到一个空间足够的分区,因为后面缓存会持续增长,跟程序目录混在一起容易把系统盘写满;chown必须在启动前做完,用 root 启动过一次之后再改属主,blob 文件里会残留 root 权限,后面再启动就报权限错误,属于典型的血泪经验。

接着改 JVM 参数。bin/nexus.vmoptions里默认给的是 1200M 堆,小团队够用,但一旦开了多个 group 仓库、并发构建上来,这个值会明显不够:

# 堆大小按物理内存的 1/4 左右给,别超过 8G,Nexus 不是吃堆型的服务 sudo sed -i 's/^-Xms.*/-Xms2703M/' /opt/nexus/current/bin/nexus.vmoptions sudo sed -i 's/^-Xmx.*/-Xmx2703M/' /opt/nexus/current/bin/nexus.vmoptions # 数据目录改到 /opt/nexus/data,两处都要改 sudo sed -i 's#^-Dkaraf.data=.*#-Dkaraf.data=/opt/nexus/data/nexus3#' /opt/nexus/current/bin/nexus.vmoptions sudo sed -i 's#^-Dkaraf.log=.*#-Dkaraf.log=/opt/nexus/data/nexus3/log#' /opt/nexus/current/bin/nexus.vmoptions grep -E '^-Xm|^-Dkaraf' /opt/nexus/current/bin/nexus.vmoptions

-Xms和-Xmx设成一样是为了避免运行期反复扩堆,这类服务一旦堆抖动,表现就是请求间歇性卡顿,很难定位。-Dkaraf.data决定了数据落在哪,如果只改了一个不注意改-Dkaraf.log,日志还会写到老路径,下次出问题查日志查不到,等于给自己留了个黑匣子。改完用grep确认一遍,比盲信sed稳妥。

2.3 启动、端口与初始口令

前台先跑一次,把启动过程的日志看全,别上来就丢后台:

# 前台启动,能看到完整启动日志,第一次务必这么跑 sudo -u nexus /opt/nexus/current/bin/nexus run # 观察这行日志说明起来了 # Started Sonatype Nexus OSS 3.24.0-02

run是前台模式,start是后台模式。第一次排错用run,日志直接打到终端;确认能起来之后,再换成 systemd 托管。默认监听 8081,上下文路径是根路径,浏览器访问http://<机器IP>:8081/就是界面。

初始管理员口令不在配置文件里,而是启动后生成的一个一次性文件:

# 初始口令文件,第一次登录后该文件会被自动删除 sudo cat /opt/nexus/data/nexus3/admin.password # 日志位置,起不来看这里 sudo tail -n 200 /opt/nexus/data/nexus3/log/nexus.log

用户名是admin,密码取上面那个文件的内容。登录后会强制改密码,改完这个文件就消失了,所以第一次进去之后顺手把新口令记到内部凭据管理里,别指望还能再翻出来。如果口令文件不存在而你又没记密码,只能走重置流程,那就要停机改库,很麻烦——这个后悔药不好吃。

systemd 单元写起来很短,重点是User、LimitNOFILE和重启策略:

# /etc/systemd/system/nexus.service [Unit] Description=nexus repository After=network.target [Service] Type=forking User=nexus LimitNOFILE=65536 ExecStart=/opt/nexus/current/bin/nexus start ExecStop=/opt/nexus/current/bin/nexus stop Restart=on-abort [Install] WantedBy=multi-user.target

LimitNOFILE必须显式给,默认的 1024 在并发拉包时会撞到文件句柄上限,报出来的错误是Too many open files,看着像磁盘问题,其实是句柄问题。Restart=on-abort只处理异常退出,正常 stop 不会被拉起来,避免运维脚本和 systemd 打架。

3. 仓库规划:Maven 与 NPM 两类格式怎么建、怎么串

3.1 hosted、proxy、group 三件套与命名规范

Nexus 的核心模型只有三个词,理解这三个词,整个规划就通了。hosted 是自建的、往里推包的仓库;proxy 是代理外部仓库的缓存层;group 是把前两者聚合成一个对外地址的门面。客户端永远只认 group 的地址,这样内部包和外部包走同一个入口,配置不用随仓库增减而变。

类型作用Maven 典型命名NPM 典型命名是否对客户端暴露
hosted (release)存内部正式构件maven-releasesnpm-hosted否,只挂进 group
hosted (snapshot)存内部快照maven-snapshots—否
proxy缓存外部仓库maven-centralnpm-proxy否
group聚合读写入口maven-publicnpm-group是,唯一对外地址

命名上我坚持一条规矩:类型前缀 + 来源,全部小写中划线。原因是这个仓库名会出现在settings.xml、.npmrc、CI 变量、甚至 Dockerfile 里,名字一乱,后面 grep 都搜不齐。曾经接手过一套环境,同一个人建了maven、Maven-Public、maven_public三个仓库,最后没人说得清哪个在用。

还有一处细节:hosted 的部署策略要分开设。正式包仓库设成Disable Redeploy,同一个版本号不允许覆盖,逼着大家升版本;快照仓库设成Allow Redeploy,否则 CI 连推两次就报 400。

3.2 Maven 代理仓库的关键参数

建maven-central代理仓库时,界面上参数不少,真正影响使用的就那几个:

参数建议值说明
Remote storageMaven Central 官方地址代理源,内网无外网时改成上级仓库地址
Auto blocking enabled勾选上游连续失败自动摘除,避免拖慢整体响应
Auto block timeout1440 分钟摘除后多久重试,别设太小
Content max age1440 分钟缓存有效期,设 -1 表示永久不校验
Metadata max age1440 分钟maven-metadata.xml的有效期,必须比构件短
Artifact max age-1 或 1440 分钟正式版构件一般不需要重复校验
Layout policyPermissive宽松模式,避免非标准布局的包被拒
Checksum policyWarn校验失败只告警不阻断,内网代理场景更实用

这里最容易翻车的是Metadata max age。有人图省事把所有 max age 全设成 -1,结果上游发了新版本,本地怎么都刷不出来,mvn -U强制更新也没用,因为 Nexus 自己认为缓存永远新鲜。快照和元数据这类会变的内容,TTL 必须留一个有限值。

Checksum policy设成Strict也有代价:上游仓库偶尔会有校验文件缺失或格式不规范的构件,严格模式直接拒绝,构建报错但错误信息指向的是校验和不匹配,新手很难联想到是策略问题。内网代理场景我一般用Warn。

建完代理后,maven-public这个 group 的成员顺序有讲究:maven-releases→maven-snapshots→maven-central。内部仓库放前面,是为了让同名构件优先命中内部版本;如果顺序反了,内部修过的包会被中央仓库的旧版盖掉,这种问题排查起来极其玄学。

3.3 NPM 仓库的 registry 与 scope 规划

NPM 侧的模型和 Maven 一样,但多一层 scope 的概念,规划时要一起考虑。常见做法是建三个:npm-proxy代理公共源、npm-hosted存内部包、npm-group聚合前两者。

只有三个仓库不够,还要决定“内部包用什么 scope”。约定是这样:内部包统一用@公司缩写/包名的形式,scope 直连 hosted,其余所有包走 group。这样做的好处是发布路径和拉取路径彻底分开——发布只可能打到 hosted,不会误推到 group 上;拉取时内部包和公共包又都走一个地址,package-lock.json里不会出现两套源。

仓库类型用途客户端配置方式
npm-proxyproxy缓存公共包不直接配置,挂在 group 下
npm-hostedhosted内部包发布scope 级 registry
npm-groupgroup统一拉取入口全局 registry

group 的成员顺序同理,npm-hosted放在npm-proxy前面。这样内部包名一旦和公共包重名,优先拿内部的,避免供应链混淆。另外提醒一句:NPM 的 group 仓库里不要放npm-hosted之外的可写成员,权限一旦配错,匿名用户就能往代理仓库里推包。

4. 客户端接入:settings.xml 与 .npmrc 抄作业

4.1 Maven:mirror、servers 与发布配置

Maven 侧要改两个文件:全局的settings.xml管镜像和认证,项目里的pom.xml管发布地址。先看settings.xml:

<settings> <mirrors> <!-- 所有 central 请求走内网 group,客户端不再直连外部 --> <mirror> <id>nexus-public</id> <name>internal nexus group</name> <url>http://192.168.10.20:8081/repository/maven-public/</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors> <servers> <!-- id 必须和 pom 里 distributionManagement 的 id 完全一致 --> <server> <id>nexus-releases</id> <username>deployer</username> <password>********</password> </server> <server> <id>nexus-snapshots</id> <username>deployer</username> <password>********</password> </server> </servers> </settings>

mirrorOf的取值是最容易出错的地方。写central只劫持中央仓库;写*会劫持所有仓库,包括项目pom.xml里自定义的那些,如果你的项目依赖了另一个团队的私服地址,*会把它也指向 Nexus,然后 404。稳妥写法是external:*或者用排除法*,!my-other-repo。这个坑我在三个不同团队见过,每次都要解释一遍。

<server>的id和pom.xml里的distributionManagement的id必须字面一致,差一个字母认证就拿不到,报的是 401,但错误信息只会说 Unauthorized,不会告诉你 id 对不上。发布配置长这样:

<distributionManagement> <repository> <id>nexus-releases</id> <url>http://192.168.10.20:8081/repository/maven-releases/</url> </repository> <snapshotRepository> <id>nexus-snapshots</id> <url>http://192.168.10.20:8081/repository/maven-snapshots/</url> </snapshotRepository> </distributionManagement>

建议把这段放进公司级的父 POM,各子项目继承,省得每个项目都复制一遍还复制错。发布账号单独建一个deployer,只给 releases 和 snapshots 的写权限,别用 admin 发布,凭据一旦泄露代价太大。

4.2 NPM:registry、scope 与认证

NPM 侧改.npmrc,项目级或用户级都行,推荐项目级放一份模板,用户级放凭据:

# 公共包和内部包统一走 group registry=http://192.168.10.20:8081/repository/npm-group/ # 内部 scope 直连 hosted,发布只可能打到这个仓库 @corp:registry=http://192.168.10.20:8081/repository/npm-hosted/ # 认证按 registry 路径精确匹配,末尾斜杠不能少 //192.168.10.20:8081/repository/npm-group/:_auth= BASE64编码串 //192.168.10.20:8081/repository/npm-hosted/:_auth= BASE64编码串 always-auth=true

三个参数说明白。registry是全局默认源,所有不带 scope 的包都走这里;@corp:registry是 scope 级覆盖,优先级高于全局,发布内部包时自动落到 hosted;_auth的 key 必须和 registry 的 URL 前缀逐字符匹配,包括协议、端口和末尾斜杠,少一个斜杠认证就不生效,但 NPM 只会报一个笼统的 401。

always-auth=true这个开关在 Nexus 前面挂反向代理时尤其重要,不加的话 NPM 只在部分请求上带凭据,表现为安装能过、发布 401。

认证串的生成方式:

# 生成 base64 凭据,注意 -n 不能少,否则会把换行符编进去 printf '%s:%s' 'deployer' 'your-password' | base64 # 或者直接走交互式登录,Nexus 支持 npm token npm login --registry=http://192.168.10.20:8081/repository/npm-hosted/

printf比echo稳,echo在不同 shell 下对换行的处理不一致,编出来的串末尾多一个字符,认证就失败,而且肉眼完全看不出来。用npm login的话,服务端需要先启用 npm Bearer Token 相关的认证域,否则登录会成功但拿不到 token,这一步在 Nexus 的认证配置页里勾一下即可。

4.3 用一条命令验证链路通不通

配置改完不要急着让全组切换,先自己验证三件事:能不能从 group 拉到包、log 里有没有命中缓存、能不能往 hosted 推包。

# Maven:拉一个确定存在的构件,走临时本地仓库避免污染 mvn -s settings.xml -Dmaven.repo.local=/tmp/m2-test \ dependency:get -Dartifact=org.slf4j:slf4j-api:1.7.30 # NPM:查版本号,能返回说明 registry 通了 npm view lodash version --registry=http://192.168.10.20:8081/repository/npm-group/ # NPM:登录状态自检 npm whoami --registry=http://192.168.10.20:8081/repository/npm-hosted/

-Dmaven.repo.local指定临时目录很关键,用默认的~/.m2验证,即使 Nexus 挂了也可能因为本地有缓存而误判成功。验证完之后去 Nexus 界面的 Browse 里看maven-central下有没有出现刚拉的那个构件,出现了说明缓存生效;再tail一下request.log,能看到请求是从内网地址进来的还是回源出去的,这是判断代理是否真正生效最直接的证据。

5. 避坑与排查:五个翻车点

5.1 前台跑得起,systemd 起不来

现象:手动sudo -u nexus bin/nexus run一切正常,换成 systemd 之后状态是 failed,日志里只有一行java.lang.IllegalStateException或者干脆为空。

原因基本两个:一是 systemd 环境里没有继承JAVA_HOME,脚本找不到 java;二是Type=forking配上start之后,主进程 fork 出去,systemd 认为进程已经退出。

解决:在 service 文件里显式加Environment=JAVA_HOME=/usr/lib/jvm/java-8-openjdk,路径用readlink -f $(which java)反推;确认Type=forking且ExecStart用的是nexus start而不是run。改完systemctl daemon-reload再启动。

5.2 发包报 413 或连接被重置

现象:npm publish或者mvn deploy传大一点的包时报413 Request Entity Too Large,或者直接ECONNRESET。

原因:Nexus 前面挂了一层反向代理,代理的请求体上限是默认的 1M,或者读超时太短。直连 8081 没这个问题,一经代理就出现。

解决:代理侧把client_max_body_size调到 0 或 512m,proxy_read_timeout调到 300s 以上,并且确认proxy_request_buffering的设置不会把大包全缓存在内存里。改完先用curl -X POST传一个大文件对比,确认不是 Nexus 自己的限制。

5.3 npm publish 一直 401,但 login 是成功的

现象:npm login提示成功,npm whoami也能返回用户名,但npm publish报 401 或 403。

原因分三类:_auth的 key 和 registry 路径不精确匹配,尤其是末尾斜杠;hosted 仓库的部署策略被设成了Read-only;账号有读权限但没有目标仓库的写权限。

解决:把.npmrc里的认证行打印出来逐字符比对;在 Nexus 界面确认 hosted 仓库的 Deployment policy 是Allow Redeploy;用管理员账号在仓库的权限页确认deployer角色有nx-repository-view-npm-*-add之类的权限。三类原因表现一样,只能一条条排,先排配置再排权限。

5.4 磁盘半年涨了几百 G

现象:上线时数据目录只有几 G,跑几个月变成几百 G,磁盘告警。

原因:Content max age设成了 -1,代理缓存只增不减;快照仓库没有清理策略,每天 CI 推几十个快照;blob store 删了包但底层文件没有压实,空间不释放。

解决:把 proxy 仓库的 max age 调回有限值;给 snapshots 配清理策略,只保留最近 N 天或最近 N 个版本;定期跑 blob store 的 compact 任务。三件事缺一件,磁盘都会继续涨。

5.5 备份只拷了数据目录,恢复后起不来

现象:把数据目录整个 tar 走,换机器解压,启动报 blob 缺失或者组件状态异常。

原因:热备时正在写入,数据库和 blob 文件不同步;或者只拷了 blob 没拷数据库,反之亦然。

解决:要么停机备份,要么先跑一次数据库导出任务再拷 blob 目录,两次动作之间停止写入。恢复后先做一致性检查,再对外提供服务。备份这件事没有捷径,验证过一次恢复流程才算真的有备份。

6. 长期运维的两个动作:备份脚本与清理任务

真正让这套东西能长期跑下去的,不是配置多漂亮,而是两个固定动作:能恢复的备份、会自己打扫的清理任务。

备份我一般用停机窗口做,脚本很简单,但每一步都有原因:

#!/usr/bin/env bash set -euo pipefail DATA_DIR=/opt/nexus/data BACKUP_DIR=/backup/nexus STAMP=$(date +%F-%H%M) # 1. 先停服务,保证数据库和 blob 一致 systemctl stop nexus # 2. 打包,排除日志目录,日志没必要进备份 tar czf "${BACKUP_DIR}/nexus-${STAMP}.tar.gz" \ -C "$(dirname "$DATA_DIR")" \ --exclude='nexus3/log' \ "$(basename "$DATA_DIR")" # 3. 起服务,无论打包成功与否 systemctl start nexus # 4. 只保留最近 7 份,避免备份盘被自己撑满 ls -1t "${BACKUP_DIR}"/nexus-*.tar.gz | tail -n +8 | xargs -r rm -f

--exclude排除日志是必须的,日志目录往往比数据目录还大,备份进去纯属浪费空间。tail -n +8配合ls -1t是按时间倒序保留 7 份,xargs -r在没匹配到文件时不会报错。set -euo pipefail保证任何一步失败立即退出,不会出现“备份文件生成了但内容不完整”这种情况。

清理和压实通过 REST 接口触发,方便放进定时任务:

# 触发 blob store 压实,回收已删除包占用的磁盘 curl -sS -u admin:密码 -X POST \ "http://192.168.10.20:8081/service/rest/v1/tasks" \ -H "Content-Type: application/json" \ -d '{ "name": "compact-blob", "type": "blobstore.compact", "enabled": true, "alertEmail": "", "notificationCondition": "FAILURE", "properties": { "blobstoreName": "default" } }' # 查任务状态,看 lastRunResult 是不是 OK curl -sS -u admin:密码 \ "http://192.168.10.20:8081/service/rest/v1/tasks" | head -c 2000

type必须是接口文档里列出的类型名,写错了会返回 400 但不会告诉你正确的有哪些;properties里的blobstoreName要和界面里建的那个名字一模一样。这个任务不要在业务高峰跑,压实期间 IO 会明显上来,我一般放在凌晨。清理策略本身则在仓库的 Cleanup 配置里设,比如快照只保留最近 30 天,配合上面的压实任务,磁盘占用才会真正降下来。

从那以后我每次交付这套东西,都会在交付清单里加上一条硬性要求:备份恢复必须真跑一遍,从备份包里解出来起一个实例,登录进去随便拉一个包,确认能拉通才算完。没验证过的备份就是心理安慰,真出事的时候它不会给你第二次机会。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询