news 2026/9/16 20:14:28

Nexus3 私库实战:Maven、Yum、Apt、npm 四类仓库配置与避坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Nexus3 私库实战:Maven、Yum、Apt、npm 四类仓库配置与避坑

1. 私库的真实收益:从一次 CI 卡住说起

上周帮一个团队看构建问题,现象很典型:流水线上某个 Java 服务突然构建失败,报错是拉不到一个三方依赖,而本机开发的同学重跑一遍又好了。查下去发现是外网源偶发抖动,本地有.m2缓存所以没事,干净容器里没有缓存就直接挂。这类问题只要出现过一次,团队里就会有人提:"要不我们搭个nexus3 私库吧。"这个提议基本不会错,但真正落地的时候,绝大多数人卡的不是安装,而是四种包格式(maven、yum、apt、nodejs)各自的仓库怎么建、客户端怎么指、出错了往哪查。

我自己维护过一套同时承载 Maven、RPM、Debian 包和 npm 的 Nexus3,也见过只用来缓存 Maven 的轻量部署。两种都成立,区别在于你要不要为"离线可构建"这件事买单。Nexus3 的价值说白了就四块:把公网依赖缓存到内网在没有外网的机器上照样装包把公司自研的构件集中托管让所有依赖走同一个出口便于审计和限流。它不是一个加速器插件,而是一个制品仓库服务,理解这一点,后面很多配置上的"反直觉"就顺了。

提示:Nexus3 的社区版(OSS)已经覆盖 maven2、yum、apt、npm、raw、docker、pypi 等主流格式,不需要为多格式能力额外付费。真正需要评估的是版本差异,有些格式的 hosted 支持是后期才补齐的。

1.1 没有私库时,团队在哪些地方持续流血

最容易被忽略的成本是"重装环境"。一个新人入职,第一件事是配 Maven、配 npm、配 yum 源,如果全靠公网,网络状况一差就是半天。第二块成本是 CI 的稳定性,公网源的 SLA 你控制不了,镜像站偶尔同步延迟几分钟,你的发布窗口就可能被卡。第三块是自研构件的分发,内部 SDK 靠install:install-file手工塞进本地仓库,版本一多就没人说得清线上跑的是哪一版。第四块是合规与审计,到底哪些外部依赖被引入了、谁在什么时候拉了什么包,没有统一出口时几乎查不出来。

这四块成本叠加起来,一次生产事故的排查代价就够买几台机器了。所以判断标准很简单:只要团队里有超过三个人在写同一种语言的代码,或者存在任何一个不能直接出网的环境,私库就值得上。

1.2 一套 Nexus3 能不能同时喂饱四种包格式

能,而且这是 Nexus3 相对其他方案最舒服的地方:一个进程、一个端口、一套账号体系,把 Java、Linux 系统包、Debian 包和前端依赖全收进去。需要提前想清楚的是磁盘和内存的分配,因为这四种格式的体积量级差得很远。

包格式单套上游体积量级私库里的典型增长来源内存敏感度
maven2全量十几 TB,实际只缓存被请求的团队日常构建、CI 每次全新构建中,元数据索引较多
yum单发行版 4 到 10 GB多发行版、多架构叠加很快
apt单发行版 3 到 8 GB第三方源(如 ROS、显卡驱动源)另算
npm全量 TB 级,实际缓存的较小前端项目多、node_modules反复重建中,包数量极多、单包很小

看这张表就能得出一个结论:磁盘要给足,但内存不用按磁盘线性放大。真正吃内存的是并发请求数和包元数据索引,一台 8 核 / 16 GB 的机器撑一个几十人团队绰绰有余,前提是 JVM 堆别配得太小。

2. 上线前的资源盘点和目录规划

我见过太多人下载完 tarball 直接tar -zxvf/root里跑起来,能跑通,但三件事会立刻找上门:磁盘被写满在系统盘、升级时数据目录找不到、进程被Ctrl+C之后重建索引。花十分钟把目录和账号规划好,后面能省掉几小时的救火。

2.1 JDK 版本和 Nexus 版本的对应关系

Nexus3 的运行依赖 JDK,这里的坑是"版本对不上直接启动失败,而且日志里只有一行看不懂的异常"。经验做法是:比较老的 3.2x 系列用 JDK 8 就够;较新的 3.3x 之后官方逐步推荐 JDK 11,部分新版本已经不再支持 JDK 8。先查发行说明,再装 JDK,别反过来。

# 确认本机 JDK 版本,不要用 JRE,Nexus 启动脚本会检查 java 可执行文件 java -version readlink -f $(which java) # 如果机器上有多个 JDK,用环境变量锁定 Nexus 使用哪一个 export JAVA_HOME=/usr/lib/jvm/java-11-openjdk

提示:服务器上同时跑着别的 Java 应用时,不要改全局JAVA_HOME,直接在 Nexus 的启动脚本或 systemd 单元里指定Environment="JAVA_HOME=...",避免互相影响。

2.2 磁盘、内存、端口三件事怎么定

磁盘上,我习惯给 Nexus 单独挂一块盘或者单独一个 LVM 卷,挂载到/data。这样做的实际好处是:磁盘告警、扩容、快照都不影响系统盘,出问题时也容易判断"是不是又被缓存撑爆了"。如果是四格式全开,起步建议 200 GB,前端团队大、或者要托管多套发行版 ISO 的场景给到 500 GB 更从容。

内存上,Nexus 的 JVM 参数写在bin/nexus.vmoptions里,默认大概是-Xms2703m -Xmx2703m再加一个等值的MaxDirectMemorySize。这个默认值是给轻量场景准备的,四格式全开加上多任务并发,我一般会调到 4 GB 到 8 GB,同时保证物理内存比堆多出 2 GB 以上留给操作系统文件缓存和堆外内存。

# /opt/nexus/bin/nexus.vmoptions 摘录 -Xms4096m -Xmx4096m -XX:MaxDirectMemorySize=4096m -XX:+UnlockDiagnosticVMOptions -Dkaraf.data=/data/nexus-data -Djava.io.tmpdir=/data/nexus-data/tmp

最后一行特别值得留意:-Dkaraf.data决定数据目录位置,默认会落在安装目录同级的一个sonatype-work/nexus3下。如果你把安装目录放在/opt,数据实际写在/opt/sonatype-work,系统盘很容易被写满,所以我通常显式改成/data/nexus-data

端口方面,Nexus 默认监听 8081,配置在etc/nexus-default.properties里,可以改application-portapplication-hostnexus-context-path。如果前面要挂反向代理,建议保持 8081 只监听127.0.0.1,对外走 80 或 443。

2.3 用 systemd 托管,别用 nohup 硬扛

裸跑的问题是重启后不会自恢复,而且nexus start会 fork 出子进程,用nohup包一层经常出现主进程活着但子进程死了的假象。标准做法是写一个 systemd 单元:

# /etc/systemd/system/nexus.service [Unit] Description=Nexus Repository Manager After=network.target [Service] Type=forking LimitNOFILE=65536 User=nexus Group=nexus Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk" ExecStart=/opt/nexus/bin/nexus start ExecStop=/opt/nexus/bin/nexus stop Restart=on-abort TimeoutSec=600 [Install] WantedBy=multi-user.target

注意LimitNOFILE=65536,Nexus 并发连接较多时默认的 1024 会不够用,表现为偶发连接被拒,但日志里几乎看不出原因。另外建议单独建一个nexus系统账号来跑,root 直接运行会被启动脚本拦下来或给出强烈警告。

3. Nexus3 装完到能用之间,还差这几步

解压、启动、拿到初始密码,这三步是所有人都会做的。但真正决定这套私库"能不能长期用"的,是接下来那几步安全配置,它们不写进任何快速上手文档,却是踩坑高发区。

3.1 初始密码在哪,以及为什么要立刻改掉

启动之后,初始管理员密码在数据目录下的admin.password文件里。注意这个路径取决于你前面设置的-Dkaraf.data,很多人按默认路径去找结果找不到。

# 假设数据目录已改为 /data/nexus-data cat /data/nexus-data/admin.password # 用完之后这个文件会失效,建议顺手确认它已经不在了 ls -l /data/nexus-data/admin.password

首次登录会强制改密码。改完之后立刻做两件事:一是建一个专门用来部署的账号,不要用 admin 去做mvn deploy;二是确认匿名访问是否符合你的预期。Nexus3 默认开启匿名访问,并给匿名角色授予所有仓库的读权限。内网环境下这样最省事,客户端不用配凭据;如果你想统计"谁拉了什么包",就得关掉匿名访问,然后给每台机器配独立账号,代价是所有 Maven、npm、yum 客户端都要带上凭据。

3.2 Realm 开关:npm 和 docker 用户必看

这一项我踩过,而且踩的时候完全不知道是它的问题。Nexus3 的认证机制是一组 Realm 插件,默认只开了几个基础的。用npm login时如果不打开npm Bearer Token Realm,你会一直收到 401,但用浏览器登录同一个账号却是好的,非常迷惑。

操作路径是SecurityRealms,把需要的 Realm 从 Available 移到 Active:

  • npm Bearer Token Realm:npm 登录和发布必须
  • Docker Bearer Token Realm:要托管镜像仓库时再开
  • Local Authenticating Realm / Default Realm:保持开启,否则本地账号登不进来

改完 Realm 需要重新登录一次。这个动作我记得很清楚,因为当时排查了一个小时,最后发现是 Realm 列表里少勾了一项。

3.3 反向代理与 Base URL,npm 用户的重灾区

如果前面挂了 Nginx 做 HTTPS 和域名,有一个配置项必须补上:Base URL Capability。原因是 npm 的包元数据里包含 tarball 的完整下载地址,这个地址由 Nexus 自己生成。如果不告诉它"你对外暴露的地址是什么",它就会按请求时的 Host 头或者本机 8081 端口生成,导致客户端拿到一个内网端口地址,走到一半就 404。

Nginx 侧的最小配置大概是这样:

server { listen 443 ssl; server_name nexus.internal.example.com; ssl_certificate /etc/nginx/certs/nexus.crt; ssl_certificate_key /etc/nginx/certs/nexus.key; # 大构件上传(比如 RPM、大的前端包)需要放开这个限制 client_max_body_size 2048m; 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-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 缓存的首次拉取可能很慢,别让代理先超时 proxy_read_timeout 900s; proxy_send_timeout 900s; } }

然后在 Nexus 的SystemCapabilities里新建一个Base URL能力,填上https://nexus.internal.example.com。这一步做完,npm 的 tarball 地址才会正确。

4. proxy、hosted、group:把仓库类型想成"三层结构"

Nexus3 里每建一个仓库,第一步都要选类型,很多人在这里犹豫,因为三个选项看起来都像"仓库"。我用一个生活类比来解释:proxy 是外卖代购,hosted 是自家冰箱,group 是取餐窗口。代购负责去外面的店把东西买回来存着,冰箱负责存你自己做的东西,取餐窗口就是把两者摆在一起,让客户端只记一个地址。理解这个类比,后面所有配置都能自己推导出来。

4.1 proxy 是缓存,不是实时镜像

proxy 仓库的关键参数是Remote storage(上游地址),它决定了 Nexus 去哪里抓包。抓回来的东西存在本地 blob store 里,下次同样请求直接命中缓存。这里有两个常被误解的点:第一,proxy不会主动全量同步,你不请求它就不下载,所以磁盘增长是渐进的;第二,缓存的过期策略由Negative Cache TTL和 HTTP 缓存头共同决定,公网源返回 404 时也会被短时间缓存,这就是为什么有些包你刚发到公网、通过私库却拉不到,过几分钟又好了。

Remote storage 的写法有讲究:一定要指向包含元数据目录的那一层。Maven 要指向包含org/com/的根(通常是https://repo1.maven.org/maven2/),yum 要指向包含repodata/的那一层,apt 要指向包含dists/pool/的根。指向错了不会立刻报错,而是会不断返回 404,并在 Nexus 里留下一堆负面缓存。

4.2 hosted 是自留地,release 与 snapshot 必须分开

hosted 仓库是未来放自研构件的地方。Maven 场景下我强烈建议建两个host 仓库:一个maven-releases设为 Release 版本策略,一个maven-snapshots设为 Snapshot 版本策略。混在一个仓库里的后果是:某天一个正式版本被同名快照覆盖,或者发布流水线把错误的策略传给了 Nexus,返回一个 400,全组一起去查原因。

yum 和 npm 的 hosted 仓库同样有一些开关值得注意。yum hosted 有一个Deployment policy,测试期建议设成Allow redeploy,否则同一个 RPM 想重新上传会被拒绝,而 RPM 的版本号一旦写死就不能改了,只能靠加 release 号绕过。npm hosted 默认不允许覆盖已发布的版本,这是有意为之的设计,别轻易关掉。

4.3 group 只负责聚合读流量

group 仓库本身不存东西,它只是一个成员列表,并且按顺序匹配:请求进来后,先从第一个成员找,找到了就返回;找不到才往后走。所以顺序需要按业务意图排:内部构件优先时把 hosted 放前面,纯粹当公共代理用时把代理放前面。

还有一个新手最容易犯的错:部署(deploy / publish / upload)不能指向 group。group 是只读的聚合视图,往它上面mvn deploy会返回 405 或者 400,而报错信息不会明确告诉你"你指错仓库类型了"。记住这条就够了:读走 group,写走 hosted

5. Maven 私库落地:从 mirror 配置到 mvn deploy

Maven 是 Nexus 最经典的用法,配置看起来就改一个settings.xml,但实际踩坑密度最高。我把完整链路拆成三段:建仓库、配客户端拉取、配客户端发布。

5.1 三个仓库的搭建顺序与 URL 形态

标准做法是建三个仓库:maven-central(proxy,指向公共中央仓库或国内镜像)、maven-releases(hosted)、maven-snapshots(hosted),最后建maven-public(group)把三者聚合。建完之后你会得到三种 URL,形态上很容易混淆:

仓库类型典型 URL用途
maven-centralproxyhttps://nexus.internal/repository/maven-central/只给 Nexus 内部用,客户端一般不直连
maven-releaseshostedhttps://nexus.internal/repository/maven-releases/正式版本发布目标
maven-snapshotshostedhttps://nexus.internal/repository/maven-snapshots/快照版本发布目标
maven-publicgrouphttps://nexus.internal/repository/maven-public/客户端唯一读取入口

URL 结尾的那个斜杠很重要,少了它某些客户端版本会把最后一段当成文件名,表现是拉元数据时路径拼错。

5.2 settings.xml 里 mirrorOf 写错,会把所有仓库都劫持

这是我最想提醒的一条。很多教程直接给mirrorOf填一个*,意思是"所有仓库都走 Nexus"。这个写法在只有中央仓库的项目里没问题,但它同时会劫持项目pom.xml里显式声明的第三方仓库(比如某个公司自己维护的 Maven 仓库),而你的 Nexus 里并没有对应的代理,结果就是明明能拉的包突然拉不到了。

<mirrors> <mirror> <id>nexus-public</id> <!-- 只劫持外部源,localhost 和 file:// 保持原样 --> <mirrorOf>external:*</mirrorOf> <url>https://nexus.internal.example.com/repository/maven-public/</url> </mirror> </mirrors>

如果团队情况简单,只写<mirrorOf>central</mirrorOf>更保守,只替换中央仓库,其他仓库声明照常走原地址。两种都可以,关键是别盲目抄*

5.3 发布内部构件:id 必须和凭据对上

发布配置分两处:项目pom.xml里写目标地址,settings.xml里写凭据。两者的连接点是id 字符串必须完全一致,这是 401 报错最主要的原因。

<!-- 项目 pom.xml --> <distributionManagement> <repository> <id>nexus-releases</id> <url>https://nexus.internal.example.com/repository/maven-releases/</url> </repository> <snapshotRepository> <id>nexus-snapshots</id> <url>https://nexus.internal.example.com/repository/maven-snapshots/</url> </snapshotRepository> </distributionManagement>
<!-- ~/.m2/settings.xml --> <servers> <server> <id>nexus-releases</id> <username>ci-deploy</username> <password>你的密码或令牌</password> </server> <server> <id>nexus-snapshots</id> <username>ci-deploy</username> <password>你的密码或令牌</password> </server> </servers>

id 对不上时,Maven 找不到匹配的 server 配置,于是发出匿名请求,Nexus 返回 401。测试期可以先用mvn deploy -X看 Maven 实际发出的请求地址和目标仓库 id,一眼就能看出来。

5.4 部署阶段几个高频报错的对应关系

把常见报错和根因列成一张表,比反复翻日志快得多:

报错片段根因处理方式
Return code is: 401id 不匹配、密码错、账号无写权限核对 server id,检查角色是否有对应仓库的nx-repository-admin-*或写权限
Return code is: 405部署目标填成了 group 或 proxy改成 hosted 仓库地址
Repository version policy: RELEASE does not allow ...往 release 仓库推快照,或反之检查版本号后缀,-SNAPSHOT必须进 snapshot 仓库
拉不到刚发布的包本地.lastUpdated缓存了失败结果删除~/.m2/repository下对应目录的*.lastUpdated,或用mvn -U强制刷新
部署卡住后失败大构件上传被 Nginx 体积限制拦截提高client_max_body_size,并加大proxy_read_timeout

那个.lastUpdated的坑特别值得说。Maven 在拉取失败时会生成一个时间戳文件,在一段时间内不再重试。所以"我在 Nexus 里明明看到包了,本地就是拉不到"这种情况,八成是它。脚本化处理:

find ~/.m2/repository -name "*.lastUpdated" -delete mvn -U clean package

6. Yum 私库:本地 ISO、上游代理与自研 RPM 的合流

Yum 这块的需求往往比 Maven 更刚:生产环境不能出网,但服务器要装监控代理、要打安全补丁、要装自研的运维工具包。Nexus 的 yum 格式能把这三类来源合到一个baseurl上,客户端配置极简。

6.1 remote storage 要指到 repodata 那一层

建 proxy 仓库时,Remote storage 必须指向直接包含repodata/目录的地址,不是发行版的根。举几个实际例子,体会一下差别:

  • 某个 7 系列发行版的基础源:指到.../centos/7/os/x86_64/
  • 更新源:指到.../centos/7/updates/x86_64/
  • 较新的发行版把基础包和模块包拆成了两个独立仓库(BaseOS 和 AppStream),两者各有自己的repodata/,所以必须建两个 proxy 仓库,再用一个 group 把它们合起来

第二点经常被忽略。有人只代理了 BaseOS,然后发现装某些开发工具包时提示找不到,其实是 AppStream 没代理。判断方法很直接:去上游站点看一眼,哪个目录下有repodata/,就给它建一个 proxy。

6.2 把本地 ISO 变成私库里的一个源

离线场景最现实的素材是发行版安装 ISO,它本身就是一套完整的 yum 源。有三种处理方式,各有取舍:

第一种,客户端直接挂载 ISO。优点是零改造,缺点是每台机器都要有一份 ISO、都要手动挂载,而且容器里挂不了。

mkdir -p /mnt/iso mount -o loop /data/CentOS-7-x86_64-DVD-2009.iso /mnt/iso # 客户端 repo 文件里写 file:///mnt/iso

第二种,把 ISO 里的 Packages 上传到 Nexus 的 hosted yum 仓库。这是我最推荐的方式,一次上传,所有机器都能用,也能被容器访问。缺点是初始上传耗时,几 GB 的 RPM 数量在几千个量级,界面批量上传容易中断,建议分批或者用 REST 接口。

第三种,在中间搭一个静态文件服务,用createrepo重建索引后让客户端指向它。这适合你已经在用 Nginx 提供其他静态资源的场景,但对 Nexus 用户来说是重复建设。

提示:如果只是想让"安装系统时能选到本地源",那在安装介质里做 kickstart 更合适;Nexus 私库解决的是装机之后长期的、跨机器的包获取问题。

6.3 自研 RPM 上传与 repodata 的自动生成

Nexus 的 yum hosted 仓库有一个很省心的特性:上传 RPM 之后会自动重建 repodata,不需要你在服务器上装createrepo手动跑。界面路径是进入仓库后点Upload,可以多选一批.rpm一起传。

批量场景下用 REST 接口更稳,参数名以你所用版本的 API 文档为准,yum 格式大致是这样:

curl -v -u ci-deploy:'密码' \ -F "yum.asset=@./ops-agent-2.3.1-1.el7.x86_64.rpm" \ -F "yum.asset.filename=ops-agent-2.3.1-1.el7.x86_64.rpm" \ "https://nexus.internal.example.com/service/rest/v1/components?repository=yum-hosted"

如果确实需要在 Nexus 之外维护一套 RPM 目录(比如从 ISO 里筛出子集),那就必须自己重建索引:

# 在存放 RPM 的目录下执行,生成 repodata/ createrepo -v /data/myrepo # 后续增删了 RPM,用 --update 增量刷新,比全量重跑快很多 createrepo --update /data/myrepo

6.4 客户端 repo 文件与缓存刷新

客户端侧要做的就是写一个 repo 文件指向 group 仓库。我一般建议用 group,把上游 proxy 和自研 hosted 合起来,这样一台机器只需要一个源。

# /etc/yum.repos.d/nexus.repo [nexus-all] name=Nexus All Packages baseurl=https://nexus.internal.example.com/repository/yum-group/ enabled=1 gpgcheck=0

gpgcheck=0在内网很常见,但它意味着不校验包签名。更稳妥的做法是把上游的 GPG 公钥也作为一个文件放到 raw 仓库里,然后gpgkey指向它、gpgcheck=1。另外要提醒的是:Nexus 缓存的上游元数据有 TTL,客户端刚配置完如果报找不到包,先做一次强制刷新,别急着怀疑配置。

yum clean all yum makecache yum repolist -v

如果yum makecache报元数据下载失败,通常就三个方向:baseurl 拼错(尤其是少了或多了路径层级)、group 里成员顺序有问题导致先命中了空仓库、以及 HTTPS 证书不被信任(内网自签证书要额外导入)。

7. Apt 私库:不同 Nexus 版本的能力差异与兜底方案

Apt 这块是四者中最需要"看版本说话"的。Nexus3 对 apt 的支持是逐步补齐的,proxy 相对成熟,hosted 在很多版本里并没有开放,所以实际落地时往往需要一条兜底路线。先把预期建立起来,再动手。

7.1 apt proxy 的指向与签名校验

proxy 仓库的 Remote storage 要指向包含dists/pool/的根目录,比如某个 Ubuntu 镜像的根,或者packages.ros.org/ros/ubuntu这种第三方源根路径。然后在客户端的 sources 列表里,把发行版代号和组件写清楚:

# /etc/apt/sources.list.d/nexus.list deb https://nexus.internal.example.com/repository/apt-ubuntu/ focal main restricted universe multiverse deb https://nexus.internal.example.com/repository/apt-ros/ focal main

这里的focal是发行版代号,它对应上游dists/focal/这个目录。代号写错了就是 404,报错信息里会带上完整路径,对着找一下就能定位。

真正麻烦的是签名。Apt 会校验 Release 文件的 GPG 签名,如果缺公钥,会明确报"public key is not available"。内网环境没法直接联网取密钥,但有个很省事的办法:任何一台已装好的同发行版机器上,公钥文件本来就在系统里,直接拷过来即可。

# 从一台现成的同发行版机器上拷贝(路径是系统自带的,不需要联网) cp /usr/share/keyrings/ubuntu-archive-keyring.gpg /etc/apt/trusted.gpg.d/ # 如果是第三方源(比如 ROS),把对应的 keyring 文件也一起放进去 ls /usr/share/keyrings/ | grep -i ros

如果确实取不到公钥,可以在方括号里加trusted=yes跳过校验。这条路能走通,但它等于放弃了完整性验证,只适合完全隔离、可控的内网,别把它当成常规配置传播。

7.2 当你的版本没有 apt hosted 时怎么办

这是最容易让人卡住的一步:你想把自研的.deb也托管进来,却发现仓库类型里根本没有 apt 的 hosted 选项。这时候别硬等版本升级,用raw 仓库 + 扁平仓库布局就能解决,而且非常稳定。

原理很简单:Apt 支持一种"扁平"仓库布局,只要在指定路径下能找到Packages.gz,就能被识别成一个源。我们要做的就是自己生成这个索引文件,然后把它和.deb一起放进 raw 仓库。

# 1. 准备目录,把所有 .deb 放进去 mkdir -p /tmp/flatrepo cp /data/builds/*.deb /tmp/flatrepo/ cd /tmp/flatrepo # 2. 生成扁平布局的索引(./ 表示当前目录即仓库根) dpkg-scanpackages -m ./ /dev/null | gzip -9c > Packages.gz # 3. 把 Packages.gz 和所有 .deb 一起上传到 raw 仓库(可用界面批量上传,也可用 REST) ls -lh Packages.gz

客户端侧用./明确告诉 Apt 这是扁平布局,而不是标准布局:

# /etc/apt/sources.list.d/internal-deb.list deb [trusted=yes arch=amd64] https://nexus.internal.example.com/repository/raw-debs/ ./

提示:arch=amd64建议写上,否则 Apt 会尝试去请求其他架构的索引,日志里会出现一堆没有实际影响的跳过提示,但会干扰排查。

如果不想用trusted=yes,可以进一步做成签名仓库:用apt-ftparchive release生成 Release 文件,再用 GPG 生成Release.gpgInRelease一并上传,然后把公钥导入客户端。多花十分钟,换来的是可校验的完整性,值不值取决于你的合规要求。

7.3 apt 客户端常见报错速查

报错片段根因处理
public key is not available缺少上游公钥拷贝 keyring 到/etc/apt/trusted.gpg.d/,或临时trusted=yes
Release file ... is not valid yet客户端与服务器时间偏差同步时间,偏差超过几分钟就会触发
404 Not Found且路径带 dists发行版代号或组件名写错对着上游dists/目录核对
Skipping acquire of configured file 'main/binary-i386/Packages'未限定架构arch=amd64
更新极慢但最终成功首次缓存冷启动,所有索引都要回源属正常现象,第二次会明显变快

时间偏差这一条我特别想强调,因为它看起来跟私库毫无关系。内网服务器常见时间漂移,Apt 的签名有效期校验很敏感,一偏就全盘失败。

8. Node.js / npm 私库:registry 切换、发布与离线包托管

前端这块的需求和 Maven 有点像,但依赖数量大得多,一个中型项目node_modules里上千个包很常见,走公网源每次全新安装都在赌网络。npm 私库带来的提速体感在这四种格式里通常是最明显的。

8.1 三个仓库与客户端 registry 的写法

和 Maven 一样的三件套:npm-proxy(指向公共 npm 源)、npm-hosted(自研包)、npm-group(聚合)。group 里的成员顺序建议把 hosted 放前面,这样内部包名和公共包名撞车时优先命中自己的,避免供应链混淆。

客户端配置有两种粒度。全局配置写在用户级.npmrc里,项目级配置写在项目根目录的.npmrc里——项目级的优先级更高,我在团队里统一推行项目级配置,好处是换了机器不用重新配,而且 CI 里天然生效。

# 项目根目录 .npmrc registry=https://nexus.internal.example.com/repository/npm-group/ # 作用域包单独指向内部仓库,避免发错地方 @mycompany:registry=https://nexus.internal.example.com/repository/npm-hosted/ strict-ssl=true

如果把strict-ssl设成false来绕过自签证书,会同时关掉所有 HTTPS 校验,我的建议是宁可把内网 CA 装到系统信任链里,也不要关这个开关。

8.2 npm login 与 Bearer Token Realm

发布内部包需要先登录。这里就是前面提到的 Realm 坑:没开 npm Bearer Token Realm,登录一定失败。登录命令和发布命令分别是:

npm login --registry=https://nexus.internal.example.com/repository/npm-hosted/ # 依次输入用户名、密码、邮箱 # 发布时显式指定 registry,别依赖默认值 npm publish --registry=https://nexus.internal.example.com/repository/npm-hosted/

更省事的做法是在package.json里写死发布目标,这样团队成员直接npm publish就不会发错:

{ "name": "@mycompany/utils", "version": "1.4.2", "publishConfig": { "registry": "https://nexus.internal.example.com/repository/npm-hosted/" } }

两个高频报错:

  • npm ERR! code E401 Unable to authenticate:Realm 没开,或者账号密码不对。
  • npm ERR! 403 Forbidden - PUT ... does not allow updating assets:同一个版本号重复发布。npm 的哲学是版本不可变,正确做法是升版本号,而不是去开覆盖权限。真要覆盖,只能在 hosted 仓库设置里调整 deployment policy,但我不建议在生产仓库这么做。

8.3 用 raw 仓库解决 Node.js 本体的离线安装

包的问题解决了,还有个前置问题:那台不能出网的机器上,Node.js 运行时本身怎么装?答案是用 Nexus 的 raw 仓库托管二进制包,这是很多人没想到但极其好用的一招。

# 在能出网的机器上下载好,上传到 raw 仓库的 nodejs/ 目录 # 目标机器上直接拉取 curl -O https://nexus.internal.example.com/repository/raw-binaries/nodejs/node-v18.20.4-linux-x64.tar.xz tar -xJf node-v18.20.4-linux-x64.tar.xz -C /usr/local/lib ln -sfn /usr/local/lib/node-v18.20.4-linux-x64 /usr/local/lib/nodejs
# 写入 profile,注意把 lib/nodejs 放在 PATH 靠前位置 cat > /etc/profile.d/nodejs.sh <<'EOF' export NODE_HOME=/usr/local/lib/nodejs export PATH=$NODE_HOME/bin:$PATH EOF source /etc/profile.d/nodejs.sh node -v npm -v

如果团队用 nvm 管理多版本,nvm 支持通过环境变量改下载镜像地址。这里有个细节:nvm 会按v版本号/这一层目录去找文件,所以上传到 raw 仓库时要建对应的子目录,否则它会一直 404。

export NVM_NODEJS_ORG_MIRROR=https://nexus.internal.example.com/repository/raw-binaries/nodejs # 对应的目录结构应是 .../nodejs/v18.20.4/node-v18.20.4-linux-x64.tar.xz

8.4 客户端环境里的两个小麻烦

第一个是 npm 自身的本地缓存。私库已经把"下载慢"解决了,但node_modules的写入开销还在。CI 里如果每次都全新安装,可以考虑缓存~/.npm目录,让解压阶段也省掉一部分时间。

第二个问题出现在 Windows 开发机上:执行npm时报无法加载文件 ... npm.ps1,因为在此系统上禁止运行脚本。这不是 Nexus 的问题,是 PowerShell 执行策略拦下了 npm 的包装脚本。两种处理方式,任选其一:

# 方式一:调整当前用户的执行策略(管理员 PowerShell 中执行) Set-ExecutionPolicy -Scope CurrentUser RemoteSigned # 方式二:不改策略,直接用 cmd 版本的可执行文件 npm.cmd install

团队内我一般推荐方式二,改动最小,也不会影响其他脚本的安全策略。

9. 跑起来之后:清理、备份与故障速查

私库上线只是开始,真正拉开运维水平的是三个月之后:磁盘增长到告警线、某个仓库的元数据索引坏掉、升级时发现没有可用备份。这几件事提前做,成本很低。

9.1 清理策略和 blob store 压实

Nexus 不会自动删东西,磁盘只会一直涨。合理做法是三步走:

  1. RepositoryCleanup Policies里建策略,按"最后下载时间"或"最后更新时间"加保留天数来定规则。快照类仓库可以激进一些,比如保留 30 天;release 仓库基本不用清。
  2. 建一个Admin - Cleanup unused components任务,让它每天或每周跑一次。它删除的是"没有任何仓库再引用"的 blob,不会误删被引用的内容。
  3. 建一个Admin - Compact blob store任务,定期压实 blob store。前一步删掉的数据在 blob store 里只是标记为未引用,磁盘并不会立刻释放,压实之后才会真正回收。

提示:压实任务在大仓库上可能跑很久,且期间性能会下降,建议放在业务低峰期,并且别和别的重任务排在同一时段。

9.2 备份到底备什么

最简单也最可靠的方式是冷备:停掉 Nexus,把整个数据目录打包,再启动。数据目录的路径就是你前面配的-Dkaraf.data,默认大概在sonatype-work/nexus3

systemctl stop nexus tar --exclude='./tmp' --exclude='./cache' --exclude='./log' \ -czf /backup/nexus-data-$(date +%F).tar.gz -C /data nexus-data systemctl start nexus

排除tmpcachelog是为了控制体积,这三个目录都不影响一致性。绝对不要在运行中直接拷贝数据目录,尤其是底层数据库文件,拷出来大概率是损坏的,恢复时你会得到一堆看不懂的索引异常。

9.3 一张故障速查表,省掉反复翻日志

现象优先排查说明
页面打不开,进程在端口占用、LimitNOFILE、JVM 堆过小导致频繁 GCnexus-data/log/nexus.logkaraf.log
服务健康但客户端 404仓库类型用错(往 group 写)、路径层级错用浏览器直接访问那个 URL 验证
拉取突然变慢缓存未命中,正在回源;或上游限流第一次慢属正常,持续慢要看上游
上传大文件失败Nginxclient_max_body_size、超时设置同时检查 Nexus 侧是否有大小限制
磁盘满导致服务异常blob store 增长、清理策略未生效先扩容止血,再调整保留策略
客户端报证书错误自签证书未导入信任链导入 CA,别关strict-ssl/gpgcheck

健康检查可以直接调接口,方便接进监控:

curl -u monitor:'密码' -s https://nexus.internal.example.com/service/rest/v1/status curl -u monitor:'密码' -s https://nexus.internal.example.com/service/rest/v1/status/check

第二个接口会逐个检查 blob store 的可写性,比单纯判断进程存活更有意义,建议直接接到告警里。

最后分享一个我自己用下来的经验:这套环境搭好之后,把四种格式的客户端配置都整理成可以一键执行的脚本,放进内部文档或者配置管理工具里。新人配环境从"折腾半天"变成"跑一条命令",这才是私库真正发挥价值的地方——它不只是让下载变快,而是把"环境怎么配"这件事从每个人的个人经验,变成了团队可复制的资产。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 20:14:19

NFS端口为何随机漂移?固定mountd、statd、lockd实战

1. NFS 到底占了哪些端口&#xff0c;为什么它老是变1.1 从一个典型的挂载卡死现场说起去年年底帮一个做视频后期的朋友排查问题&#xff0c;他们的素材库放在一台内网存储服务器上&#xff0c;走 NFS 共享给十几台剪辑工作站。服务器和客户端之间隔了一台硬件防火墙&#xff0…

作者头像 李华
网站建设 2026/9/16 20:12:29

VisualSVN Server备份还原与仓库创建实战指南

1. VisualSVN不是“SVN客户端”&#xff0c;而是Windows平台上的企业级SVN服务中枢很多人第一次接触VisualSVN&#xff0c;是在公司IT部门发来的一封邮件里写着“请安装VisualSVN Server并配置仓库”。结果一搜“VisualSVN下载”&#xff0c;点开官网首页就看到两个并列产品&am…

作者头像 李华
网站建设 2026/9/16 20:10:23

STM32F429 USB RNDIS网络配置实战:裸机LwIP+DHCP打通指南

简介&#xff1a;本资源是面向嵌入式开发工程师与STM32进阶学习者的RNDIS网络通信实战项目&#xff0c;聚焦在STM32F429DISCO开发板上基于LwIP协议栈实现无DHCP的USB RNDIS以太网功能&#xff0c;解决嵌入式设备通过USB虚拟网卡接入主机网络并收发TCP/IP数据的核心问题。压缩包…

作者头像 李华
网站建设 2026/9/16 20:10:23

忘记WiFi密码不用重置:字典攻击与握手包跑包实战

1. 先说清楚&#xff1a;这个故事发生在什么前提下去年年底我把家里那台老路由器的后台管理密码忘了&#xff0c;手机里存着的WiFi密码也换了三次&#xff0c;谁也记不起现在这个到底是多少。家里人急着上网&#xff0c;当时我脑子里冒出来的第一个念头就是&#xff1a;算了&am…

作者头像 李华
网站建设 2026/9/16 20:09:59

OpenMontage:面向AI原生内容生产的智能体编排引擎

1. 项目概述&#xff1a;这不是一个视频剪辑软件&#xff0c;而是一套面向AI原生内容生产的智能编排引擎OpenMontage这个名字乍一听容易让人联想到传统影视后期里的“蒙太奇”&#xff08;montage&#xff09;——那种靠人工拼接镜头、调度节奏、构建情绪的创作方式。但实际接触…

作者头像 李华
网站建设 2026/9/16 20:08:59

MFCC+GMM实现说话人识别:Python完整代码与实战

从MFCC到GMM&#xff1a;手把手教你用Python实现说话人识别&#xff08;附完整代码&#xff09;说话人识别&#xff0c;通俗讲就是让机器通过声音判断“你是谁”。注意它和语音识别是两码事&#xff0c;语音识别是听清“你说了什么”&#xff0c;说话人识别是听出“谁在说”。这…

作者头像 李华