Nexus / 制品私服
本文范围
这篇讲 Nexus Repository 作为企业制品私服怎么落地:安装、数据目录、hosted/proxy/group 仓库模型、Maven 私服、npm 私服、Raw 制品、第三方 jar 上传、CI 接入、权限、磁盘清理、备份恢复、内网离线依赖闭环和 401/404/下载慢排障。
不展开 Maven、Gradle、Node 的完整构建教程,也不把 Docker 镜像仓库作为主线。镜像仓库优先交给 Harbor;Nexus 在本文里主要负责 jar、npm 包、通用压缩包和离线依赖缓存。
前置条件
先准备一台稳定的制品仓库服务器,最好单独挂载数据盘。Nexus 是构建入口,一旦不可用,CI、离线部署和回滚都会受影响。
| 项目 | 要求 |
|---|---|
| 服务器 | 建议 4 核 8GB 起步,数据盘独立,文件句柄限制已调大 |
| 运行时 | 可用 Docker/Compose 或按官方安装包部署;生产不要 root 用户运行进程 |
| 仓库规划 | Maven releases/snapshots/public、npm hosted/proxy/group、Raw releases 等先命名 |
| 网络 | CI、开发机、离线准备机能访问 Nexus;Nexus 能访问企业代理或公共仓库 |
| 权限 | 管理员、发布账号、只读账号、CI token 分开 |
| 版本 | 以上线当天 Sonatype 版本状态页、系统要求和 release notes 为准 |
Sonatype 当前系统要求强调:Nexus Repository 需要 Java 21,文件句柄不足会造成严重风险;新安装默认 H2 数据库只适合小规模场景,生产中等以上规模要评估外部 PostgreSQL。
先说清楚 Nexus 到底管什么
很多团队一开始不觉得制品私服重要。项目能在本机跑,Maven 能下载 jar,npm 能装包,似乎就够了。问题通常是到了内网、离线现场、CI 节点或者生产回滚时才爆出来:
- 公网 Maven 仓库今天很慢,构建卡在下载依赖。
- npm 包能在你电脑上装,CI 节点却 404。
- 某个商业 jar 只在一个同事本机
.m2目录里。 - 内网服务器不能访问外网,项目根本构建不起来。
- 上线包没有统一保存,回滚时找不到上一版制品。
- 谁上传、谁覆盖、谁删除过包,说不清楚。
Nexus 就是用来收口这些问题的。它不是 Maven 的替代品,也不是 npm 的替代品,它是放在团队中间的一层制品仓库:
开发机 / CI
-> 只访问 Nexus
-> Nexus 再去代理公共仓库、缓存依赖、保存内部制品这篇文章只讲通用制品私服,不展开 Maven、Gradle、Node 的完整构建过程,也不展开 Harbor 镜像仓库。Docker 镜像有自己的治理方式,这里最多讲 Nexus 的 Docker 形态边界,不把它当主线。
版本上以 Nexus Repository 3 为准。截至 2026-07-10,官方版本状态页显示 3.94.0 已是 General Availability。下面示例用 sonatype/nexus3:3.94.0,真正上线前仍要以安装当天的官方下载页、系统要求和 Release Notes 为准。
先把最小可用环境跑起来
我们先不要一上来就聊权限、清理、备份。先把一个能访问、能登录、数据能持久化的 Nexus 跑起来。这样后面的 Maven、npm、Raw 仓库配置才有地方落。
准备目录:
sudo mkdir -p /opt/nexus
sudo mkdir -p /data/nexus/nexus-data
sudo chown -R 200:200 /data/nexus/nexus-data这里要注意两点。
第一,/opt/nexus 放部署文件,比如 compose.yaml。 第二,/data/nexus/nexus-data 放 Nexus 的真实数据。这个目录一定要独立挂出来,不要让数据留在容器可写层里。容器删了可以重建,制品仓库数据丢了就麻烦了。
如果 chown 200:200 看着有点奇怪,可以先记住:官方镜像里的 Nexus 进程一般不是用 root 跑的。如果目录权限不对,后面容器会启动失败,日志里会看到 /nexus-data 没有写权限。
进入部署目录:
cd /opt/nexus新建 compose.yaml:
services:
nexus:
image: sonatype/nexus3:3.94.0
container_name: nexus
restart: unless-stopped
ports:
- "8081:8081"
environment:
INSTALL4J_ADD_VM_PARAMS: "-Xms2g -Xmx2g -XX:MaxDirectMemorySize=2g"
volumes:
- /data/nexus/nexus-data:/nexus-data启动:
docker compose up -d看容器是否起来:
docker ps --filter name=nexus正常应该能看到 nexus 容器处于 Up 状态。如果没有,先不要急着重装,直接看日志:
docker logs --tail=200 nexus如果日志里是权限问题,回去检查 /data/nexus/nexus-data 的属主。 如果日志里是端口冲突,说明宿主机 8081 被占用了,可以把端口改成 "8082:8081"。 如果日志里是内存不够,就调小 JVM 参数或者给机器加内存。
持续看启动日志:
docker logs -f nexus看到类似 Started Sonatype Nexus 之后,再访问:
http://服务器IP:8081首次启动会生成 admin 初始密码:
docker exec nexus cat /nexus-data/admin.password打开页面,右上角登录,账号是 admin,密码就是上面文件里的内容。登录后会要求改密码,这一步不要跳。改完密码后,先不要把这个账号拿给 CI 用,admin 只用于初始化和紧急维护。
再用命令验证一次页面能访问:
curl -I http://127.0.0.1:8081/如果返回里能看到 HTTP/1.1 200 或者重定向响应,说明服务入口是通的。浏览器打不开时,就按这个顺序查:
docker ps --filter name=nexus
docker logs --tail=200 nexus
ss -lntp | grep 8081 || true
curl -I http://127.0.0.1:8081/现场排障不要靠猜。先确认容器活着,再确认端口监听,再确认本机能访问,最后再查防火墙、Nginx、域名和安全组。
安装完成后先做三件小事
Nexus 跑起来以后,不建议马上给全团队用。先把基础动作做完。
第一,记录版本:
docker exec nexus sh -c 'cat /opt/sonatype/nexus/VERSION 2>/dev/null || true'如果这个文件路径随镜像变动查不到,也没关系,登录控制台查看版本即可。关键是你要知道现在跑的到底是哪一版。
第二,确认数据目录大小:
du -sh /data/nexus/nexus-data
df -h /data/nexus刚安装完数据不大。后面 Maven、npm proxy 缓存起来以后,这个目录会持续增长,所以生产环境不要放系统盘。
第三,给 Nexus 准备域名。开发测试可以用 IP,生产建议用:
https://nexus.example.com后面 Maven settings.xml、npm .npmrc、CI 凭证都要写这个地址。今天写 IP,明天换机器,全团队都要改配置。域名提前规划,后面迁移省很多事。
仓库类型先别背,按用途理解
Nexus 里最重要的是三种仓库:hosted、proxy、group。不要把它们当概念背,按使用场景理解就很简单。
hosted 是你自己放东西的地方。公司内部 jar、内部 npm 包、闭源 SDK、离线 zip 包,都是放 hosted。
proxy 是帮你代理远程仓库的地方。第一次有人下载 spring-core,Nexus 去上游仓库拉下来并缓存。第二个人再下载,就直接从 Nexus 取。
group 是统一入口。它自己不真正存文件,只是把多个 hosted 和 proxy 组合起来,让客户端只配一个地址。
最常见的 Maven 结构是这样:
maven-public(group)
-> maven-releases(hosted)
-> maven-snapshots(hosted)
-> maven-thirdparty(hosted)
-> maven-central-proxy(proxy)npm 结构通常更简单:
npm-public(group)
-> npm-hosted(hosted)
-> npm-proxy(proxy)这里要注意一个非常实用的原则:
内部包放 hosted。
公共包走 proxy。
开发机和 CI 只配 group。不要把公共依赖一股脑上传到 hosted。Spring、Jackson、Guava、Lodash 这类公共包,本来就应该让 proxy 去缓存。hosted 里只放公司自己的包,或者公共仓库没有的闭源包。这样仓库更干净,清理也更安全。
制品私服依赖与上传下载链路图
把 hosted、proxy、group 放进真实构建链路里看,会更容易理解 Nexus 的价值:开发机和 CI 不应该到处配置仓库地址,它们只访问 group;内部制品上传到 hosted;公共依赖由 proxy 去上游拉取并缓存;Raw 仓库保存可回滚的交付包和校验文件。
这张图落到团队规范就是三句话:下载走 group,上传走 hosted,交付包进 Raw 并带校验。group 是客户端入口,不是上传目标;proxy 是缓存公共依赖,不是公司内部包仓库;Raw 不是网盘,必须按应用名、版本号和 checksum 管。
验证下载链路:
mvn help:effective-settings
mvn -U dependency:get -Dartifact=org.slf4j:slf4j-api:2.0.17
npm config get registry
npm ping --registry=https://nexus.example.com/repository/npm-public/
npm view lodash version --registry=https://nexus.example.com/repository/npm-public/验证上传和回读:
mvn clean deploy
mvn -U dependency:get -Dartifact=com.example:order-api:1.0.0
npm publish --registry=https://nexus.example.com/repository/npm-hosted/
npm view @company/order-ui versions --registry=https://nexus.example.com/repository/npm-public/
curl -u "$NEXUS_USER:$NEXUS_PASSWORD" \
--upload-file order-service-1.4.2.tar.gz \
https://nexus.example.com/repository/raw-releases/order-service/1.4.2/order-service-1.4.2.tar.gz常见坑也顺着图查。401 多半是 Maven server.id、npm token 作用域或 Nexus 权限不一致;404 多半是包没上传到对应 hosted、group 没包含目标仓库、SNAPSHOT 和 release 仓库混用,或者 proxy 上游没有这个包;下载慢要分清是开发机到 Nexus 慢、Nexus 到 upstream 慢,还是 CI 本地缓存没有复用。
生产约束是:release 仓库禁止随意覆盖,同版本包不要反复上传;npm token 必须绑定到具体 registry 路径;公共上游可以切换,但客户端只改 Nexus group 地址;清理策略先 preview,再 cleanup 和 compact blob store,不能手工删除 blob 目录里的文件。
创建 Maven 仓库
打开 Nexus 控制台,进入:
Repository -> Repositories -> Create repository先创建正式版本仓库,选择 maven2 (hosted):
| 配置项 | 建议值 |
|---|---|
| Name | maven-releases |
| Version policy | Release |
| Deployment policy | 生产用 Disable redeploy |
这里要注意,release 仓库不建议允许覆盖。同一个 1.0.0 版本,今天是一个 jar,明天又被覆盖成另一个 jar,回滚和排障都会很难查。
再创建快照仓库,还是选择 maven2 (hosted):
| 配置项 | 建议值 |
|---|---|
| Name | maven-snapshots |
| Version policy | Snapshot |
| Deployment policy | Allow redeploy |
SNAPSHOT 本来就是开发和集成阶段用的,允许重复发布比较常见。但生产发布尽量不要依赖 SNAPSHOT。
再创建第三方闭源包仓库:
| 配置项 | 建议值 |
|---|---|
| Name | maven-thirdparty |
| Version policy | Mixed 或 Release |
| Deployment policy | 生产用 Disable redeploy |
这个仓库专门放商业 SDK、国产中间件适配包、客户提供的 jar。不要把所有公共依赖都塞进来。
然后创建代理仓库,选择 maven2 (proxy):
| 配置项 | 建议值 |
|---|---|
| Name | maven-central-proxy |
| Remote storage | 官方中央仓库、企业上级仓库或当前可用镜像 |
| Version policy | 通常用 Release |
中国大陆网络下,远程地址可以按企业规范选择 Maven Central、阿里云 Maven 公共仓库、上级 Nexus 或其他经过验证的镜像站。不要把某个第三方镜像写成不可替换的唯一方案。镜像站会变,Nexus proxy 的意义就是把变化收敛在服务端。
最后创建组仓库,选择 maven2 (group),名字用:
maven-public成员顺序建议:
1. maven-releases
2. maven-snapshots
3. maven-thirdparty
4. maven-central-proxy这里要注意顺序。内部包放前面,公共代理放后面。这样 Maven 查包时优先命中内部仓库,找不到再去公共代理。
创建完后,复制 group 仓库地址,后面会用到:
https://nexus.example.com/repository/maven-public/配置 Maven 使用私服
Maven 客户端主要改 settings.xml。开发机一般放在:
~/.m2/settings.xmlCI 里建议单独放一份受控模板,例如:
ci/settings.xml先看最小配置:
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
<mirrors>
<mirror>
<id>nexus-public</id>
<name>Nexus Maven Public</name>
<url>https://nexus.example.com/repository/maven-public/</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
</settings>这段配置解决的是“依赖从哪里下载”的问题。mirrorOf="*" 表示 Maven 的仓库请求都先走 Nexus。
验证:
mvn help:effective-settings输出里应该能看到 nexus-public。如果看不到,说明 Maven 没有读到你这份 settings.xml,要检查文件路径或 CI 命令是否用了 -s。
再实际拉一个依赖:
mvn -U dependency:get -Dartifact=org.slf4j:slf4j-api:2.0.17正常日志里应该看到:
Downloading from nexus-public: https://nexus.example.com/repository/maven-public/...如果日志还是访问公网地址,先查:
mvn help:effective-settings | grep -A5 -B2 nexus-public
mvn -X dependency:get -Dartifact=org.slf4j:slf4j-api:2.0.17-X 会打印更详细的仓库解析过程。排 Maven 私服问题时,effective-settings 和 -X 很有用。
配置 Maven 上传权限
下载只需要 mirror。上传还需要账号密码。把账号配置在 servers 里:
<servers>
<server>
<id>nexus-releases</id>
<username>${env.NEXUS_USER}</username>
<password>${env.NEXUS_PASSWORD}</password>
</server>
<server>
<id>nexus-snapshots</id>
<username>${env.NEXUS_USER}</username>
<password>${env.NEXUS_PASSWORD}</password>
</server>
</servers>这里最容易错的是 <id>。它不是显示名,也不是仓库名随便写。后面 pom.xml 里的 distributionManagement 会用这个 id 去找账号密码。
项目的 pom.xml 里加:
<distributionManagement>
<repository>
<id>nexus-releases</id>
<name>Nexus Releases</name>
<url>https://nexus.example.com/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>nexus-snapshots</id>
<name>Nexus Snapshots</name>
<url>https://nexus.example.com/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>执行发布:
export NEXUS_USER=ci-maven-deploy
export NEXUS_PASSWORD='********'
mvn clean deploy如果项目版本是:
<version>1.0.0</version>会发到 maven-releases。如果版本是:
<version>1.0.0-SNAPSHOT</version>会发到 maven-snapshots。
发布成功后,先不要只看命令退出码。再验证一次能不能从私服拉回来:
mvn -U dependency:get -Dartifact=com.example:order-api:1.0.0也可以直接用 curl 看 POM 是否存在:
curl -I -u "$NEXUS_USER:$NEXUS_PASSWORD" \
https://nexus.example.com/repository/maven-releases/com/example/order-api/1.0.0/order-api-1.0.0.pom如果这里是 200 或 302,说明地址和权限大体没问题。 如果是 401,先查账号和 server id。 如果是 404,先查版本路径、仓库类型和是否真的上传成功。
上传单个第三方 jar
有些商业 jar 不在公共仓库里,这时不要让每个开发各自 mvn install:install-file。本地 install 只解决你自己电脑,团队共享要上传到 Nexus。
如果有 POM 文件,推荐这样传:
mvn deploy:deploy-file \
-DgroupId=com.vendor \
-DartifactId=vendor-sdk \
-Dversion=1.2.3 \
-Dpackaging=jar \
-Dfile=vendor-sdk-1.2.3.jar \
-DpomFile=vendor-sdk-1.2.3.pom \
-DrepositoryId=nexus-releases \
-Durl=https://nexus.example.com/repository/maven-thirdparty/这条命令解决的是“把一个不属于当前项目构建产物的 jar 上传到私服”。几个参数要对上:
| 参数 | 说明 |
|---|---|
groupId | 组织坐标 |
artifactId | 包名 |
version | 版本号 |
file | 要上传的 jar |
pomFile | 完整 POM,能保留传递依赖 |
repositoryId | 对应 settings.xml 里的 server id |
url | 目标 hosted 仓库地址 |
如果没有 POM,可以让 Maven 生成简单 POM:
mvn deploy:deploy-file \
-DgroupId=com.vendor \
-DartifactId=vendor-sdk \
-Dversion=1.2.3 \
-Dpackaging=jar \
-Dfile=vendor-sdk-1.2.3.jar \
-DgeneratePom=true \
-DrepositoryId=nexus-releases \
-Durl=https://nexus.example.com/repository/maven-thirdparty/这里要注意:只传 jar、不传完整 POM,传递依赖可能丢失。如果这个 SDK 自己还依赖别的 jar,建议补齐 POM,不然后面其他项目引用时可能编译失败。
本地验证:
rm -rf ~/.m2/repository/com/vendor/vendor-sdk
mvn -U dependency:get -Dartifact=com.vendor:vendor-sdk:1.2.3删除本地缓存后还能拉下来,才说明团队其他人也能正常用。
创建 npm 仓库
npm 也按 hosted、proxy、group 来建。
进入:
Repository -> Repositories -> Create repository创建 npm (hosted):
| 配置项 | 建议值 |
|---|---|
| Name | npm-hosted |
| 用途 | 发布公司内部 npm 包 |
创建 npm (proxy):
| 配置项 | 建议值 |
|---|---|
| Name | npm-proxy |
| Remote storage | 官方 npm registry 或企业认可镜像 |
创建 npm (group):
| 配置项 | 建议值 |
|---|---|
| Name | npm-public |
| Member repositories | npm-hosted、npm-proxy |
同样的原则:安装依赖走 npm-public,发布内部包走 npm-hosted。
group 地址一般是:
https://nexus.example.com/repository/npm-public/hosted 地址一般是:
https://nexus.example.com/repository/npm-hosted/配置 npm 使用私服
项目根目录新建 .npmrc:
registry=https://nexus.example.com/repository/npm-public/
always-auth=true如果内部包统一使用 scope,比如 @company/order-ui,可以这样写:
@company:registry=https://nexus.example.com/repository/npm-public/
registry=https://nexus.example.com/repository/npm-public/
always-auth=true验证当前 registry:
npm config get registry应该输出:
https://nexus.example.com/repository/npm-public/测试 Nexus npm 仓库是否可用:
npm ping --registry=https://nexus.example.com/repository/npm-public/
npm view lodash version --registry=https://nexus.example.com/repository/npm-public/如果 npm ping 不通,先查网络、证书、Nexus 仓库地址。 如果 npm view lodash version 失败,通常是 proxy upstream 不通、group 没包含 proxy、或者 lockfile 里写死了旧 registry。
发布内部包时,不要把 token 写进代码仓库。CI 里可以临时生成 .npmrc:
registry=https://nexus.example.com/repository/npm-public/
@company:registry=https://nexus.example.com/repository/npm-public/
//nexus.example.com/repository/npm-hosted/:_authToken=${NPM_TOKEN}
always-auth=true这里要注意 token 这一行。它要绑定到 npm-hosted 的 URL 下,不要写成一个全局 token。否则 npm 可能把凭证发到不该发的 registry。
登录验证:
npm login --registry=https://nexus.example.com/repository/npm-hosted/
npm whoami --registry=https://nexus.example.com/repository/npm-hosted/发布:
npm publish --registry=https://nexus.example.com/repository/npm-hosted/发布后再从 group 读取:
npm view @company/order-ui versions --registry=https://nexus.example.com/repository/npm-public/npm 包不要覆盖同版本。发错了就升版本号,别让同一个版本对应两份不同内容:
npm version patch
npm publish --registry=https://nexus.example.com/repository/npm-hosted/npm 里容易漏掉的二进制下载
很多人以为 npm registry 改成 Nexus 就万事大吉了。实际项目里经常还有一类包会绕过 registry,自己下载二进制文件,比如:
node-sasssharppuppeteerplaywright
它们安装时可能去单独的下载地址拿二进制包。企业内网里,这一步经常失败。排查时可以先看 npm 日志:
npm ci --verbose如果日志里出现了跟 registry 不一样的下载地址,就说明问题不在 Nexus npm proxy 本身,而在二进制源。
生产建议:
- 尽量选择不依赖额外二进制下载的包版本。
- 必须使用时,把二进制包下载源也纳入企业镜像或离线包。
- CI 构建镜像里固定 Node 版本和操作系统。
package-lock.json必须提交,避免每次解析出不同依赖。
通用制品用 Raw 仓库
不是所有东西都适合 Maven 或 npm。比如:
- 后端服务压缩包。
- 前端
dist压缩包。 - SQL 初始化包。
- 离线安装包。
- 客户现场交付包。
这类文件可以建一个 raw (hosted) 仓库,例如:
raw-releases上传:
curl -u "$NEXUS_USER:$NEXUS_PASSWORD" \
--upload-file order-service-1.4.2.tar.gz \
https://nexus.example.com/repository/raw-releases/order-service/1.4.2/order-service-1.4.2.tar.gz这条命令解决的是“把一个普通文件上传到 Nexus”。上传后要再传校验文件:
sha256sum order-service-1.4.2.tar.gz > order-service-1.4.2.tar.gz.sha256
curl -u "$NEXUS_USER:$NEXUS_PASSWORD" \
--upload-file order-service-1.4.2.tar.gz.sha256 \
https://nexus.example.com/repository/raw-releases/order-service/1.4.2/order-service-1.4.2.tar.gz.sha256下载验证:
curl -L -u "$NEXUS_USER:$NEXUS_PASSWORD" \
-o order-service-1.4.2.tar.gz \
https://nexus.example.com/repository/raw-releases/order-service/1.4.2/order-service-1.4.2.tar.gz
curl -L -u "$NEXUS_USER:$NEXUS_PASSWORD" \
-o order-service-1.4.2.tar.gz.sha256 \
https://nexus.example.com/repository/raw-releases/order-service/1.4.2/order-service-1.4.2.tar.gz.sha256
sha256sum -c order-service-1.4.2.tar.gz.sha256看到 OK 才说明文件没有传坏。
Raw 仓库目录建议按这个方式组织:
应用名/版本号/文件名不要上传 latest.zip。生产环境里,latest 这类名字很难追踪。出了问题,你需要知道当时发布的是哪个版本、哪个 commit、哪个构建号。
中国大陆、内网和离线环境怎么做
Nexus 在国内和内网环境里最有价值。因为你不能假设所有机器都能访问 Maven Central、npm 官方仓库、GitHub 和各种二进制下载源。
一般按三种环境设计。
第一种,有公网但速度不稳定:
开发机 / CI -> Nexus group -> Nexus proxy -> 官方仓库或企业认可镜像这里的重点是,所有开发机都只配 Nexus。上游源慢了或者要切换,只改 Nexus proxy,不要让每个项目自己改。
第二种,企业内网不能直接访问公网:
内网开发机 / 内网 CI -> 内网 Nexus -> 上级 Nexus 或同步包可以在可联网区放一个上级 Nexus,内网 Nexus 再代理它。这样内网机器不用直接访问公网。
第三种,完全离线:
可联网环境预热依赖
-> 导出包和校验文件
-> 离线环境导入 Nexus
-> 用真实项目构建验证Maven 预热可以这样做:
mvn -Dmaven.repo.local=./m2repo dependency:go-offline
mvn -Dmaven.repo.local=./m2repo clean package -DskipTests
tar -czf m2repo-preheat.tar.gz m2repo
sha256sum m2repo-preheat.tar.gz > m2repo-preheat.tar.gz.sha256这能临时解决“把依赖带到现场”的问题,但长期不建议靠 .m2 压缩包治理团队依赖。更稳的是把依赖沉淀到离线 Nexus。
npm 也可以预热缓存:
npm ci --cache .npm-cache --prefer-offline
tar -czf npm-cache-preheat.tar.gz .npm-cache
sha256sum npm-cache-preheat.tar.gz > npm-cache-preheat.tar.gz.sha256这里要注意,npm cache 不是私服。离线生产环境最终还是要有 Nexus npm 仓库、lockfile 和可复现构建流程。
CI 接入时不要只改一条命令
很多团队接入 Nexus,只改了一个 registry 地址,后面还是会不稳定。CI 里至少要做这几步:
检查 Nexus 可达
-> 注入凭证
-> 使用统一 settings.xml / .npmrc
-> 拉依赖
-> 构建
-> 上传制品
-> 再下载验证Maven 示例:
set -euo pipefail
curl -fsS https://nexus.example.com/service/rest/v1/status >/dev/null
export NEXUS_USER="$CI_NEXUS_USER"
export NEXUS_PASSWORD="$CI_NEXUS_PASSWORD"
mvn -s ci/settings.xml -U clean verify
mvn -s ci/settings.xml deploy -DskipTests第一条 curl 是提前失败。如果 Nexus 都不可达,就不要等 Maven 跑了半天才报错。
npm 示例:
set -euo pipefail
npm config set registry https://nexus.example.com/repository/npm-public/
npm ping --registry=https://nexus.example.com/repository/npm-public/
npm ci
npm test
npm publish --registry=https://nexus.example.com/repository/npm-hosted/这里要注意 npm ci。CI 里一般不要用会改 lockfile 的安装方式。lockfile 是可复现构建的入口,不能一边构建一边变。
Nexus 缓存和 CI 本地缓存不是一回事:
| 缓存 | 解决的问题 |
|---|---|
| Nexus proxy 缓存 | 团队级依赖来源和远程下载加速 |
Maven .m2 缓存 | 同一 CI 节点重复构建少下载 |
| npm cache | 同一 CI 节点重复安装少下载 |
| node_modules 缓存 | lockfile 不变时减少安装耗时 |
| 构建缓存 | webpack、Gradle 等二次构建加速 |
Nexus 管“依赖从哪里来”,CI 缓存管“这台构建机是不是每次都从头干活”。
权限不要全给 admin
Nexus 跑起来后,最容易偷懒的做法是大家都用 admin。短期方便,长期一定出事。
建议拆这些账号:
| 账号 | 用途 |
|---|---|
admin | 初始化、升级、紧急维护 |
ci-maven-deploy | 发布 Maven 包 |
ci-npm-publish | 发布 npm 包 |
dev-readonly | 开发机只读拉依赖 |
ops-maintainer | 维护仓库、清理任务、巡检 |
角色也按用途拆:
role-maven-read
role-maven-deploy
role-npm-read
role-npm-publish
role-raw-upload
role-repo-admin这里要注意最小权限。发布账号只需要对目标 hosted 仓库有 add/edit/read 权限,不需要管理整个 Nexus。只读账号只需要 group 的 read/browse 权限,不需要上传权限。
CI 凭证不要提交到 Git。Maven 可以用环境变量注入 settings.xml,npm 可以在流水线里临时生成 .npmrc。构建结束后,工作区最好清理掉凭证文件。
磁盘为什么会越用越大
Nexus 最常见的生产问题是磁盘增长。增长来源一般有这些:
- Maven proxy 缓存了越来越多公共依赖。
- npm proxy 缓存大量小包和历史版本。
- SNAPSHOT 每天发布很多次。
- Raw 仓库上传了大量发布包。
- 删除组件后没有 compact blob store。
先看磁盘:
df -h
du -sh /data/nexus/nexus-data
du -sh /data/nexus/nexus-data/blobs/* 2>/dev/null || truedf -h 看磁盘剩余。 du -sh /data/nexus/nexus-data 看 Nexus 总占用。 blobs/* 可以帮助判断哪个 blob store 长得最快。
这里要注意,不要直接进 blob store 目录手动删文件。Nexus 的 blob 和元数据有关联,手动删会破坏仓库一致性。
清理要走 Nexus 的 cleanup policy 和 task。一般策略是:
| 对象 | 清理建议 |
|---|---|
| Maven releases | 按项目生命周期保留,不轻易删 |
| Maven snapshots | 保留最近 N 天或最近 N 个版本 |
| Maven proxy cache | 清理长期未访问缓存 |
| npm hosted | 内部包按版本策略保留 |
| npm proxy | 清理长期未访问缓存 |
| Raw 包 | 按发布和回滚周期保留 |
删除后不一定立刻释放磁盘,还需要 compact blob store。建议在低峰期做:
配置 cleanup policy
-> 执行 cleanup task
-> 验证依赖拉取
-> 执行 compact blob store
-> 再看 df -h备份不是复制一个目录就完事
Nexus 备份要覆盖这些东西:
- 数据库和元数据。
- blob stores。
- 仓库配置。
- 用户、角色、token。
- 证书和反向代理配置。
compose.yaml或安装脚本。- 当前 Nexus 版本。
单机小规模环境,最稳的是冷备。先停服务:
cd /opt/nexus
docker compose stop打包数据:
sudo tar --xattrs --acls -czf /backup/nexus-data-$(date +%F-%H%M%S).tar.gz \
-C /data/nexus nexus-data生成校验:
sha256sum /backup/nexus-data-*.tar.gz > /backup/nexus-backup.sha256启动服务:
docker compose up -d
docker logs --tail=100 nexus生产环境要按官方备份文档来设计,尤其是数据库备份、blob store 备份和恢复演练。备份文件生成了不等于可恢复,必须找一台测试机恢复一次。
恢复后至少验证:
curl -fsS https://nexus.example.com/service/rest/v1/status
mvn -U dependency:get -Dartifact=org.slf4j:slf4j-api:2.0.17
npm ping --registry=https://nexus.example.com/repository/npm-public/
npm view lodash version --registry=https://nexus.example.com/repository/npm-public/这些命令分别验证:Nexus 服务活着、Maven 仓库能用、npm 仓库能用。
Maven 401 怎么排
Maven 上传时报 401,一般不是 Maven 坏了,而是认证没对上。
典型现象:
Return code is: 401, ReasonPhrase: Unauthorized先看 Maven 最终读到的配置:
mvn help:effective-settings确认里面有没有对应的 server:
<server>
<id>nexus-releases</id>
...
</server>再看项目里的 distributionManagement:
<repository>
<id>nexus-releases</id>
<url>https://nexus.example.com/repository/maven-releases/</url>
</repository>两个 id 必须一致。
再用 curl 验证账号:
curl -I -u "$NEXUS_USER:$NEXUS_PASSWORD" \
https://nexus.example.com/repository/maven-releases/如果 curl 都 401,先查账号密码和 Nexus 权限。 如果 curl 正常,Maven 401,多半是 settings.xml 没被 Maven 读到,或者 repositoryId 写错了。
Maven 404 怎么排
Maven 拉依赖 404,先判断包是不是真的存在。
mvn -X dependency:get -Dartifact=com.example:demo:1.0.0再直接访问 POM:
curl -I https://nexus.example.com/repository/maven-public/com/example/demo/1.0.0/demo-1.0.0.pom常见原因:
- 包没有上传。
- 上传到了
maven-snapshots,但你拉的是 release。 - group 没包含目标 hosted 仓库。
- Maven 配错了仓库地址。
- proxy upstream 没有这个包。
- Nexus 缓存了之前的 404。
进入 Nexus 控制台,按路径 Browse 一遍。能在 hosted 仓库里看到 POM 和 jar,再去查 group。hosted 里都没有,就不要纠结 Maven 客户端了,先把包上传对。
SNAPSHOT 不更新怎么排
SNAPSHOT 经常出现“我明明发布了,别人还是旧版本”。先强制更新:
mvn -U clean package再删除本地缓存验证:
rm -rf ~/.m2/repository/com/example/demo
mvn -X dependency:get -Dartifact=com.example:demo:1.0.0-SNAPSHOT如果还是旧版本,查这几件事:
maven-snapshots的 Version policy 是否是 Snapshot。maven-publicgroup 是否包含 snapshots 仓库。settings.xml是否开启 snapshots。- CI 节点是否复用了旧
.m2缓存。 - 项目是不是其实发到了另一个 Nexus。
生产建议很简单:SNAPSHOT 只用于开发和集成,正式发布用 release 版本。
npm 401 怎么排
npm 401 常见于 token、registry 和权限不一致。
先看配置:
npm config list
npm config get registry再看能不能登录 hosted:
npm whoami --registry=https://nexus.example.com/repository/npm-hosted/如果失败,检查 .npmrc 里 token 的 URL:
//nexus.example.com/repository/npm-hosted/:_authToken=${NPM_TOKEN}这里必须和发布命令里的 registry 对上:
npm publish --registry=https://nexus.example.com/repository/npm-hosted/如果 URL 对了还是 401,就查 Nexus 账号是否有 npm hosted 的发布权限。只读账号能安装包,不代表能 publish。
npm 404 怎么排
npm 404 先看 scope 和 lockfile。
npm view @company/order-ui versions --registry=https://nexus.example.com/repository/npm-public/如果这里查不到,去 Nexus 控制台看 npm-hosted 是否真有这个包,再看 npm-public group 是否包含 npm-hosted。
再检查项目里有没有旧 registry:
grep -R "registry" package-lock.json npm-shrinkwrap.json .npmrc 2>/dev/nulllockfile 里如果写死了旧地址,CI 可能还会去旧 registry 拉包。修复后重新生成 lockfile,并提交到仓库。
下载慢怎么定位
下载慢不要只说“私服慢”。先分段:
| 慢的位置 | 怎么判断 |
|---|---|
| 开发机到 Nexus 慢 | curl Nexus 地址耗时高 |
| Nexus 到 upstream 慢 | 首次下载慢,缓存后变快 |
| CI 每次都慢 | CI 本地缓存没复用 |
| npm 慢 | 二进制包绕过 registry |
| Maven 慢 | proxy upstream 慢或依赖树太大 |
看 HTTP 耗时:
curl -w "\nconnect=%{time_connect} start=%{time_starttransfer} total=%{time_total}\n" \
-o /dev/null -s https://nexus.example.com/repository/maven-public/看 Maven 解析:
mvn -X dependency:get -Dartifact=org.slf4j:slf4j-api:2.0.17看 npm 安装耗时:
npm ci --timing --registry=https://nexus.example.com/repository/npm-public/如果第一次慢、第二次快,说明 proxy 缓存起作用了。 如果每次都慢,查 CI 本地缓存、Nexus 到 upstream 网络、二进制包下载源。
Nexus 启动失败怎么排
先看容器:
docker ps -a --filter name=nexus再看日志:
docker logs --tail=300 nexus看端口:
ss -lntp | grep 8081 || true看磁盘和内存:
df -h
free -h看数据目录权限:
ls -ld /data/nexus/nexus-data权限问题可以修:
sudo chown -R 200:200 /data/nexus/nexus-data
docker compose up -d如果是升级后启动失败,不要反复重启。先备份当前数据目录,读日志和 Release Notes,再决定修复或回退。
落地工程深水区
Nexus 深水区在于:它看起来只是下载依赖,实质上托住了研发、构建、离线交付和回滚。
容量和清理:Maven snapshot、npm tarball、Raw 包和代理缓存都会增长。清理策略要分仓库配置,先 preview,再执行 cleanup 和 compact blob store。只删组件不 compact,磁盘不一定立刻下降。
df -h
du -sh /data/nexus/nexus-data
du -sh /data/nexus/nexus-data/blobs/* 2>/dev/null || true权限和发布边界:开发机通常只需要读 group 仓库;CI 发布账号只允许 deploy 到 releases/snapshots 或 npm hosted;第三方 jar 上传要有审批和命名规则。不要所有人共用 admin。
缓存和可重复构建:Nexus 代理仓库不是“万能加速器”。如果上游包被删除、lockfile 漂移、SNAPSHOT 被覆盖,构建仍会不稳定。生产发布要保存最终制品和校验值,不能只依赖“能重新构建一次”。
备份和恢复:备份要覆盖 blob store、数据库、配置和安全数据。恢复演练要验证 Maven 下载、npm 下载、制品上传和权限,不是只看控制台能打开。
升级兼容:Nexus 近版本对 Java、数据库、S3 blob store SDK 都有变化。升级前先看系统要求、版本状态页、Release Notes 和插件兼容性;用 S3 兼容存储时尤其要验证非 AWS 兼容性。
团队协作:settings.xml、.npmrc、仓库命名、发布版本号、清理窗口、备份责任要形成团队模板。否则 Nexus 会变成“谁都能上传、谁都不敢删”的大仓库。
生产上线前检查清单
最后给一份上线前检查。Nexus 一旦被 CI 和生产发布依赖,它就是基础设施,不是一个可有可无的小工具。
- Nexus 版本、安装方式、数据目录、端口和域名已经记录。
- Maven hosted、proxy、group 已创建并验证。
- npm hosted、proxy、group 已创建并验证。
- Raw 仓库已经有命名规范和校验文件要求。
- 开发机和 CI 都只通过 group 拉依赖。
- CI 发布账号和开发只读账号分离。
- admin 不用于日常发布。
- Maven
settings.xml和 npm.npmrc有统一模板。 - 中国大陆、内网、离线环境的 upstream 和导入方案已经确定。
- 清理策略、compact blob store、磁盘告警已经设计。
- 备份脚本和恢复演练已经做过。
- 至少用一个 Maven 项目、一个 npm 项目、一个 Raw 包跑通过上传和下载。
日常巡检命令:
curl -fsS https://nexus.example.com/service/rest/v1/status
df -h
du -sh /data/nexus/nexus-data
docker ps --filter name=nexus
docker logs --tail=100 nexus真正可靠的私服,不是控制台能打开,而是依赖能拉、制品能发、权限清楚、磁盘可控、备份能恢复、故障能定位。
常用命令速查
启动和日志:
docker compose up -d
docker compose stop
docker logs -f nexus
docker exec nexus cat /nexus-data/admin.passwordMaven 下载验证:
mvn help:effective-settings
mvn -U dependency:get -Dartifact=org.slf4j:slf4j-api:2.0.17Maven 发布:
mvn clean deployMaven 上传单个 jar:
mvn deploy:deploy-file \
-DgroupId=com.vendor \
-DartifactId=vendor-sdk \
-Dversion=1.2.3 \
-Dpackaging=jar \
-Dfile=vendor-sdk-1.2.3.jar \
-DrepositoryId=nexus-releases \
-Durl=https://nexus.example.com/repository/maven-thirdparty/npm 验证:
npm config get registry
npm ping --registry=https://nexus.example.com/repository/npm-public/
npm view lodash version --registry=https://nexus.example.com/repository/npm-public/npm 发布:
npm publish --registry=https://nexus.example.com/repository/npm-hosted/Raw 上传:
curl -u "$NEXUS_USER:$NEXUS_PASSWORD" \
--upload-file app.tar.gz \
https://nexus.example.com/repository/raw-releases/app/1.0.0/app.tar.gz磁盘巡检:
df -h
du -sh /data/nexus/nexus-data
du -sh /data/nexus/nexus-data/blobs/* 2>/dev/null || true官方文档入口
- Sonatype Nexus Repository 下载页
- Sonatype Nexus Repository 3 Versions Status
- Sonatype Nexus Repository 3.94.0 Release Notes
- Sonatype Nexus Repository System Requirements
- Sonatype Repository Types
- Sonatype Maven Repositories
- Sonatype npm Registry
- Sonatype Cleanup Policies
- Sonatype Backup
- Apache Maven Settings Reference
- Apache Maven Mirror Settings Guide
- npm registry 文档
- npm .npmrc 文档
- 阿里云 Maven 仓库服务
- npmmirror 镜像站
