Nexus 3.50 Windows版部署配置与避坑指南
简介Nexus Repository Manager 3.50.0-01 的 Windows 64 位安装包面向需要搭建 Maven 私服、统一管理构建产物的开发与运维团队也适合学习制品仓库配置的初、中级工程师。压缩包共 690 个文件约 240.15MB以 jar 依赖库、dll 动态库、xml 配置和 exe 启动程序为主另含 properties、cfg、yml 等运行配置及图标说明文件呈现完整服务端发行结构。已有 494 人学习或下载。借助它可部署本地仓库服务实践依赖代理、制品上传与版本归档流程并通过权限控制和存储检索机制理解私服安全与性能优化也可对照配置模块梳理 Karaf、JMX、证书等内容为排错和团队协作提供参考。1. 从 zip 到内网制品仓库nexus-3.50.0-01-win64.zip 解压前该想清楚的三件事很多人拿到nexus-3.50.0-01-win64.zip第一反应是解压、双击nexus.exe、浏览器打开 8081 看到界面就觉得装完了。真正让人卡住的是之后的事服务装成 Windows Service 后改了内存不生效仓库建好了客户端仍然 401磁盘半年涨到 200G 才发现没人配清理策略。这个 zip 本身只是一个自带 JVM 启动脚本的 Java Web 应用分发包它解决的是“在内网留一份可信的依赖与镜像来源”——Maven 每次拉包不再出网、npm 装依赖不再看公网脸色、Docker 镜像有一个自己说了算的地址。适合两类人要给团队做统一依赖入口的后端和运维以及只想在本机或小集群里缓存依赖、不想每次都重新下载的开发者。机器内存低于 4G、或者打算把它当公网 CDN 用的先别急着上手。2. 解压之后的目录与端口nexus-3.50.0-01-win64.zip 里哪几个文件要动2.1 解压后先认清四个目录bin 是启动器etc 才是配置把 zip 解开看到的目录结构不算复杂但每个目录的角色差别很大改错地方是新手最常见的翻车起点。我一般先tree /F看一层再决定哪些文件要进版本管理、哪些绝对不能碰。目录关键文件作用能不能手动改bin/nexus.exe、nexus.vmoptions启动器与 JVM 参数nexus.vmoptions必须改其余别动etc/nexus-default.properties、karaf/端口、监听地址、数据目录、日志配置可以改改完重启生效deploy/空目录放自定义插件 bundle不用碰system/、lib/大量 jar应用本体绝对不要删改public/静态资源前端页面不要改启动链路是这样的nexus.exe先读同目录的nexus.vmoptions拿到一堆-D参数拉起 JVMJVM 里跑的是 Karaf 容器容器去etc/读配置etc/nexus-default.properties里的application-port、application-host、nexus-context-path决定浏览器怎么访问karaf.data决定数据落在哪个盘。第一次启动会初始化数据目录这个过程慢一到三分钟都算正常别盯着控制台以为卡死了——真正的进度在数据目录下的log/nexus.log里。etc/nexus-default.properties里我通常会确认这三行# 监听所有网卡否则只有本机能访问 application-host0.0.0.0 # 端口冲突时改这里别去改 vmoptions application-port8081 # 反向代理到子路径时才改改成 /nexus 后访问地址要带前缀 nexus-context-path/这里的逻辑很直白application-host默认是0.0.0.0但有些同学从别处抄来的配置写成了127.0.0.1结果本机浏览器能开、同事的机器一律超时然后开始怀疑防火墙其实是监听地址的问题。nexus-context-path改了就回不去——已经写进数据库的仓库地址不会跟着变客户端里散落的 URL 全部要改所以这一项要么一开始就定好要么永远别动。2.2 前台跑通再装服务nexus.exe /run 与 /install 的区别我强烈建议第一次启动用前台模式出问题能直接看到堆栈。先确认本机 JDK 版本对得上这一步别凭记忆# 先看本机 java 版本和 JAVA_HOME java -version $env:JAVA_HOME # 解压到 D 盘路径里不要带空格和中文 Expand-Archive -Path .\nexus-3.50.0-01-win64.zip -DestinationPath D:\nexus Get-ChildItem D:\nexus路径里带空格是另一类经典事故某些脚本拼接路径时没加引号服务装上了但启动参数被截断日志里只留下一行莫名其妙的 “could not find or load main class”。解压完进bin目录前台跑cd D:\nexus\nexus-3.50.0-01\bin # 前台运行CtrlC 停止日志直接打屏 .\nexus.exe /run看到Started Sonatype...一类的启动完成日志浏览器开http://127.0.0.1:8081能出登录页就说明基础链路通了。默认管理员账号是admin初始密码不在界面上而在数据目录的admin.password文件里登录后系统会强制你改改完那个文件就自动删掉了——这一步别跳过也别把新密码只记在脑子里。前台验证通过再装服务# 装成 Windows 服务服务名自己定别用默认的 nexus 撞名 .\nexus.exe /install nexus-repo .\nexus.exe /start nexus-repo .\nexus.exe /status # 卸载流程先停再删 .\nexus.exe /stop nexus-repo .\nexus.exe /uninstall nexus-repo/install做的事是把nexus.exe注册成一个由服务管理器托管的进程并把当前目录记成工作目录。这里有个容易忽略的点服务装好之后nexus.exe /run和服务的日志是同一份但进程环境变量不一样。前台跑的时候用的是你当前 PowerShell 会话里的JAVA_HOME服务跑的时候用的是系统环境变量两者不一致会导致“前台能起、服务起不来”。装服务之前把JAVA_HOME配到系统变量里别只配在用户变量。2.3 内存怎么给nexus.vmoptions 里三个数字的算法bin/nexus.vmoptions是唯一需要动的手术刀位置。默认的堆是 2703m——这个数字不是随便写的是给“能跑起来但不保证快”的场景留的余量。对内网十几个人用的仓库我会直接给到 4G。-Xms4096m -Xmx4096m -XX:MaxDirectMemorySize4096m -XX:UnlockDiagnosticVMOptions -XX:LogVMOutput -Dkaraf.home. -Dkaraf.base. -Dkaraf.etcetc/karaf # 数据目录挪到大盘必须在首次启动前改好 -Dkaraf.dataD:/nexus-data -Dkaraf.logD:/nexus-data/log -Djava.io.tmpdirD:/nexus-data/tmp # Windows 上不加这行中文包名和中文属性文件会乱码 -Dfile.encodingUTF-8 # 内网有自签 CA 时用它路径相对 bin 目录 -Djavax.net.ssl.trustStoreetc/ssl/truststore.jks -Djavax.net.ssl.trustStorePasswordchangeit三个数字的关系要算清楚-Xmx是 Java 堆MaxDirectMemorySize是堆外直接内存Nexus 处理上传下载和大文件校验时堆外吃得不少再加上元空间、线程栈、JVM 自身的开销实际占用大概是Xmx DirectMemory 1G。所以 16G 的机器给 4G 堆是安全的8G 的机器给 2G4G 的机器只能给 1G 并且别指望它扛 CI 全量拉取。把Xmx设成物理内存的 80% 是纯粹的自杀行为系统开始换页服务没死但每个请求都慢得像在泥里走这种“活着但很难用”的状态比直接崩掉更难排查。还有一条血泪经验-Dkaraf.data只在首次启动前改有效。如果已经用默认路径跑过一次、生成了数据再去改这一行服务会用新目录重新初始化你会以为“数据丢了”。真要迁移正确做法是停服务、把整个数据目录拷到新位置、改配置、再启动顺序不能错。3. 仓库类型选型proxy、hosted、group 三者怎么分工3.1 三类仓库的职责边界与 group 成员顺序Nexus 的仓库模型只有三种基本类型理解了这三者的边界后面所有格式Maven、npm、Docker、PyPI、raw都是同一套逻辑换个壳。类型数据从哪来典型用途客户端能不能写proxy代理从上游远程地址按需拉取并缓存缓存中央仓库、缓存 npm 公共源不能hosted宿主你自己上传自研 jar、内部 npm 包、私有镜像能group分组聚合多个 proxy 和 hosted给客户端一个统一入口不能写只能读选型上没什么玄学对外部依赖建 proxy对自研产物建 hosted给客户端只暴露一个 group。这样开发同学的settings.xml和.npmrc里永远只写一个地址后面你加减仓库他们完全无感。如果让每个人自己记住“jar 从这里下、自己的包往那里传”三个月后一定会有人把 snapshot 传到 release 仓库里去。group 有个特别容易被忽略的参数成员顺序。group 解析时是自上而下匹配的排在前面的仓库优先。所以正确的顺序是 hosted 在前、proxy 在后——自研包和公共包同名时比如内部改过一个开源库你希望拿到的是自己那份。反过来排会出现“明明传了新版本客户端拉到的还是公共仓库的老版本”这种看起来毫无道理的故障。3.2 用 REST API 批量建仓库一份 JSON 对应一个仓库界面点几下就能建仓库但要做三套环境开发、测试、生产就是重复劳动还容易漏参数。我一般用 REST API 脚本化把每个仓库写成一个 JSON 文件建的时候循环提交。先建 Maven 的代理仓库maven-central-proxy.json{ name: maven-central-proxy, online: true, storage: { blobStoreName: default, strictContentValidation: true }, proxy: { remoteUrl: https://repo1.maven.org/maven2/, contentMaxAge: 1440, metadataMaxAge: 1440 }, negativeCache: { enabled: true, timeToLive: 1440 }, httpClient: { blocked: false, autoBlock: true, connection: { retries: 3, timeout: 60, enableCircularRedirects: false, enableCookies: false } }, maven: { versionPolicy: RELEASE, layoutPolicy: STRICT } }参数含义怎么定remoteUrl上游地址Maven 填中央仓库根路径注意结尾斜杠contentMaxAge已缓存构件的有效期分钟1440一天 jar 基本不变可以更长metadataMaxAge元数据有效期分钟1440太短会频繁回源拖慢构建negativeCache.timeToLive找不到的构件缓存多久分钟1440调太小会把“不存在的包”反复回源autoBlock上游连续失败后自动熔断保持 true防止上游挂了拖死自己strictContentValidation内容校验保持 true能挡掉一批投毒包提交# 用户名密码走 basic auth建议用专门的服务账号而不是 admin curl -u admin:YourPassword -X POST \ http://127.0.0.1:8081/service/rest/v1/repositories/maven/proxy \ -H Content-Type: application/json \ -d maven-central-proxy.jsonWindows 上不想装 curl用 PowerShell 等价写法注意-InFile传的是文件路径$cred Get-Credential Invoke-RestMethod -Uri http://127.0.0.1:8081/service/rest/v1/repositories/maven/proxy -Method Post -Credential $cred -ContentType application/json -InFile .\maven-central-proxy.jsonhosted 仓库的 body 短很多关键在writePolicy{ name: maven-releases, online: true, storage: { blobStoreName: default, strictContentValidation: true, writePolicy: ALLOW_ONCE }, maven: { versionPolicy: RELEASE, layoutPolicy: STRICT } }writePolicy取值里ALLOW_ONCE表示同一个路径只允许写一次这是 release 仓库该有的约束——版本一旦发出去就不该被悄悄替换否则线上问题追溯时你根本不知道跑的是哪份包。snapshot 仓库则要用ALLOW允许覆盖。很多人图省事全设成ALLOW半年后遇到“同样的 1.2.0 在两个环境行为不一致”那就是纯粹的后悔药没处买。group 仓库最后建body 里指定成员顺序{ name: maven-public, online: true, storage: { blobStoreName: default, strictContentValidation: true }, group: { memberNames: [maven-releases, maven-snapshots, maven-central-proxy] } }memberNames的数组顺序就是解析优先级自研在前、代理在后和上面说的是同一件事。3.3 两个必须提前打开的 realmnpm Bearer Token 与 Docker Bearer Token这一条是纯踩坑换来的仓库建好了、账号权限也给了npm publish和docker push死活 401日志里写着 token 校验失败人就开始怀疑人生。原因是 Nexus 默认只启用基础的认证 realmnpm 和 Docker 这两种协议用的令牌认证机制默认是关闭的。去Security → Realms把npm Bearer Token Realm和Docker Bearer Token Realm从 Available 挪到 Active保存。注意顺序基础的NexusAuthenticatingRealm必须留在列表最上面挪到下面会导致所有账号登录失败。改完不用重启但已经登录的客户端要重新登录一次本地的 token 缓存不会自动失效。npm logout再npm logindocker logout再docker login这两步别省否则你会以为配置没生效。4. 客户端接入实战settings.xml、.npmrc 和 docker 私有端口的配置4.1 MavenmirrorOf 写错插件下载全走公网~/.m2/settings.xml里两处配置最容易混mirrors管下载走哪servers管认证凭据。它们靠id关联但关联的不是同一个东西。settings mirrors mirror !-- id 随便取但下面 server 里要有一份同名的 -- idnexus/id !-- external:* 表示除 localhost 和 file:// 之外全部走镜像 -- mirrorOfexternal:*/mirrorOf urlhttp://nexus.internal:8081/repository/maven-public//url /mirror /mirrors servers server idnexus/id usernamedeployer/username password明文密码生产环境用 mvn -ep 加密后填这里/password /server server idnexus-releases/id usernamedeployer/username password同上/password /server server idnexus-snapshots/id usernamedeployer/username password同上/password /server /servers /settingsmirrorOf的值决定了代理范围这几种写法的差别很实在写法效果适用场景*所有仓库请求都走 Nexus完全隔离的内网最省事external:*除 localhost 和 file 协议外都走 Nexus推荐本地私服调试不受影响central只代理中央仓库项目里还有别的私服时用*,!internal-repo除指定仓库外全代理需要保留个别直连时用写成central是最常见的误用pom 里声明的第三方仓库比如某个中间件厂商的仓库不会被镜像依然直连公网在隔离环境里直接构建失败报错信息是“Could not resolve dependencies”看不出是镜像没覆盖到。上传则要在 pom 里写distributionManagement这里的id必须和servers里的id一一对应跟 mirror 的id没关系distributionManagement repository idnexus-releases/id urlhttp://nexus.internal:8081/repository/maven-releases//url /repository snapshotRepository idnexus-snapshots/id urlhttp://nexus.internal:8081/repository/maven-snapshots//url /snapshotRepository /distributionManagementmvn deploy时如果报 401先看这里的id拼写九成是两边对不上。4.2 npm一条 .npmrc 把读和写分开npm 的坑在于读和写是两个地址。读走 group能同时看到公共源和内部包写只能走 hostedgroup 不可写。这两件事用一条.npmrc说清楚# 默认读走 group registryhttp://nexus.internal:8081/repository/npm-group/ # 内部作用域的包也走 group internal:registryhttp://nexus.internal:8081/repository/npm-group/ # 两个地址各自的 token路径要和上面严格对应 //nexus.internal:8081/repository/npm-group/:_authTokenNpmToken.xxxxxxxx //nexus.internal:8081/repository/npm-hosted/:_authTokenNpmToken.xxxxxxxx # 内网 http 协议必须关掉严格 ssl 校验 strict-sslfalse always-authtrue几个细节值得单独说。第一_authToken前面那一串不是域名是“协议 主机 端口 仓库路径”写错一个字符就等于没配而且不会报错只会 401。第二strict-sslfalse只在纯内网 http 场景下用如果 Nexus 前面挂了 https 反向代理这一行要删掉否则等于自己给自己降级。第三always-authtrue保证每次请求都带凭据避免缓存过期后偶发的 401。发布时显式指定仓库别指望 registry 自动切换# 登录一次凭据会被写进 .npmrc npm login --registry http://nexus.internal:8081/repository/npm-hosted/ # 发布必须显式指向 hosted npm publish --registry http://nexus.internal:8081/repository/npm-hosted/如果这台机器同时连着多个 Registry加--registry参数是最稳的做法比改全局配置靠谱得多。4.3 Dockerconnector 端口、insecure-registries 与 docker loginDocker 仓库和别的格式有个根本差别它必须占一个独立端口。不能复用 8081因为 Docker Registry 协议对 URL 路径结构有硬性要求。建 docker hosted 仓库时在仓库设置里勾上 HTTP 的 connector填一个空闲端口比如 8082并在Allow anonymous docker pull上按需选择。建完之后客户端这台机器要告诉 Docker 守护进程“这个地址是走明文的”{ insecure-registries: [nexus.internal:8082], registry-mirrors: [] }改完daemon.json要重启 Docker 服务才生效Windows 上在 Docker Desktop 的设置里改更省事。然后docker login nexus.internal:8082 -u deployer docker tag app:1.0 nexus.internal:8082/app:1.0 docker push nexus.internal:8082/app:1.0 docker pull nexus.internal:8082/app:1.0docker push时报http: server gave HTTP response to HTTPS client就是insecure-registries没生效或者拼写不一致端口漏了也算不一致。报unauthorized: authentication required先回去检查 3.3 节说的 Docker Bearer Token Realm 开没开。如果想让客户端只记一个地址就建一个 docker group 仓库把 hosted 和 proxy 加进去group 自己绑一个端口。代价是 group 里的成员仓库如果各自也绑了端口客户端可能要从不同端口登录凭据不通用——所以规范的做法是group 只绑一个对外端口成员仓库不绑端口全部通过 group 访问。5. 排查与避坑Windows 上跑 Nexus 最容易翻车的 5 个现场5.1 服务显示 running8081 死活连不上现象nexus.exe /start返回成功/status显示服务在跑但浏览器和 curl 一律超时任务管理器里能看到 java 进程CPU 占用接近零。原因服务托管层只负责拉起进程进程自己因为 JVM 参数不合法最常见是-Xmx超过物理内存或者-Dkaraf.data指向一个没权限的路径启动到一半就退出了托管层还没来得及更新状态。真正的原因全在数据目录的log/nexus.log尾部。解决别在服务模式里瞎猜直接nexus.exe /stop然后用nexus.exe /run前台跑一次异常堆栈会直接打在控制台。同时确认三件事JAVA_HOME配在系统变量而不是用户变量数据目录所在盘有写权限服务默认以 LocalSystem 跑网络盘和某些同步盘目录会失败nexus.vmoptions里的路径用的是正斜杠D:/nexus-data用反斜杠在某些版本里会被当成转义符。5.2 上传 jar 报 400提示仓库不允许更新现象mvn deploy失败返回 400错误信息里带Repository does not allow updating assets之类的字样。原因往maven-releases这个 hosted 仓库重复部署了同一个版本号。release 仓库的writePolicy设的是ALLOW_ONCE这是设计上的约束而不是 bug——发出去的版本就该不可变。解决正规做法是升版本号重发。如果确实是发错了要补救在界面上对该构件执行删除再重发但这属于破坏性操作删之前确认没有别的项目已经依赖它。想避免这类来回团队里约定一条日常联调用 snapshot只有准备上线的版本才进 release。5.3 npm 装包卡住几十秒然后超时现象npm install每条依赖都要等很久最后报ETIMEDOUT或ECONNRESET但浏览器访问上游地址是通的。原因Nexus 作为代理去拉上游时用的是 JVM 自己的证书链和网络栈Windows 上常见两种情况企业内网有自签 CAJDK 的cacerts里没有或者上游对某些请求做了限流、需要走代理。解决拿到内网根证书导入 Nexus 用的那个 JDK 的信任库或者用 2.3 节里的-Djavax.net.ssl.trustStore指向一个独立的 truststorekeytool -importcert -noprompt -alias corp-root-ca \ -file corp-root-ca.crt \ -keystore etc/ssl/truststore.jks \ -storepass changeit同时在仓库设置里把HTTP request settings的连接超时和读取超时调大默认值对慢链路偏紧retries给到 3。排查阶段最有效的一招是看log/nexus.log里带Remote storage的行那里会写清楚到底是 DNS 解析失败、TLS 握手失败还是 407。5.4 磁盘半年涨到 200G删了包空间也不回来现象D 盘报警仓库里明明删了很多构件可用空间几乎没变化。原因Nexus 的存储是 blob store 加数据库索引两层。界面上删除只是把索引标记成不可见物理 blob 文件还在没有配 cleanup policy 的代理仓库会把上游拉过的每个版本都永久留着CI 每天全量拉一次缓存只会单向往上涨。解决分三步走。先建 cleanup policyRepository → Cleanup Policies按格式选 maven2 或 npm条件一般设“最后下载超过 90 天”加“保留最近 N 个版本”再把 policy 关联到代理仓库和 snapshot 仓库最后手动跑两个任务Admin - Cleanup repositories using their associated policies清索引Admin - Compact blob store真正回收磁盘。第二步不做第一步等于白干。5.5 备份只拷了 blob store恢复后元数据全丢现象机器重装把 blob store 目录拷回来启动后仓库列表是空的或者仓库在但里面的构件全部显示不可用。原因只备份了文件没备份数据库。仓库名、权限、构件与 blob 的映射关系全在数据目录的数据库文件里blob 只是一堆没有名字的二进制。解决备份必须包含数据目录下的数据库文件和 blob store 两部分并且要在停机状态下做或者用内置的Admin - Export databases for backup任务导出一致性快照。热拷贝正在写入的数据库恢复出来的索引大概率是坏的——这一点上省的那几分钟停机后面要用几个小时来还。6. 备份、清理与一次恢复演练把仓库养到第二年仓库这类基础设施的通病是上线那天所有人都在用之后就没人再看它直到某天磁盘满或者数据坏了。我一般在上线第一周就把备份和清理定下来不给自己留后悔的余地。备份对象其实就两块列清楚比记在脑子里可靠备份对象位置方式恢复要点数据库与配置数据目录下的数据库文件、etc/停服务冷备或用 export 任务导出必须与 blob 同批次构件本体blob store 目录文件级增量同步权限和属主保持一致清理策略的参数不要拍脑袋。我的经验值是代理仓库的“最后下载超过 90 天”就可以清因为超过三个月没人拉的版本大概率下次也不会有人拉snapshot 仓库按“保留最近 10 个版本”加“超过 30 天未更新”双条件防止某个长期不动的快照被误留release 仓库原则上不清它本来就是只增不改的。跑完 cleanup 一定要接着跑 compact否则空间只是从“可见”变成“不可见”。验证比配置更重要。我习惯在每次变更后跑一条固定命令比对状态码和字节数比打开网页点两下靠谱得多# 状态码 200 且 size 大于 0 才算这条链路是通的 curl -s -o /dev/null -w %{http_code} %{size_download}\n \ http://nexus.internal:8081/repository/maven-public/org/springframework/spring-core/5.3.30/spring-core-5.3.30.jar真要做恢复演练别在生产上试。找一台空机器装一个同版本的nexus-3.50.0-01-win64.zip把备份的数据目录和 blob store 按原路径放回去启动后随机抽三个仓库各拉一个构件、推一个构件。这套演练我做过两次第二次才发现备份脚本漏了etc/目录——恢复出来的实例仓库都在但所有仓库的上游地址和认证配置全丢了等于要重配一遍。教训很朴素备份不验证等于没备份配置文件和数据一样重要。希望帮到你。本文还有配套的精品资源点击获取