想给团队搭一套自己的 Maven 私服Nexus 几乎是绕不开的选项。我前后经手过四次同类搭建从最早的单机裸装、到后来的容器化部署、再到给多产品线做仓库拆分踩过的坑基本覆盖了从 JVM 内存到权限颗粒度的各个环节。这篇东西不讲概念科普重点说清楚三件事仓库模型该怎么分层、客户端配置有哪些隐蔽的坑、出问题之后按什么顺序排查。适合已经会用 Maven、但要把依赖管理从各人各自下载升级成团队统一收口的开发者也适合接手别人搭好的私服、需要做维护和迁移的同学。下面这些配置和参数你基本可以照着改改 IP 和端口直接用。1. 团队为什么要自己搭一个 Maven 私服1.1 从一次构建集体卡死说起先说个真实场景。项目组十几个人某天上午集体反馈构建超时mvn clean package卡在下载依赖上一动不动日志里全是 timeout。原因很简单所有人都在直连公网中央仓库而这个仓库对同一 IP 的并发请求有节流加上那阵子网络抖动一个几十兆的依赖下到一半断掉Maven 又生成了.lastUpdated文件把失败的记录缓存下来之后重试直接读缓存报错恶性循环。这就是没有私服的第一个代价公网仓库对你来说是不可控的外部依赖。它的可用性、限速策略、甚至某个历史版本会不会被下架你都无法干预。第二个代价是团队内的重复劳动——同样的spring-boot-starter-web及其几十个传递依赖十个人要下十遍每个人的本地仓库都是一份独立副本。第三个代价更隐蔽内部模块无法共享。A 组写的公共工具包想给 B 组用要么打成 jar 丢群里要么让大家先mvn install到本地——这两种方式在版本一多之后就是灾难谁也不知道手里那个 1.0.3 到底是哪次提交打出来的。私服解决的正是这三件事。它在内网架一个依赖中转站第一次有人请求某个构件时由它去上游拉取并缓存之后所有人直接从内网拿同时它还是一个内部制品发布中心你的公共模块、二方库、甚至 Docker 镜像的元数据都可以按坐标groupId / artifactId / version规范地存进来。对构建速度的提升是立竿见影的我们实测过一个中等规模的 Spring Cloud 项目首次构建从 6 分钟降到 90 秒左右增量构建的依赖解析几乎无感。1.2 Nexus 仓库模型里三类仓库的职责边界很多人搭完私服之后用得很别扭根本原因是没搞清 Nexus 的仓库分类。它不是一个仓库装所有东西而是按职责拆成几种类型每种有自己的行为规则。搞混了就会出现为什么我的快照发布不上去为什么代理仓库里搜不到我刚发的包这类问题。仓库类型典型命名是否可写主要职责常见误用proxy代理maven-central否缓存上游公网仓库的构件手动往里传包传不进去hosted宿主maven-releases是存放团队自己发布的正式版本把快照发进 release 仓库hosted宿主maven-snapshots是存放迭代中的快照版本用 release 策略导致覆盖失败group仓库组maven-public否把多个 proxy/hosted 聚合成一个入口成员顺序排错导致解析到旧版本理解这张表的关键在于读写分离。客户端读依赖时只认一个地址group写制品时按版本类型分别指向两个 hosted 仓库。中间那层 group 是个虚拟视图它自己不存数据只是把请求按顺序转发给成员仓库。所以你在 Nexus 界面上看到的maven-public下面那些构件实际都躺在maven-central或maven-releases里。这里有个特别容易犯的错把发布地址也写成 group 的地址。group 是只读的聚合视图往里 deploy 必然失败报的还是 400 这种含义模糊的错。我第一次搭的时候就干过这事查了半天日志才发现是 URL 写错了。1.3 什么规模的团队值得上私服不是所有项目都需要私服。如果就两三个人、依赖也不多、网络还稳定那用公网仓库加一个本地镜像设置就够了多维护一套服务反而增加负担。我的经验判断线大概是这样的团队超过 5 人或者有跨模块的二方库共享需求或者构建时间经常超过 3 分钟那就值得上。另外还有两类场景属于不上不行一是构建机CI需要稳定可复现的依赖来源公网抖动会直接影响发布节奏二是所在网络环境对公网访问有严格管控只能走内网出口。反过来如果团队只有一两个人、项目是单体、没有内部库复用那先别急着搭把settings.xml的镜像配好、本地仓库目录规划清楚性价比更高。私服真正的价值在多人协作和制品沉淀上人少的时候这两点体现不出来。2. 部署形态与资源规划怎么做决策2.1 三种部署形态的取舍Nexus 落地上来第一个决策就是部署形态大致三条路二进制包裸装、容器化部署、以及用现成的托管服务。托管这条对多数内网团队直接排除因为私服的核心价值就在于数据在自己手里托管反而把依赖关系又交出去了。所以实际是在前两种之间选。裸装的好处是排查方便日志、JVM 参数、数据目录都是明面上的路径出了问题直接tail -f看改配置也不涉及镜像重建。缺点是升级和迁移麻烦换机器要重新走一遍安装流程。容器化的好处是环境一致、迁移就是搬数据卷、版本升级换个 tag 起个新容器就行。缺点是首次调试稍微绕一点容器里的 JVM 参数要通过环境变量传路径映射搞错了会导致数据看起来写了但重启就没了。我后来的默认选择是容器化理由很实在私服这种装完基本不怎么动的基础设施最怕的是半年后要迁移或者扩容时没人记得当初那台机器上除了 Nexus 还改过什么。容器把这部分不确定性消掉了所有配置都在 run 命令和挂载目录里可复制。但如果团队里没人熟悉容器、运维体系也是纯物理机的路子那裸装更省心别为了时髦给自己加戏。2.2 硬件、JDK 与磁盘的量化估算资源这块别拍脑袋按数据量估。一个中等规模的 Java 团队私服上缓存的构件加上内部发布的包一年下来大概 20 到 60 GB主要吃在maven-central的缓存上——Spring 全家桶那套东西传递依赖特别深一个 starter 进来能带几十个 jar。团队规模CPU内存磁盘说明5 人以内2 核4 GB100 GB单机够用注意留缓存空间5 到 30 人4 核8 GB300 GB建议 SSD元数据查询快很多30 人以上8 核16 GB500 GB 起考虑拆分 blob store 到独立存储内存这里有个细节Nexus 跑在 JVM 上默认堆大小是 2703 MB这个数字挺怪是 1/4 物理内存算出来的一个值。如果你的机器是 8 GB 内存堆给 2 到 3 GB 就够如果机器是 32 GB别让它按默认吃 8 GB反而容易在 GC 上浪费时间。JVM 堆和直接内存要分开看Nexus 处理大文件上传时会用直接内存做缓冲所以-XX:MaxDirectMemorySize也别漏了。JDK 版本方面Nexus 3 的较新版本要求 JDK 8 或 11 以上具体看版本说明。我一般直接用 JDK 11 或 17 的 LTS 版本跑起来稳。别用 JDK 8 的老版本配合新 Nexus会撞上 TLS 或者模块系统的兼容问题。2.3 端口、目录与运行账号的约定三件事必须在装之前定死后面改起来都麻烦。端口Nexus 默认监听 8081。如果你的机器上已经有别的服务占了改etc/nexus-default.properties里的application-port。但更推荐的做法是让 Nexus 保持 8081前面挂一个 Nginx 做 80/443 的反向代理这样客户端配置里写域名不写端口将来迁移换端口时客户端不用动。数据目录裸装默认在安装目录下的../sonatype-work/nexus3容器里是/nexus-data。不管哪种强烈建议把它放在独立的数据盘或者独立挂载点上别和系统盘混在一起。原因很实际这个目录会长到几十上百 G混在系统盘上哪天把根分区撑爆了整台机器上其他服务跟着遭殃。运行账号绝对不要用 root 跑。裸装时创建一个专用系统账号比如就叫nexus把安装目录和数据目录的属主改过去。useradd -r -s /sbin/nologin nexus chown -R nexus:nexus /opt/nexus /data/nexus-data容器化时则反过来注意容器内的进程默认以某个 UID 运行如果挂载的宿主机目录权限对不上就会出现容器起来了但数据写不进去的情况。解决办法是把宿主机目录的属主改成容器内对应的 UID或者在 run 命令里用--user指定一个宿主机上存在的账号。3. Nexus 服务端安装与首次初始化3.1 二进制包部署全流程与 systemd 托管裸装流程不复杂但有几个细节容易漏。第一步解压到固定位置别放在某个用户的家目录下。tar -xzf nexus-3.x.x-linux.tar.gz -C /opt mv /opt/nexus-3.x.x /opt/nexus第二步明确告诉它数据目录在哪。编辑/opt/nexus/bin/nexus.vmoptions这里同时管两件事数据路径和 JVM 参数。-Xms2g -Xmx2g -XX:MaxDirectMemorySize4g -XX:UseG1GC -Dkaraf.data/data/nexus-data -Djava.io.tmpdir/data/nexus-data/tmpkaraf.data就是数据目录改这个比改默认的nexus-default.properties更稳妥因为重启后不会丢。tmpdir 也建议指到数据盘上否则上传大包时临时文件会写满系统盘。第三步交给 systemd 托管别用nohup挂着——机器重启就没了。[Unit] DescriptionNexus Repository Service Afternetwork.target [Service] Typeforking Usernexus Groupnexus LimitNOFILE65536 ExecStart/opt/nexus/bin/nexus start ExecStop/opt/nexus/bin/nexus stop Restarton-failure RestartSec10 [Install] WantedBymulti-user.targetLimitNOFILE这行必须加。Nexus 会同时开大量文件句柄默认的 1024 上限在多并发构建时会直接报 too many open files而且这个错误在日志里藏得很深表现为随机的连接失败。踩过一次之后我每次都会先检查ulimit -n。启动和验证systemctl daemon-reload systemctl enable --now nexus systemctl status nexus tail -f /data/nexus-data/log/nexus.log日志里出现Started Sonatype Nexus之类的字样、并且能访问http://IP:8081看到登录页就算成了。整个首次启动大概要 1 到 3 分钟因为要初始化内置的 OrientDB 数据库别急着重启。注意如果/data/nexus-data目录非空且不是有效的 Nexus 数据目录服务会启动失败并提示 not a directory 或者数据损坏。迁移时尤其小心别把新数据写进旧目录的子目录。3.2 容器化部署与数据目录持久化容器化版本最需要注意的就是数据必须挂出来不挂的话容器一删所有缓存的构件和发布的包全没了。mkdir -p /data/nexus-data chown -R 200:200 /data/nexus-data docker run -d --name nexus \ -p 8081:8081 \ -v /data/nexus-data:/nexus-data \ -e INSTALL4J_ADD_VM_PARAMS-Xms2g -Xmx2g -XX:MaxDirectMemorySize4g \ --ulimit nofile65536:65536 \ --restartalways \ nexus3:3.x那个chown -R 200:200是重点。容器里的 Nexus 以 UID 200 运行宿主机目录如果属主是 root写权限就没了表现是启动成功但一发布就报权限错误或者连内置数据库都初始化不了。不同镜像的 UID 可能不一样先用docker inspect或者起个临时容器id一下确认。JVM 参数通过INSTALL4J_ADD_VM_PARAMS传不要试图用JAVA_OPTS那个不生效。这是我在一个环境里折腾了半小时才发现的日志里 JVM 堆大小一直显示默认值。--ulimit nofile同样是为了文件句柄容器里默认继承宿主机的限制如果宿主机上限低容器里也低。3.3 首次登录、改密与匿名访问处理Nexus 3 早期版本初始账号是admin/admin123新版本改成了首次启动生成随机密码放在数据目录的admin.password文件里cat /data/nexus-data/admin.password登录后第一件事改密码第二件事决定匿名访问怎么办。匿名访问默认是开启的意思是任何能访问到 8081 端口的人不需要账号就能下载构件。内网环境下这个通常是可以接受的甚至还方便——构建机不需要配凭据就能拉依赖。但如果你的私服暴露在稍微宽松一点的网段建议关掉匿名或者至少把匿名角色限制成只能读 group 和 proxy不能读 hosted 里的内部包。匿名开关在 Settings → Security → Anonymous 里其实质是给一个叫anonymous的内置用户配置角色。我的做法是保留匿名但把匿名角色精简为只读maven-public一个仓库。这样新人入职配好地址就能直接构建不用等管理员开账号而内部敏感的二方库单独放到另一个不给匿名权限的仓库里。3.4 JVM 与存储的调优参数除了堆大小还有几个参数值得调都是被实际问题逼出来的。G1 GC大堆情况下 G1 的停顿比 Parallel 稳Nexus 这种平时很闲、偶尔被几十个并发构建打一波的负载很适合。加-XX:UseG1GC如果有余力再配-XX:MaxGCPauseMillis200。直接内存-XX:MaxDirectMemorySize建议设成堆大小的 1.5 到 2 倍。太小了上传大 jar 会 OOM太大了又浪费。2 GB 堆配 4 GB 直接内存是比较常见的组合。blob store 的选择Nexus 3 支持文件系统和 S3 兼容对象存储两种后端。前者简单直接后者适合已经有对象存储基础设施、想要容量弹性扩展的场景。中小团队用文件系统就行别为了看起来高级上对象存储多一层网络依赖反而增加故障点。定时任务Admin tasks里有几个内置任务要检查一下尤其是清理临时文件和压缩 blob store。后者在大量删除构件之后能回收磁盘如果你发现磁盘占用和实际内容对不上跑一次压缩就好了。4. 仓库模型落地代理、宿主、仓库组4.1 代理仓库的远程地址与缓存策略默认 Nexus 已经建好了maven-central代理仓库指向公网中央仓库。多数情况下直接用它就行但有几个地方要确认。远程地址如果内网有统一的出网代理这里要填上否则拉不到东西。填进去之后记得点 Test connection 验证别只看保存成功。缓存过期策略这决定了一个构件在私服上缓存多久之后会去上游检查更新。Not found cache TTL默认 1440 分钟意思是上游没有的包24 小时内不再问第二次——这个设置救过我们有个依赖被误删之后全组构建报错改成 60 分钟之后才有人发现是上游的问题而不是私服的问题。Metadata max age控制元数据的刷新频率对于快照依赖这个值直接决定了你能多快看到上游的新快照默认 1440 分钟偏长我一般调到 60 分钟。额外代理国内不少团队会再加一个国内镜像源做代理加速首次拉取。做法是新建一个 maven 类型的 proxy 仓库远程地址填镜像地址然后把它加进 group 里排在 central 前面。这样优先从镜像拉镜像没有的再回落到 central实际测试下来首次构建能快不少。4.2 宿主仓库的发布策略与快照策略maven-releases和maven-snapshots这两个宿主仓库配置上的核心差别是Version policy和Deployment policy。配置项maven-releasesmaven-snapshotsVersion policyRELEASESNAPSHOTDeployment policyALLOW_ONCE不允许覆盖ALLOW允许覆盖版本号要求正式版本如 1.2.0带 -SNAPSHOT 后缀Deployment policy设成ALLOW_ONCE是 release 仓库的默认也是必须守住的纪律正式版本一旦发布就不能改。这条看似麻烦实则是保护。线上跑的那个 1.2.0 依赖三个月后你回头构建必须能拿到一模一样的字节。如果允许覆盖你永远不知道当时构建用的是哪个版本的包——这是很多线上问题和测试环境不一致类事故的根源。快照仓库则相反允许反复覆盖同一个-SNAPSHOT版本。但有两点要注意一是快照的带时间戳文件会一直累积需要配清理策略下一节说二是团队最好约定快照只在开发环境用上线前必须打正式版本别让-SNAPSHOT出现在生产依赖树里。用 REST API 批量建仓库比点鼠标快也方便写进初始化脚本curl -u admin:新密码 -X POST \ http://nexus.internal:8081/service/rest/v1/repositories/maven/hosted \ -H Content-Type: application/json \ -d { name: maven-releases, online: true, storage: { blobStoreName: default, strictContentTypeValidation: true, writePolicy: ALLOW_ONCE }, maven: { versionPolicy: RELEASE, layoutPolicy: STRICT, contentDisposition: ATTACHMENT } }strictContentTypeValidation建议开着它能挡住一部分误传的非 Maven 构件。layoutPolicy用STRICT表示严格按 Maven 的目录规范校验坐标能防止有人用奇怪路径传包导致坐标解析混乱。4.3 仓库组的成员顺序与聚合逻辑group 仓库是客户端唯一需要记住的地址它的行为是按成员列表顺序依次查找。这个顺序直接决定了解析结果。推荐的顺序是内部快照仓库maven-snapshots放在前面保证内部迭代中的模块优先命中内部正式仓库maven-releases镜像代理如果加了maven-central把内部仓库放前面是为了避免内部包名和公网包名撞车的情况——虽然少见但一旦发生从公网拉到同名的错误构件会非常难排查。把快照放最前还有个好处开发期同一个模块频繁发快照能立刻被依赖方拿到不用等缓存的元数据刷新。另一个细节group 的成员列表里不要放另一个 group。虽然技术上可能允许但嵌套之后解析路径变长出问题的时候日志里只显示最终命中的仓库排查要多绕好几圈。保持扁平的一层结构。4.4 清理策略与定时任务不配清理策略的私服磁盘一定会被吃光。主要有两块垃圾快照的历史时间戳文件以及代理仓库里过期的缓存。Nexus 的清理策略Cleanup Policy按最后下载时间或最后修改时间来删。给快照仓库配一个保留最近 30 天有被下载过的构件其余删除。{ name: snapshot-cleanup, format: maven2, notes: 清理 30 天未被引用的快照, criteria: { lastDownloaded: 30, lastBlasted: 0 } }建好策略之后要绑到仓库上仓库配置的Cleanup项然后建一个定时任务去执行它。任务类型选Admin - Compact blob store配合Maven - Delete unused snapshots频率一周一次跑在业务低峰期。注意清理任务是不可逆的。第一次配的时候把保留天数设得很短比如 7 天结果有个组的老分支构建挂了需要半年前的快照依赖才发现已经被清掉了。生产环境的策略上线前先在一个测试仓库上跑一轮看看会删掉什么。5. 客户端接入settings.xml 与 pom.xml 双端配置5.1 settings.xml 完整模板与 mirrorOf 的正确写法客户端配置是整个私服方案里最容易出错的一环因为错误表现往往很迷惑——明明服务器上能看到构件本地就是拉不下来。先看settings.xml的核心部分settings mirrors mirror idnexus-public/id name内网仓库组/name urlhttp://nexus.internal:8081/repository/maven-public//url mirrorOfcentral/mirrorOf /mirror /mirrors servers server idnexus-releases/id usernamedeploy-release/username password{加密后的密码}/password /server server idnexus-snapshots/id usernamedeploy-snapshot/username password{加密后的密码}/password /server /servers /settings几个关键点逐条说。mirrorOf写central而不是*。这是我最想强调的一条。写*表示所有仓库请求都走这个镜像听起来很彻底但会带来一个副作用如果项目pom.xml里还声明了其他第三方仓库比如某个开源项目自己的 repo这些请求也会被强行转发到你的私服而私服上恰好没代理那个源结果就是 404。更麻烦的是有些公司内部的另一个老私服地址也会被覆盖导致老项目构建失败。用central就精确得多只劫持对中央仓库的请求。其他仓库该怎么走怎么走。如果你确实想把所有请求都收口可以用external:*或者*,!某个例外的写法但要知道自己在做什么。server里的id必须和pom.xml里distributionManagement的id完全一致。这是认证失败的常见原因之一——id对不上Maven 就找不到对应的凭据请求就是匿名的服务端返回 401。这个错误日志里只会写 Failed to transfer... status code 401不会告诉你你的 id 写错了。密码用mvn --encrypt-password加密。明文密码躺在settings.xml里一旦这个文件被提交到仓库或者打包进镜像凭据就泄露了。加密方式是mvn --encrypt-master-password mvn --encrypt-password第一条生成一个主密码写进~/.m2/settings-security.xml第二条用主密码加密你的部署密码得到{xxxxx}形式的串贴进password。注意settings-security.xml本身也要保管好它泄露了等于全部泄露。5.2 pom.xml 发布配置与多环境 profile读依赖的配置在settings.xml里写制品deploy的配置在pom.xml里两边配合才完整。distributionManagement repository idnexus-releases/id name内部正式仓库/name urlhttp://nexus.internal:8081/repository/maven-releases//url /repository snapshotRepository idnexus-snapshots/id name内部快照仓库/name urlhttp://nexus.internal:8081/repository/maven-snapshots//url /snapshotRepository /distributionManagementMaven 会根据当前版本号自动选repository还是snapshotRepository版本号以-SNAPSHOT结尾走快照否则走正式仓库。这个判断规则很严格1.0.0-SNAPSHOT可以1.0.0-snapshot小写就不认会被当成正式版本然后发布到 release 仓库时因为ALLOW_ONCE策略失败。多环境的情况我不建议给每个环境建一套仓库。私服的作用是制品仓库不是环境隔离工具。不同环境应该通过部署流水线区分而不是通过 Maven 仓库区分。但如果确实需要比如外包团队只能访问特定仓库用 profile 加属性来控制profiles profile iddeploy-internal/id distributionManagement repository idnexus-releases/id url${nexus.release.url}/url /repository /distributionManagement /profile /profiles然后在settings.xml的对应 profile 里定义nexus.release.url。这样pom.xml里不写死地址换个环境改配置就行。5.3 CI 中的凭据注入方式构建机上的做法和开发者本机不一样重点是不能把凭据写进代码仓库。我的常规做法是把凭据放在 CI 平台的密钥管理里构建时通过环境变量注入用一个临时生成的 settings 文件。cat /tmp/settings-ci.xml EOF settings mirrors mirror idnexus-public/id urlhttp://nexus.internal:8081/repository/maven-public//url mirrorOfcentral/mirrorOf /mirror /mirrors servers server idnexus-releases/id username${NEXUS_USER}/username password${NEXUS_PASS}/password /server server idnexus-snapshots/id username${NEXUS_USER}/username password${NEXUS_PASS}/password /server /servers /settings EOF mvn -s /tmp/settings-ci.xml clean deploy构建完之后删掉临时文件别留在构建机的磁盘上。另外构建机的本地仓库目录~/.m2/repository要定期清理否则各种残留的.lastUpdated文件和新旧版本混在一起会出现本地能构建、换台机器就不行的玄学问题。CI 环境我一般直接设成每次构建用独立的仓库目录或者用缓存机制但要能一键清空。5.4 Gradle 项目的接入团队里只要有 Android 或者用 Gradle 的项目就得单独配一套。Gradle 不走settings.xml配置在gradle.properties或者build.gradle里。build.gradlerepositories { maven { url http://nexus.internal:8081/repository/maven-public/ allowInsecureProtocol true } } publishing { repositories { maven { url version.endsWith(SNAPSHOT) ? http://nexus.internal:8081/repository/maven-snapshots/ : http://nexus.internal:8081/repository/maven-releases/ credentials { username project.findProperty(nexusUser) ?: System.getenv(NEXUS_USER) password project.findProperty(nexusPass) ?: System.getenv(NEXUS_PASS) } allowInsecureProtocol true } } }allowInsecureProtocol true这行是给 HTTP 地址用的。Gradle 7 之后默认拒绝非 HTTPS 仓库不加这行直接报错。当然更正确的做法是给私服配上 HTTPS后面安全加固那节会说。6. 账号权限体系与最小化授权6.1 角色设计三类账号私服的权限如果只有admin 一个账号到处用那迟早出问题——构建机的配置里存着 admin 密码等于所有能看构建配置的人都是管理员。我的做法是固定拆成三类角色。角色账号示例权限范围使用方只读ci-reader读 maven-public构建机拉依赖发布deploy-release读 maven-public 写 maven-releases发布流水线快照发布deploy-snapshot读 maven-public 写 maven-snapshots开发分支构建管理管理员个人账号全部运维不共享关键在于deploy-release和deploy-snapshot分开。正式版本的发布权限应该只给主干的发布流水线人的账号不给。这样谁在什么时候发了个正式版本是可追溯的也避免了误操作把未测完的代码发成正式版。Nexus 的权限模型是Privilege→Role→User三层。不用自己去建 privilege用内置的就够nx-repository-view-maven2-maven-releases-add、nx-repository-view-maven2-maven-releases-read这类。建角色的时候把这些 privilege 挑出来组合即可。6.2 权限粒度的实际选择有个细节值得说add和edit是两个不同权限。add是新增文件edit是修改已有文件。对于 release 仓库因为ALLOW_ONCE策略本身就不允许覆盖所以只给add和read就够了别给edit和delete。这样即使有人拿到了发布账号也删不掉历史版本。对于快照仓库因为要覆盖所以addeditread都要给。但delete还是别给清理交给定时任务。手工删仓库内容这个操作风险太高一旦删错别人的构建立刻挂。还有一个容易被忽略的权限nx-repository-view-*-*-browse。这个控制能不能在界面上浏览。查问题时很有用但也意味着能看到所有构件列表。内部包如果涉及敏感信息这个权限要收一收。6.3 安全加固清单内网不等于安全这几条建议都做上。第一把 HTTP 换成 HTTPS。用 Nginx 或者别的反向代理在前面做 TLS 终止Nexus 自己还监听 8081 只对代理开放。配置大概是server { listen 443 ssl; server_name nexus.internal; ssl_certificate /etc/ssl/nexus.crt; ssl_certificate_key /etc/ssl/nexus.key; client_max_body_size 200m; location / { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto https; } }client_max_body_size这行必须调大默认 1 MB。上传一个 Spring Boot 的 fat jar 动辄几十兆不调就会撞 413。这个错误在 Maven 端显示为 Failed to transfer file... status code 413看日志完全想不到是 Nginx 拦的。X-Forwarded-Proto是为了让 Nexus 生成正确的回调地址否则某些界面操作会跳回 http。第二改默认端口或者限制访问来源。8081 端口只允许反向代理所在机器访问用防火墙规则限制住。这样外面扫不到管理界面。第三定期轮换部署账号密码。尤其是有人离职、或者怀疑凭据外泄的时候。轮换之后记得同步更新所有构建机的配置这一步最容易漏。第四关掉不需要的功能。比如不用的仓库格式npm、docker、raw 之类如果没在用就别留空仓库在那减少攻击面。Nexus 支持的能力挺多但每开一个就多一份维护成本。7. 高频故障排查实录7.1 Maven 3.8 的 HTTP 仓库拦截这个问题坑了无数人必须单独说。Maven 3.8.1 开始默认禁止通过 HTTP 访问外部仓库会直接报Blocked mirror for repositories: [central (http://nexus.internal:8081/repository/maven-public/, default, releases)]看到这个说明你的私服地址是 HTTP 的。三条出路一是给私服上 HTTPS推荐一劳永逸二是在镜像配置里显式声明允许用maven.wagon.http.ssl.insecure之类的参数不解决这个正解是在settings.xml的 mirror 里加blockedfalse/blocked部分版本支持三是降级 Maven 版本不推荐会被后来的问题追着跑。我的建议就是老老实实上 HTTPS。内网也上理由不只是绕过这个限制更是因为部署密码会随 HTTP 明文传输。7.2 401/403 认证类问题这两类错误看起来像一回事其实原因完全不同。401 Unauthorized没认证或者认证失败。排查顺序pom.xml里distributionManagement的id和settings.xml里server的id是否一字不差。注意大小写。部署的用户名密码是否正确。可以先用 curl 验一下curl -u user:pass http://nexus.internal:8081/service/rest/v1/status返回 200 说明凭据没问题。密码是否用了加密串但没配settings-security.xml导致解密失败实际传了空密码。403 Forbidden认证过了但没有权限。这时候去看 Nexus 里这个账号的角色大概率是缺了目标仓库的add权限或者角色建对了但没绑到用户上。还有一种情况是账号被锁定登录界面上看不出来得在 Users 里看状态。7.3 404、checksum 与 .lastUpdated404 Not Found在拉依赖时出现通常是这个构件在私服和上游都没有。先确认坐标写对了没有再确认私服的代理仓库能不能连上外网。可以登录 Nexus 直接在界面上搜这个 artifactId搜到了说明私服有那就是客户端的 URL 指错了搜不到就去检查代理仓库的远程连接。Checksum validation failed是校验和不匹配说明下载下来的 jar 是坏的。原因可能是网络中断导致文件不完整也可能是私服上的缓存本身就坏了。解决方法是先把本地仓库里对应的目录整个删掉再重新构建rm -rf ~/.m2/repository/com/example/some-lib mvn clean package -U-U是强制更新快照和元数据很多明明私服上有新版本本地就是拿不到的问题加这个参数就好了。.lastUpdated 文件是 Maven 的失败缓存。一次下载失败之后会生成xxx.jar.lastUpdated记录这条失败之后一段时间内不再重试。表现是网络早就恢复了本地还是报错。根治办法是把所有.lastUpdated删掉find ~/.m2/repository -name *.lastUpdated -delete我一般把这个做进一个清理脚本里遇到莫名其妙的依赖问题先跑一遍。7.4 发布失败与 400 报错Return code is: 400, ReasonPhrase: Bad Request这个报错信息基本等于没说得看 Nexus 服务端的日志。常见的三个原因一是往 release 仓库发了带-SNAPSHOT的版本。版本号和仓库的 version policy 不匹配。检查pom.xml的version。二是覆盖了已存在的正式版本。ALLOW_ONCE策略下重复部署会失败报的是同一类 400。这种情况正确的做法是升版本号重新发而不是去改仓库策略放开覆盖。三是发布到了 group 仓库的地址。前面提过group 是只读视图。检查distributionManagement里的 URL 是不是以maven-releases/maven-snapshots结尾而不是maven-public。7.5 磁盘暴涨与 Nginx 413磁盘暴涨一般有三个来源快照的历史时间戳堆积、代理仓库缓存了大量其实只用过一次的构件、以及日志文件。前两个靠清理策略解决日志则要注意 Nexus 自己的日志轮转配置。我遇到过一次数据盘在一周内从 30% 涨到 95%查到原因是某个组把 CI 的构建产物几十兆的 fat jar当成快照每次构建都发一遍一天几百次。解决办法是让 CI 只在合并到主干时才发布快照而不是每次提交都发。413 Request Entity Too Large前面提过是反向代理的client_max_body_size太小。除了 Nginx如果前面还有别的网关或者负载均衡也要同步调大逐层排查。7.6 一个排查顺序的小结遇到拉不到依赖这类问题我固定的排查顺序是先在 Nexus 界面上搜坐标确认服务端有没有 → 用 curl 直接请求构件的 URL 确认网络通不通 → 检查客户端settings.xml的镜像地址 → 清本地.lastUpdated加-U重试 → 最后才怀疑 Maven 版本或者 JDK 兼容性。这个顺序能覆盖 90% 的情况而且每一步都有明确的判定结果不用瞎猜。8. 备份、迁移与日常巡检8.1 必须备份的三类内容备份这件事说清楚备份什么比怎么备份更重要。Nexus 需要备份的就三样内容位置重要程度说明数据目录sonatype-work/nexus3或/nexus-data最高包含 blob、数据库、配置配置文件etc/nexus.properties、bin/nexus.vmoptions高端口、JVM、数据路径反向代理配置Nginx 的 server 段中重建时容易忘数据目录里最核心的是blobs和db两个子目录。blobs是实际的文件db是索引元数据。这两者必须同时备份且要保持一致——只备份 blobs 恢复出来是一堆无索引的文件界面上什么都看不到。备份方式上不要直接对运行中的数据目录做文件级拷贝。Nexus 运行时会写数据库拷出来的可能是半截状态。正确做法有两种一是停服务再拷简单但停机二是用 Nexus 内置的Admin - Export databases for backup任务导出一个一致的状态再拷那个导出目录。数据量大的话后者更实际可以配成每天凌晨跑。我的实际方案是每天凌晨跑一次内置导出任务导出目录同步到备份存储保留最近 14 天。数据量在几十 G 的规模下增量同步压力不大。8.2 迁移到新机器的操作顺序迁移这事我做过三次每次都因为顺序搞错多花时间。正确的顺序是旧机器停服务确保没有新的写入。完整拷贝数据目录到新机器注意保持目录结构和属主裸装用rsync -a容器化注意 UID 映射。新机器上装同版本或稍新版本的 Nexus先别启动。把配置文件里的路径改好尤其是karaf.data和java.io.tmpdir。启动服务看日志确认数据库正常加载。验证登录界面看仓库列表是否完整、搜一个已知构件、用一个测试项目完整跑一遍deploy和dependency:resolve。切换客户端配置把 DNS 或者 hosts 指过去再观察一两天旧机器先别删。版本上的经验跨大版本迁移要谨慎Nexus 3 的不同小版本之间升级通常是平滑的但从很老的版本直接跳到很新的版本数据库结构变化可能需要中间版本过渡。做法是先在一个测试环境演练一遍别直接在生产上试。8.3 日常巡检清单私服这类服务的特点是平时没人管出事就是全组停摆所以巡检要形成固定动作。我每周会看这几项磁盘使用率超过 70% 就该规划清理或者扩容了。blob store 的实际大小和可用空间在界面的 Blob Stores 页面能直接看到。异常日志grep -i error\|exception nexus.log扫一眼很多连接超时的早期信号都在里面。定时任务的执行记录清理任务如果连续失败磁盘会悄悄涨上去。证书有效期如果配了 HTTPS证书到期前一个月要续这个最容易忘而且一到期所有构建全线报错。还有一个小技巧给私服的/service/rest/v1/status端点配一个健康检查接到监控系统里。这个端点返回 200 就代表服务活着比端口探测更准确因为它会检查内部数据库状态。我个人在实际运维中的体会是私服这东西的价值不在搭起来那一刻而在三年后还有人愿意往里发东西。有些团队的私服搭完就荒废了原因是配置太严格——发布要审批、版本号规则死板、出问题没人管。等大家发现还不如自己丢 jar 包快就绕过去了。所以规则可以严但一定要配上响应速度出了问题当天能有人看这才是让私服真正活下来的关键。
企业数字化 ERP 产品动态
相关推荐
LLM驱动动画创作工具的核心瓶颈:中间件与生产级编排深度拆解 过去一年我陆陆续续把市面上能接触到的文本LLM驱动动画创作工具试了一个遍,最直观的体感是:能让人惊艳三分钟的项目很多,真正能把手头动画项目往前推的很少。卡住进度的往往不是生成画面那一下,而是它周围那圈名叫“中间件”的胶水… · 2026/11/9 16:42:45
Windows资源管理器显示STL缩略图:三条路线与故障排查 文件夹里躺着两百多个 STL,文件名从part_01一路排到part_200,图标清一色空白纸片,想找某个支架得挨个双击、等切片软件启动、看模型、关掉、再点下一个——这大概是每个碰过 3D 打印或者结构设计的人都经历过的崩溃时刻。STL 文件预览这件事之… · 2026/11/9 15:00:37
BUUCTF wdb2018_guess题解:Canary绕过与栈溢出利用 1. 题目背景与初步分析BUUCTF是一个知名的CTF(Capture The Flag)竞赛平台,而"wdb2018_guess"是其中一道经典的pwn题目。这道题目主要考察选手对Canary保护机制的绕过能力,属于二进制安全领域的栈溢出类题目。Canary&… · 2026/11/5 14:11:11
电工证场合必须知道的报名避坑指南,上岗必备怎么选? 电工证场合必须知道的报名避坑指南,上岗必备怎么选? 你是不是也遇到过这种情况?想找电工证场合报名,却不知道哪个机构靠谱,怕被中介骗钱?别急,这篇文章就是为你量身打造的,从河南转岗电工的真实经历出发,手把手教你避开那些坑,找到真正能帮你拿到【… · 2026/11/10 0:20:22
在北京考安监局电工证证怎么转异地 北京考安监局电工证怎么过?怕考不过白交培训费的看这篇 怕考不过白交培训费?在北京考安监局电工证的含金量你真了解吗?很多建筑工人以为只要交钱就能拿到证,结果一考试就挂,钱花了不说,还耽误时间。其实,真正能顺利通过考试,拿到含金量高的电工证,离… · 2026/11/10 0:20:22
蚌埠电工证年审怎么过?工地太忙根本没时间复习考试 蚌埠电工证年审怎么过?工地太忙根本没时间复习考试 工地太忙根本没时间复习考试,这是很多电工兄弟的痛点。 上岗必备 的电工证年审如果不过,直接面临停工、罚款甚至被清退的后果。今天就从实际操作出发,帮你理清蚌埠电工证年审的流程、内容、避坑要点,… · 2026/11/10 0:20:22
唐海哪里能学电工证真实经历:学历不高也能报上名 唐海哪里能学电工证真实经历:学历不高也能报上名 你是不是也担心自己学历不高,报不了电工证?别急,今天我来跟你说说唐海哪里能学电工证的真实经历,告诉你怎么一步步搞定报名,而且学历真的不是问题。… · 2026/11/10 0:20:22
低压电工证报名后多久要考完试保姆级教程 低压电工证报名后多久要考完试保姆级教程 怕考不过白交培训费?别慌!这篇保姆级教程帮你搞懂低压电工证报名后多久要考完试,还有答题技巧、考试时间安排、备考建议,全是干货,不绕弯子!… · 2026/11/10 0:19:22
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/9 2:05:04
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/9 3:11:26
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/9 5:24:17
丹寨电工证怎么考?内部消息透露报考条件和流程 丹寨电工证怎么考?内部消息透露报考条件和流程 急需特种作业证才能上岗找工作,这是很多电工朋友最真实的焦虑。尤其是像丹寨电工证这样的证书,直接影响你的职业发展和薪资待遇。但你知道吗?很多人都在报考过程中踩坑,有的甚至花了几千块最后白忙一场。今… · 2026/11/10 0:00:13
成都建筑电工证办理郑州报考避坑指南 成都建筑电工证办理郑州报考避坑指南 证书过期了不知道怎么复审延期?别慌!这篇文章手把手带你解决成都建筑电工证办理的难题,顺便避掉郑州报考的那些坑。不管你是刚拿到证的新手,还是准备复审的老电工,这波干货都别错过。… · 2026/11/10 0:00:13
梅州低压电工证怎么考?怕被中介坑?手把手教你全国通用流程 梅州低压电工证怎么考?怕被中介坑?手把手教你全国通用流程 你是不是正为梅州低压电工证发愁?不知道去哪报名,怕被中介骗钱,又担心证书是不是全国通用?其实,这些顾虑都是正常人会有的。今天我就从一个在开封房建工程一线干了几年的电工角度,手把手教你… · 2026/11/10 0:00:13
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/9 11:23:35
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/9 11:23:58
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/9 11:25:08