首页 / 资讯中心 / 文章详情

Nexus 3.24内网制品库搭建:Maven/NPM私服与运维避坑

Nexus 3.24内网制品库搭建:Maven/NPM私服与运维避坑 ★ FEATURED ARTICLE
简介这份资源是 Sonatype 官方发布的 Nexus Repository Manager 3.24.0-02 Unix/Linux 安装包面向 Java 与前端开发团队中负责构建、依赖治理和私服运维的工程师。它解决的是 Maven、Gradle 依赖与 NPM 包分散下载、外网访问慢、内部组件难以统一托管的问题可用于搭建企业级制品仓库代理远程仓库并缓存常用依赖。压缩包约 149.69MB解压后分为服务程序目录与 sonatype-work 运行时数据目录前者含可执行文件、配置与依赖库后者承载制品存储、索引、日志及数据库内容升级时保持数据独立。通过修改 conf 下的配置文件并用 Web 界面管理可完成端口、代理仓库、存储库组与权限设置NPM 用户还可将 Nexus 设为默认注册表实现私有包托管与加速。目前已有 212 人学习下载适合需要落地私服或补齐依赖治理方案的中级开发者参考。1. 从 3.24.0-02 这个 tar.gz 说起内网为什么还要自己维护一个制品库nexus-3.24.0-02-unix.tar.gz 解出来就是一个能直接跑的 Nexus Repository Manager 3 实例Maven 的 jar、NPM 的 tarball 都能往里塞、往外发配置全在界面上点。它的价值不在于「又一个私服」而在于把构建链里最脆弱的一段——外网仓库——挪进内网中央仓库抖动、依赖被撤版、NPM 包作者删库这类事故在内网镜像面前就变成可控的缓存问题而不是集体构建失败。适合两个人以上、有 CI、有不能公开的包的后端和前端团队。下面按解包、开机自启、Maven 三层仓库、NPM 发布链路、排错、备份的顺序拆一遍都是我在内网环境里实际走通的路径。2. 解包到 systemd把 tar.gz 变成一个正经服务拿到 tarball 之后最容易犯的错是就地解压、用 root 跑、把数据目录和安装目录混在一起。跑到第三个月要做版本升级时你会发现安装目录里既有程序又有几百 G 的 blob回退只能整份拷回来。所以第一步不是启动而是把「可丢弃的程序」和「不能丢的数据」在磁盘上分开然后交给 systemd 托管让启停顺序、文件句柄上限、停止超时都可控。2.1 落地前的三件事账号、磁盘、JDK账号必须是不可登录的系统账号因为 Nexus 会以运行用户身份写 blob 和 OrientDB 的本地存储用 root 跑出来的文件后面换成普通用户启动时会一片 Permission denied。磁盘要单独规划blob 是只增不减的删掉组件只是删元数据磁盘空间要等压缩任务跑完才回来。文件系统用 xfs 或 ext4别放 NFS——3.24 的元数据还在内嵌 OrientDB 的 plocal 存储里它对文件锁的假设很强网络盘上跑起来可能几天没事、某次重启后数据库直接打不开。JDK 这条线的主跑环境是 JDK 8装完先确认JAVA_HOME指向的是 JDK 不是 JREbin/nexus启动脚本靠它找 java。# 1) 系统账号不可登录、无家目录依赖 sudo groupadd -r nexus sudo useradd -r -g nexus -M -d /opt/nexus -s /sbin/nologin nexus # 2) 解包到 /opt安装目录改名成不带版本号的路径方便以后软链切换 sudo tar -xzf nexus-3.24.0-02-unix.tar.gz -C /opt sudo mv /opt/nexus-3.24.0-02 /opt/nexus # 3) 数据目录独立挂盘先建空目录首次启动时 Nexus 自己初始化 sudo mkdir -p /data/nexus-data sudo chown -R nexus:nexus /opt/nexus /data/nexus-data sudo chmod 750 /data/nexus-data # 4) 确认 JDK 版本与路径 java -version # 期望 1.8.x readlink -f $(which java)--strip-components这里没用是因为官方包根目录下同时有nexus-3.24.0-02/和sonatype-work/两个目录剥掉一层会把两者混在一起。数据目录改成/data/nexus-data之后安装目录就变成了「随时可以删掉重来」的东西这一点在升级和回滚时会救命。2.2 bin/nexus 与 vmoptions内存参数别照抄bin/nexus是启动器子命令有start / stop / run / status / restart。调试阶段用run前台跑日志直接刷屏起不来时能立刻看到堆栈生产用start它自己会后台化。内存不写在启动脚本里而是写在同目录的nexus.vmoptions格式是一行一个 JVM 参数没有-Dkeyvalue以外的语法糖。# /opt/nexus/bin/nexus.vmoptions节选只列需要改的 -Xms2703m -Xmx2703m -XX:MaxDirectMemorySize2703m # 数据目录改成绝对路径不依赖安装目录的相对位置 -Dkaraf.data/data/nexus-data -Dkaraf.log/data/nexus-data/log -Djava.io.tmpdir/data/nexus-data/tmp三个内存值取同一个数是有原因的堆内存和直接内存是两块独立的账Nexus 上传下载走的是直接内存Netty / Jetty 的缓冲区只把-Xmx调大、MaxDirectMemorySize留着小值表现就是大 jar 传到 99% 报 500日志里是OutOfMemoryError: Direct buffer memory。经验值是三者之和不超过机器内存的 60%8G 的机器给 2703m 刚好16G 可以给到 4G 一档。改完内存重启如果发现没生效先看/data/nexus-data/log/jvm.log开头那行完整启动参数——INSTALL4J_ADD_VM_PARAMS这个环境变量会和nexus.vmoptions叠加两处都设了内存时最终生效的是哪一组只有 jvm.log 说了算别靠猜。2.3 systemd 托管与三个启动检查点# /etc/systemd/system/nexus.service [Unit] DescriptionNexus Repository Manager 3.24.0-02 Afternetwork.target [Service] Typeforking Usernexus Groupnexus EnvironmentJAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64 LimitNOFILE65536 ExecStart/opt/nexus/bin/nexus start ExecStop/opt/nexus/bin/nexus stop Restarton-failure TimeoutStopSec180 [Install] WantedBymulti-user.targetTypeforking是因为nexus start会自己 fork 到后台用 simple 会让 systemd 误判主进程退出。LimitNOFILE不给够并发上传时日志里会出现Too many open files而且报的位置往往在很深的 IO 栈里不容易第一眼认出来。TimeoutStopSec180是给 OrientDB 关库留的时间systemd 默认 90 秒就 SIGKILL被强杀之后下次启动要做恢复慢的话能卡上好几分钟。sudo systemctl daemon-reload sudo systemctl enable --now nexus sudo systemctl status nexus --no-pager # 检查点 1日志里出现启动完成标记 tail -f /data/nexus-data/log/nexus.log | grep -i Started Sonatype Nexus # 检查点 2端口在听 ss -lntp | grep 8081 # 检查点 3HTTP 有响应302 跳登录页也算正常 curl -sI http://127.0.0.1:8081/ | head -n 1首次登录的初始密码不在配置文件里而是首次启动时随机生成在/data/nexus-data/admin.password登录后立刻改掉改完这个文件会被删除——找不到它说明已经有人初始化过了。端口和上下文路径在etc/nexus.properties里改application-port换端口nexus-context-path换路径前缀。改路径前缀是个连锁操作所有客户端的仓库地址都要跟着改非必要不动。3. Maven 私服proxy、hosted、group 三层怎么摆Maven 侧的设计核心只有一句话客户端只认一个 group 地址其余全部由服务端决定。这样settings.xml在团队里发一次就够后面加仓库、换镜像、下线某个代理都不用通知任何人改配置。反过来如果让每个人的settings.xml里写三四个仓库地址那你以后每次调整都会变成一次全员协同。3.1 三种仓库类型的职责边界仓库名类型Version policyDeployment policy作用maven-centralproxyRelease—代理 repo1.maven.org负责缓存公共依赖maven-releaseshostedReleaseDisable redeploy团队正式版本禁止覆盖maven-snapshotshostedSnapshotAllow redeploy联调快照允许反复覆盖maven-publicgroup——对外唯一入口成员按顺序聚合上面三个Disable redeploy是刻意的正式包一旦允许覆盖同一个版本号在两个人机器上可能是两份不同的字节线上问题就无法复现。快照仓库反过来必须允许覆盖否则每次联调都得升版本号-SNAPSHOT的意义就没了。group 的成员顺序有实际影响查找是从上往下命中的把自己维护的maven-releases、maven-snapshots放在maven-central前面同名坐标会优先命中内部包——这在打补丁替换某个第三方包时非常有用也意味着如果有人误传了一个和公共包同坐标的包全团队都会拿到它。我一般会把顺序固定成 snapshots → releases → central并在群里说明这条规则避免有人图省事往 releases 里塞别人的包。3.2 建 maven-public 组与 settings.xml 写法界面上按齿轮 → Repository → Repositories → Create repository 依次建先maven2 (proxy)填远程地址https://repo1.maven.org/maven2/再两个maven2 (hosted)最后maven2 (group)把前三个加进 Members。建完在 group 页面右上能看到完整的仓库 URL复制下来。!-- ~/.m2/settings.xml -- settings xmlnshttp://maven.apache.org/SETTINGS/1.0.0 mirrors mirror idnexus-public/id nameinternal maven-public/name urlhttp://nexus.lab.local:8081/repository/maven-public//url !-- 全部走内网个别第三方仓库要直连时用 *,!repoId 排除 -- mirrorOf*/mirrorOf /mirror /mirrors servers server idnexus-releases/id usernamedeployer/username password{加密串}/password /server server idnexus-snapshots/id usernamedeployer/username password{加密串}/password /server /servers /settingsid是这套配置里唯一不能随便写的字段mirror的 id 决定本地缓存归属server的 id 必须和pom.xml里distributionManagement的仓库 id 逐字一致不一致就是 401而且报错信息只说Unauthorized不会告诉你是哪个 id 对不上。密码用mvn --encrypt-password生成密文别在 settings.xml 里留明文——这个文件经常被顺手提交进 dotfiles 仓库。mirrorOf*有个副作用它会拦下所有仓库请求包括某些 pom 里自定义的第三方仓库表现为那个依赖永远拉不下来、只报 404。真遇到这种情况用*,!thirdparty-repo把那个仓库 id 排除掉而不是把 mirror 关了。3.3 发布到 hosteddistributionManagement 与 redeploy 策略distributionManagement repository idnexus-releases/id urlhttp://nexus.lab.local:8081/repository/maven-releases//url /repository snapshotRepository idnexus-snapshots/id urlhttp://nexus.lab.local:8081/repository/maven-snapshots//url /snapshotRepository /distributionManagement# 正式包发布 mvn -B clean deploy -DskipTests # 第三方 jar 没有源码、无法自己构建时直接推上去 mvn deploy:deploy-file \ -DgroupIdcom.demo -DartifactIdlegacy-sdk -Dversion1.2.3 \ -Dpackagingjar -Dfilelegacy-sdk-1.2.3.jar \ -DrepositoryIdnexus-releases \ -Durlhttp://nexus.lab.local:8081/repository/maven-releases/-B是批处理模式CI 里不加会有满屏的下载进度-DskipTests只跳过测试执行、测试代码照样编译想连编译一起跳过得用-Dmaven.test.skiptrue两者混用是常见的「本地能过 CI 过不了」来源之一。deploy:deploy-file的-DrepositoryId同样要和 settings.xml 的 server id 对上这条命令是我用得最多的一个——老系统里那些没有源码的 jar靠它才能进内网仓库。版本号已经是1.2.3却又发了一次会得到400 Repository does not allow updating assets。这不是权限问题是Disable redeploy在起作用正确做法是升版本号临时把策略改成Allow redeploy能过但那等于把可复现性丢了。改完 mirror 之后本地还可能有旧缓存残留~/.m2/repository下的_remote.repositories记录了来源仓库 id对不上时会重新下载实在诡异就把对应目录连同*.lastUpdated一起删掉重来。4. NPM 私服从 proxy 到 npm publish 的完整链路前端这边比 Maven 更容易踩坑因为 NPM 的认证走的是 Bearer Token和 Maven 的 Basic Auth 不是一套机制Nexus 里对应的 Realm 默认没开。很多人装完 Maven 一切正常转去配 NPM 就卡在 401来回改.npmrc改到怀疑人生其实问题在服务端的开关上。4.1 三个 npm 仓库怎么组合仓库名类型关键配置作用npm-proxyproxyRemote storage: https://registry.npmjs.org缓存公共包npm-hostedhostedDeployment policy: Allow redeploy放内部包、私有 scopenpm-groupgroupMembers 顺序hosted 在前proxy 在后对外唯一入口顺序是刻意的内部包优先命中npm-hosted公共包落到npm-proxy。如果反过来某个内部包名恰好和公共包重名比如内部改过的 fork客户端会拿到公共版本而且不会有任何提示——这种问题排查起来极其难受因为package.json里看不出任何异常。npm-hosted的部署策略选Allow redeployNPM 生态里同一个版本号重发是常态尤其是内网工具包禁了之后每次都要手动改版本号反而没人愿意发布。4.2 npm Bearer Token Realm 与 .npmrc 写法第一步不在命令行在界面齿轮 → Security → Realms把npm Bearer Token Realm从 Available 挪到 Active保存。这一步不做npm login一定失败。# 登录registry 指向 hosted不要指向 group npm login --registryhttp://nexus.lab.local:8081/repository/npm-hosted/ --always-auth # 发布在包目录下 npm publish --registryhttp://nexus.lab.local:8081/repository/npm-hosted/ # 安装走 group npm config set registry http://nexus.lab.local:8081/repository/npm-group/# .npmrc项目级只提交 registry 那两行token 走 CI 变量 registryhttp://nexus.lab.local:8081/repository/npm-group/ demo:registryhttp://nexus.lab.local:8081/repository/npm-group/ # 注意 key 是完整路径少写一段就是 401 //nexus.lab.local:8081/repository/npm-hosted/:_authTokenNpmToken.xxxxxxxx always-authtrue.npmrc里最容易错的是_authToken那一行的 key它不是一个域名而是协议://主机:端口/仓库路径/整串Nexus 按这个完整前缀匹配凭证。写成//nexus.lab.local:8081/:_authToken...在别的 registry 上能work在这里就会 401因为路径没对上。发布指向 hosted、安装指向 group也是同一个道理——group 是只读聚合入口往里 publish 时到底写进哪个成员取决于成员顺序与其事后去猜包落在哪不如发布时直接写 hosted 地址。4.3 用 curl 验证 group 顺序和 tarball 落点浏览器点得开不代表命令行能用中间还隔着代理、路径编码和缓存。三步验证从元数据到实际文件BASEhttp://nexus.lab.local:8081/repository/npm-group # 1) 公共包的元数据能不能取到 curl -s $BASE/lodash | head -c 200 # 2) 私有 scope 包斜杠必须编码成 %2f否则中间层可能直接 404 curl -s $BASE/demo%2futils | head -c 200 # 3) tarball 是否真的能下载200 说明 blob 落盘正常 curl -sI $BASE/lodash/-/lodash-4.17.21.tgz | head -n 1第 1 步通、第 3 步 404说明元数据缓存住了但 tarball 没取回来通常是 proxy 仓库的 Remote storage 地址写错或者出网策略只放行了元数据接口。第 2 步 404 而第 1 步正常八成是 scope 没在 hosted 仓库的配置里对上或者.npmrc里demo:registry写成了 group 但 group 成员顺序不对。用npm view pkg version --registry$BASE也能验证但 curl 更快而且能直接看到 HTTP 状态不用猜 npm 把错误包装成了什么。5. 避坑与排查五个真实翻车现场5.1 启动与资源类现象一第二次启动卡住日志里 OrientDB 报本地存储被锁。原因通常是上一次stop被 SIGKILL 掉了锁文件没释放或者更隐蔽的——复制了一份安装目录做「备份环境」却忘了改nexus.vmoptions里的-Dkaraf.data两个进程指向同一个数据目录。解决ps -ef | grep karaf.data确认只有一个 java 进程在跑bin/nexus status看状态把 systemd 的TimeoutStopSec调到 180 秒给足关库时间复制环境时务必把数据目录也复制一份并改绝对路径。现象二上传大 jar 到 99% 报 500界面上什么都没留下。先看/data/nexus-data/log/nexus.log如果是Direct buffer memory按 2.2 调MaxDirectMemorySize如果不是 OOM 而是写入失败df -h和df -i都跑一遍——inode 耗尽在小文件极多的 NPM 场景里很常见表现和磁盘满几乎一样但df -h显示还有空间很容易被忽略。5.2 依赖解析类现象三某个依赖中央仓库明明有内网就是 404而且过很久都不恢复。原因是 proxy 仓库把「没找到」也缓存了HTTP request settings里的Not found cache TTL默认是 1440 分钟也就是一次失败要背一天的锅。解决把这个值改成 0 或几分钟然后在仓库页面点Invalidate cache清掉负缓存再重试。这个坑最坑的地方在于你在这期间无论怎么验证「上游确实有这个包」都没用因为请求根本没出去。现象四mirrorOf*之后某个第三方仓库的依赖全挂了。原因是镜像规则把 pom 里声明的额外仓库一并拦下请求全打到 group而 group 里没有那个仓库。解决改成*,!thirdparty-repo精确排除或者在 group 里补一个对应 proxy。判断方法很简单mvn -X看依赖是从哪个 URL 解析的一眼就能看出是不是被拦了。5.3 权限与发布类现象五npm publish一直 401账号密码确认没错Maven 那边却能正常发布。原因就是 4.2 里说的npm Bearer Token Realm没启用Maven 走 Basic Auth 不受影响NPM 走 Token 直接被拒。解决Realms 里加上去重启不需要保存即生效然后重新npm login。如果 Realm 开了还是 403那就是权限问题——给发布账号的角色里补上nx-repository-view-npm-*-add和nx-repository-view-npm-*-edit而不是直接给 admin。6. 备份、清理与升级一套能复用的维护动作备份这件事在 3.24 上只有一种可靠做法停服务、整份拷、再启动。原因是元数据还在内嵌 OrientDB 的db/目录里热拷贝出来的文件大概率是坏的恢复时才发现就晚了。Admin - Export databases for backup这个任务可以在不停服的情况下导出一份数据库快照但它和 blob 目录不在同一时间点真要恢复还得靠冷备兜底。#!/usr/bin/env bash set -euo pipefail TS$(date %F) /opt/nexus/bin/nexus stop # 等进程真的退出OrientDB 关库不是瞬间完成的 while pgrep -f karaf.data/data/nexus-data /dev/null; do sleep 2; done tar -czf /backup/nexus-$TS.tar.gz \ -C /data nexus-data \ --excludenexus-data/tmp \ --excludenexus-data/log /opt/nexus/bin/nexus start find /backup -name nexus-*.tar.gz -mtime 7 -deletedb/和blobs/必须在同一个 tar 里、同一个时间点分开备份等于没备。tmp和log排除掉能省不少体积恢复时 Nexus 会自己重建。日常清理靠两个计划任务Admin - Compact blob store回收已删除组件的磁盘空间删除组件时磁盘不会立刻释放就是等它Maven - Delete SNAPSHOT按「保留最近 N 个、超过 N 天」清快照。这两个任务建议错开时间跑同时跑会争 IO。升级的后悔药只有一份升级前必须做一次完整冷备。做法是把安装目录做成软链/opt/nexus - /opt/nexus-3.24.0-02新版本解到旁边、nexus.vmoptions里的karaf.data仍指向老数据目录启动时它会自动跑数据库迁移——而迁移是单向的一旦新版本启过库旧版本就再也读不了了。我因为跳过一次备份在一次小版本升级里把测试环境的元数据搞成了半迁移状态最后是重建仓库配置、重传所有私有包才恢复的。从那以后我每次动版本号都强制先跑一遍那个 tar 脚本并当场解压一份到临时目录验证能起来再动车生产。这份 tar.gz 拿回去先在一台测试机上按第 2 章跑通比直接上生产稳得多。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站