背景
团队内部需要一套私有 Maven 仓库:托管内部构件(正式版 + 快照),同时代理 Maven 中央仓库、缓存依赖加速构建。选型 Nexus 3(Sonatype,业界标准),Docker 容器化部署,前端由 Caddy 反向代理自动签发 HTTPS 证书。
整体架构
说明:本文涉及的真实域名、服务器 IP、密码均已脱敏,示例中的 repo.example.com 为占位符。
公网 HTTPS ──> Caddy(自动证书)──> Nexus 3 容器(127.0.0.1:8081)
├─ maven-releases (hosted 正式版)
├─ maven-snapshots (hosted 快照版)
├─ maven-central (proxy 中央仓库代理)
└─ maven-public (group 日常统一入口)
仓库设计
| 仓库 | 类型 | 用途 | 写策略 |
|---|---|---|---|
| maven-releases | hosted | 正式版本发布 | ALLOW(允许重复发布) |
| maven-snapshots | hosted | 快照版本发布 | ALLOW(允许重复发布) |
| maven-central | proxy | 代理 Maven 中央仓库,内容永久缓存、元数据 24h 刷新 | — |
| maven-public | group | 分组(本地仓库优先),日常构建只依赖这一个地址 | — |
分组顺序为 [maven-releases, maven-snapshots, maven-central],本地构件优先于中央仓库同名构件。
账号与权限(已脱敏)
- admin:管理员账号,初始密码在容器内
/nexus-data/admin.password,首次登录后必须修改(修改后文件自动删除)。 - deploy:团队发布账号,绑定自定义角色
maven-deploy,仅授 releases / snapshots 的上传与读取、public 读取权限,无删除、无管理权限(删除构件由 admin 负责)。 - 匿名访问默认关闭,所有读写需携带账号;如需开放匿名读,在 Settings → Security → Anonymous 中开启。
客户端接入
1. ~/.m2/settings.xml(下载依赖)
<settings>
<servers>
<server><id>internal-releases</id><username>deploy</username><password>********</password></server>
<server><id>internal-snapshots</id><username>deploy</username><password>********</password></server>
</servers>
<mirrors>
<mirror>
<id>internal-mirror</id>
<url>https://repo.example.com/repository/maven-public/</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
</settings>
2. 项目 pom.xml(发布构件)
<distributionManagement>
<repository>
<id>internal-releases</id>
<url>https://repo.example.com/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>internal-snapshots</id>
<url>https://repo.example.com/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
版本号不带 -SNAPSHOT 走 releases,带 -SNAPSHOT 走 snapshots,均执行 mvn deploy。
部署踩坑记录
- EULA 未接受导致全部 403:新版 Nexus(3.7x+)首次启动后必须接受终端用户许可协议,否则所有仓库读写返回 403(响应体有明确提示)。通过 REST API 接受:先
GET /service/rest/v1/system/eula拿disclaimer原文,再POST /service/rest/v1/system/eula提交{"accepted":true,"disclaimer":"<原文>"}(缺少 disclaimer 会报 Invalid EULA disclaimer)。 - 预置仓库是空壳:默认模板生成的仓库配置为空(GET 单仓库只有
attributes:{},写策略缺失),admin 上传也 403。解决:删除后用 API 重建(DELETE /repositories/{name},POST /repositories/maven/hosted|proxy|group)。 - 先建账号后重建仓库导致授权失效:在重建仓库之前创建的发布用户,角色权限引用旧仓库权限对象,全部 403。解决:删除用户和角色重新创建。
- 严格内容校验(Tika 嗅探):上传 .jar 必须是真正的 ZIP 结构,纯文本伪装成 jar 会返回 400 “Detected content type [text/plain]”。
- DNS 未生效导致证书申请失败:Caddy 申请证书时域名还没解析,报 NXDOMAIN 后进入 20 分钟自动重试。DNS 生效后手动
systemctl reload caddy可立即触发重试,无需等待。
日常运维
- 日志:
docker logs nexus --tail 100,详细日志docker exec nexus tail -f /nexus-data/log/nexus.log - 备份(官方方式):
docker run --rm -v nexus-data:/data -v /backup:/backup alpine tar czf /backup/nexus-$(date +%F).tar.gz /data - 内存:Nexus 是 Java 应用,低配机器务必限制 JVM(本例
-Xms256m -Xmx512m -XX:MaxDirectMemorySize=256m -XX:MaxMetaspaceSize=256m),实测运行占用约 500M。 - 清理:建议在 UI 配置清理策略,定期删除长期未使用的快照构件。
