简介: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 zip | Windows 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-02run是前台模式,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.targetLimitNOFILE必须显式给,默认的 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-releases | npm-hosted | 否,只挂进 group |
| hosted (snapshot) | 存内部快照 | maven-snapshots | — | 否 |
| proxy | 缓存外部仓库 | maven-central | npm-proxy | 否 |
| group | 聚合读写入口 | maven-public | npm-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 storage | Maven Central 官方地址 | 代理源,内网无外网时改成上级仓库地址 |
| Auto blocking enabled | 勾选 | 上游连续失败自动摘除,避免拖慢整体响应 |
| Auto block timeout | 1440 分钟 | 摘除后多久重试,别设太小 |
| Content max age | 1440 分钟 | 缓存有效期,设 -1 表示永久不校验 |
| Metadata max age | 1440 分钟 | maven-metadata.xml的有效期,必须比构件短 |
| Artifact max age | -1 或 1440 分钟 | 正式版构件一般不需要重复校验 |
| Layout policy | Permissive | 宽松模式,避免非标准布局的包被拒 |
| Checksum policy | Warn | 校验失败只告警不阻断,内网代理场景更实用 |
这里最容易翻车的是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-proxy | proxy | 缓存公共包 | 不直接配置,挂在 group 下 |
| npm-hosted | hosted | 内部包发布 | scope 级 registry |
| npm-group | group | 统一拉取入口 | 全局 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 2000type必须是接口文档里列出的类型名,写错了会返回 400 但不会告诉你正确的有哪些;properties里的blobstoreName要和界面里建的那个名字一模一样。这个任务不要在业务高峰跑,压实期间 IO 会明显上来,我一般放在凌晨。清理策略本身则在仓库的 Cleanup 配置里设,比如快照只保留最近 30 天,配合上面的压实任务,磁盘占用才会真正降下来。
从那以后我每次交付这套东西,都会在交付清单里加上一条硬性要求:备份恢复必须真跑一遍,从备份包里解出来起一个实例,登录进去随便拉一个包,确认能拉通才算完。没验证过的备份就是心理安慰,真出事的时候它不会给你第二次机会。希望帮到你。
本文还有配套的精品资源,点击获取