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 为准。官方版本状态页显示 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-sass、sharp、puppeteer、playwright。
它们安装时可能去单独的下载地址拿二进制包。企业内网里,这一步经常失败。排查时可以先看 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-public group 是否包含 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 || trueSonatype 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 文档
