详解:使用Nexus搭建Maven、NPM私服并迁移到离线环境使用

本文最后更新于 2024年7月13日 上午

使用Nexus搭建Maven、NPM私服可以帮助您在离线环境中使用Maven和NPM,并自动从私服中下载依赖包和组件。本文将教您如何配置Maven私服、NPM私服、仓库代理、迁移依赖包和组件以及配置离线环境。跟随本文的步骤,您可以轻松地搭建自己的私服,并在离线环境中使用Maven和NPM。

概念解析

为了尽可能帮助到更多的群体,这里解释以下基本的概念。

在通常情况下,本地项目在进行项目依赖构建的过程是这样的:

graph LR
    A("项目(e.g.Spring、Vue)")-->L(本地仓库)-->J{是否存在}
    J-.不存在.->C(中心仓库)-.拉取.->L(本地仓库)
    J-.存在.->B(添加到依赖内)

以Java的Maven骨架工程为例,我们在本地执行mvn install(现在大家都用IDEA,会不会有很多人都不知道手动执行mvn install是干嘛的啦( ̄∇ ̄)),maven会自动扫描pom.xml,根据里面的依赖,先到本地的仓库内寻找有没有之前下载过的jar包;有,则引入到项目内;没有,则下载远程中心仓库Jar包到本地仓库本并引入项目内。

同理,nodejs项目也是一样。所以,有时候我们下载不下远程仓库的包(比如:离线开发),我们就会直接拷贝别人的本地仓库到本地。

镜像仓库

当然,如果本来就是可以连接互联网的情况?但是还是无法连接远程仓库,怎么办?

这个时候,就需要引入镜像这个概念了。

我们熟知的镜像:

使用软件包镜像仓库,就是这样的请求:

graph LR
    A("项目(e.g.Spring、Vue)")-->L(本地仓库)-->J{是否存在}
    J-.不存在.->C(镜像的中心仓库)-.拉取.->L(本地仓库)
    J-.存在.->B(添加到依赖内)

至于大厂为什么免费提供镜像仓库呢?
提供免费的npm、maven镜像仓库是大厂的一种社会责任,同时也是为了吸引更多的开发者使用自己的技术栈和平台,可能可以为公司的其他产品和服务带来商业价值啦。

自建镜像

那么,我们有了大厂的镜像,为什么还需要自建镜像呢?

哈哈,原因当然是很多的,比如:

  • 内网无法连接互联网,内网内架设镜像网站,使得团队成员无需从公网拷贝。
  • 外加私有软件包: 除了中心仓库的公共包,一些团队内私有的工具包,可以只在自己的仓库内发布。
  • 公司、团队网速慢,自建镜像,一次下载,局域网内分发给多人。
graph TB
    A("项目(e.g.Spring、Vue)")-->L(本地仓库)-->J{是否存在}
    J-.不存在.->C(私有的中心仓库)-->JJ{是否存在}
    JJ-.不存在.->CC(互联网镜像的中心仓库)-.拉取.->C
    CC-.拉取.->L
    JJ-.存在.->L
    J-.存在.->B(添加到依赖内)

接下来就看看如何使用Nexus,搭建私有的中心仓库。

支持创作

制作教程不易,如果热心的小伙伴,想支持创作,可以加入我们的「爱发电」电圈:

当然,也欢迎在B站或YouTube上关注我们:

更多:

Nexus是什么?

说起Nexus,甚至还有点小怀念。记得我很久很久以前,有一台Google手机就叫Nexus6p,还是华为代工的。

回到正体,这里的Nexus是Sonatype出品的Sonatype Nexus Repository。可用于存储、共享和分发各种软件包和库。它支持多种软件包格式,如Maven、npm、Docker和NuGet等,并提供了强大的安全性和访问控制功能,以确保软件组件的安全性和可靠性。

Sonatype官网: https://www.sonatype.com/ 

官网提供两个产品:

  • Sonatype Nexus Repository OSS
  • Sonatype Nexus Repository Pro

如果你是大型企业的运维、开发人员,可以考虑上Pro;否则用开源的OSS即可。OSS协议是EPL v1的,对商用也友好。

目前OSS有两个分支,分别是V2和V3。没有特殊原因,建议直接使用V3版本的(V2版本历史悠久,我记得之前是不支持Maven3的,不知道现在怎么样了)。

支持创作

制作教程不易,如果热心的小伙伴,想支持创作,可以加入我们的「爱发电」电圈:

当然,也欢迎在B站或YouTube上关注我们:

更多:

准备工具

准备工具呢? 推荐使用一台Linux服务器作为搭建Sonatype Nexus Repository OSS(以下简称Nexus)。

如果你并没有Linux设备,需要一个Linux设备来练手,强烈推荐:

另外,为大家争取到优惠*(੭ˊᵕˋ)੭ଘ:

环境上,系统需要有JDK的支持,并且当前需要JDK8。如果你不知道如何在Linux上配置JDK,或者需要配置多版本JDK,可以参考教程:

Linux环境

正如前文所所的,我们需要JDK的支持。与此同时,运行Nexus的用户,不建议为系统管理员用户,使用root用户进行操作,总归是有点不安全,万一那天冒出一个类似log4j2的远程命令执行漏洞呢?

建议给非root用户安装。比如:创建一个叫nexus的用户:

1
2
3
4
# 当前root用户
useradd -s /bin/zsh -m nexus
# 设置用户密码
passwd nexus

创建Nexus用户

之后呢?因为nexus在后期高并发情况下,可能同时操作多个文件,我们最好调高进程可以打开的最大文件数:

1
2
3
4
# 查看userid
id -u nexus
# 修改/etc/security/limits.conf
vim /etc/security/limits.conf

修改用户打开文件的上限

修改文件中,可以安装这样的格式${userid} - nofile 65536,其中65536为支持打开的最大文件数量,${userid}为刚刚创建角色的userid。

最后,根据自己喜好,决定是否添加到sudo权限内。并切换到用户。
切换用户

同时,正如前文所说,我们配置了JDK:

1
java -version

配置JDK8

接下来,我们就需要安装和配置Nexus了。

Nexus下载

首先是下载Nexus,你可以在官网进行注册下载,也可以到文档页面下载:

这里有点奇怪,Linux就是代表Linux Is Not Unix;但是这里的Unix版本,实际上就是给Linux的:
Linux版本

我们复制连接,使用wget进行下载:

1
2
3
4
5
6
# 创建目录,方便进行管理
mkdir -p myApplication/nexusHostMirror
# 进入目录内
cd myApplication/nexusHostMirror
# 下载3.55.0版本Nexus
wget https://download.sonatype.com/nexus/3/nexus-3.55.0-01-unix.tar.gz

需要注意,可能部分网络环境,可能存在下载慢的问题,这个就需要大家自己解决了;你也可以向我一样,使用腾讯云的境外地区服务器:
使用wget进行下载

解压后,存在两个文件夹:

1
2
# 使用tar进行压缩包解压
tar -xf nexus-3.55.0-01-unix.tar.gz

解压后的文件

其中:

  • nexus-3.55.0-01: Nexus应用程序的安装目录,包含Nexus应用程序的所有文件和目录
  • sonatype-work: Nexus的工作目录,用于存储Nexus的配置文件、日志文件、插件、仓库数据等

在正常情况下,我们不需要手动修改或删除sonatype-work文件夹中的任何文件,否则可能会导致Nexus应用程序无法正常工作。
而我一般为把nexus-3.55.0-01重命名为nexus-main:
Nexus下载部分完成

到此,我们前期的准备就完成了。

Nexus基础配置

启动前,我们先配置一下Nexus的基础配置。

本质上,Nexus基于Java开发;官方保留并提供了Java虚拟机的启动参数修改文件,也就是Nexus应用程序的安装目录下bin目录内的nexus.vmoptions

我们可以修改其中的内容,比如:

  • -Xms:指定JVM堆的初始大小。
  • -Xmx:指定JVM堆的最大大小。
  • -Dkaraf.home:指定Nexus的安装目录。
  • -Dkaraf.base:指定Nexus的工作目录。
  • -Dkaraf.data:指定Nexus的数据目录。
  • -Dkaraf.etc:指定Nexus的配置文件目录。
  • -Djava.net.preferIPv4Stack=true:启用IPv4协议栈。
  • -Djava.net.preferIPv6Addresses=true:启用IPv6地址。

JVM配置

比如,我一般会调整Nexus的默认端口,所以我会在JVM内增加一条-Dapplication-port=12580:
设置端口

其他基础的配置,大家可以根据实际需求,进一步修改。

启动Nexus

Nexus的启动,本质上就是启动JVM。可以使用systemctl对服务进行管理;但是前期为了方便调试,并且检测是否可以正常使用,我们先用常规的方法,进行临时启动。

所以,本章节就先样式临时启动,再演示注册为systemctl服务。

临时启动

我们进入Nexus应用安装目录内的bin文件夹内。我们通过Nexus的二进制文件进行启动,这里其实还推荐使用screen进行挂起服务;

首先,这个二进制支持那些命令呢?

1
2
# 当前所在目录为Nexus安装目录的bin文件夹内
./nexus

可以看到,目前nexus3支持以下命令:
Nexus支持的命令

其中的含义:

  • start:后台模式启动Nexus服务。
  • stop:停止后台模式的Nexus服务。
  • run:以前台模式启动Nexus服务。
  • run-redirect:以前台模式启动Nexus服务,并将日志输出到控制台。
  • status:检查后台Nexus服务的状态。
  • restart:重启后台Nexus服务。
  • force-reload:强制后台Nexus服务的重载配置文件。

通常情况下,我们就使用run即可,就算后期使用后台模式运行Nexus,也会使用systemctl进行操作。

1
2
# 当前所在目录为Nexus安装目录的bin文件夹内
./nexus run

之后,开始运行:
Nexus使用run进行运行

最后,出现结果Started Sonatype Nexus OSS,代表运行成功:
Nexus run运行成功

确保可以运行,我们就可以关闭Nexus服务(使用Control+C或者Ctrl+C),准备注册为服务来运行了。
Nexus停止运行成功

注册为服务

现在,我们就可以注册Nexus为服务进行运行。方法很简单,使用systemctl:

1
2
# 当前为root用户,或者有sudo权限的用户
sudo vim /usr/lib/systemd/system/nexus.service

具体写法,大家可以按照喜好自己编写,也可以参考我的Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Description=nexus service
After=network.target

[Service]
Type=simple
LimitNOFILE=65536
WorkingDirectory=/home/nexus/myApplication/nexusHostMirror/nexus-main/bin
Environment=INSTALL4J_JAVA_HOME=/home/nexus/myEnvironment/ZuluJDK8
ExecStart=/home/nexus/myApplication/nexusHostMirror/nexus-main/bin/nexus run
User=nexus
Restart=on-abort
TimeoutSec=600

[Install]
WantedBy=multi-user.target

为Nexus编写systemctl

当然,你也可以使用forking模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Unit]
Description=nexus service
After=network.target

[Service]
Type=forking
LimitNOFILE=65536
WorkingDirectory=/home/nexus/myApplication/nexusHostMirror/nexus-main/bin
Environment=INSTALL4J_JAVA_HOME=/home/nexus/myEnvironment/ZuluJDK8
ExecStart=/home/nexus/myApplication/nexusHostMirror/nexus-main/bin/nexus start
ExecStop=/home/nexus/myApplication/nexusHostMirror/nexus-main/bin/nexus stop
User=nexus
Restart=on-abort
TimeoutSec=600

[Install]
WantedBy=multi-user.target

创建好以后,重载systemctl,没什么问题,就可以重启运行了:

1
2
3
4
5
6
7
# 当前为root用户,或者有sudo权限的用户
## 重载systemctl配置
sudo systemctl daemon-reload
## 启动Nexus服务
sudo systemctl start nexus
## 查看结果
sudo systemctl status nexus

运行成功

接下来,就是Nexus的初始化了。

初始化Nexus

通常情况下,使用特殊端口进行服务返回,还是有一些差异。

我这里使用了Nginx进行反向代理,这一步是可选的,如果你需要HTTPS进行访问,也可以加入这步骤:

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
server_name your_domain.com;

location / {
proxy_pass http://localhost:12580/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

我们进行访问:
访问成功

默认密码设置

登录的过程中,admin管理员的密码是什么呢?当然有提示啦,提示初始的密码存放的位置,如:
提示默认密码的位置

在Linux上,我们使用cat命令即可获取默认的密码:

1
cat /home/nexus/myApplication/nexusHostMirror/sonatype-work/nexus3/admin.password

在服务器上获取默认的密码

可以看到,我演示内的密码为78b0616c-9f78-41f5-8b88-454d5a22a341,这个是个随机数,大家需要自己去获取。

在第一次登录后,会让我们进行初始化。初始化请注意,这里会让我们选择是否运行匿名登录:
设置是否允许匿名登录

如果你先完全私有,可以选择Disbale;如果你想一部分公开,一部分私有,那么选择Enable;也不用担心,这一切过程是可逆的,你后续还可以进一步操作。这里我推荐第一次使用的人,选择Enable anonymous access

最后,我们就初始化成功,进入操作台内:
操作台

接下来就教大家如何使用。

Maven仓库

首先,默认就有很不错的示例:
默认样例

其中:

  • maven-central: 预定义的代理Maven Central仓库,它包含了大量的开源Java依赖包。
  • maven-public: 存储库是一个组合存储库,它包含了maven-releases和maven-snapshots存储库的内容。在Maven项目中使用Nexus 3作为私有仓库时,可以将maven-public存储库配置为Maven项目的主要仓库。
  • maven-releases: 存储库用于存储已经发布的Maven依赖包,我们可以设置为需要登录才可以访问,并发布一些团队的工具包等。
  • maven-snapshots: 存储库用于存储快照版本的Maven依赖包,一般快照包,我们在开发过程中频繁更改或更新。

但是官方默认是maven2的版本,我们可以删除并添加自己的maven3仓库。

数据存储

首先,我不建议使用默认的存储位置,这样不利于数据的迁移;我们可以创建应该新的存储,方便后续数据迁移和清理。

登录后台,我们创建一个数据存储:
创建存储

理论上,你也可以使用亚马逊的对象存储系统,也就是S3选项;我们这里还是选择文件存储:
文件储存

特别注意,这里强烈建议Path使用相对路径进行数据的存储,方便后期数据的迁移;比如上述的操作,我就设置存储到相对路径的myDataBase文件夹内,最后的效果:
文件储存

到此,我们的数据存储准备就完成了。

镜像中心仓库

现在,我们正式开始镜像maven3的仓库。首先创建一个自己的maven-central:
创建我们的仓库

之后,选择proxy,用来代理中心仓库:
创建代理

这里推荐中心仓库代理源:

这里我们就使用腾讯云的Maven代理源,镜像官方Maven仓库

设置镜像的源

记得定向存储到我们的存储地址:
存储地址

保存、创建即可。

组合仓库

我们需要创建一个Group进行归纳,也就是对应Demo的maven-public,用于归纳包括刚刚镜像的仓库:
创建

选择Group:
创建Group

还是一样,定向存储到我们的存储地址,并且在成员仓库内,把我们要用的仓库移到其中:
定向存储

保存、创建即可。

使用一个Group的好处是:

  • 方便集合多个仓库(镜像仓库、host仓库)
  • 方便权限管理;比如,想设置一个不存在release的集合专门给匿名登录使用;或者多个不同仓库组合,方便不通情况使用。

私有仓库

用同样的方法,我们创建release和snapshot的私有库:
创建私有库

需要注意,区分release和snapshot的话,使用version policy进行区分:
创建release
创建snapshot

最后,不要忘记在Group内进行引入:
在Group内进行引入

需要注意,Group内存在优先级。建议Release排在第一个。

本地接入

本地接入的地址是什么呢?

我们点开我们的Group集合的复制按钮,即可复制我们的接入地址:
复制接入的地址

假设,你复制的URL为:

1
http://nexus.example.com/repository/maven_public/

那么,你可以在项目的pom.xml内直接添加我们的仓库地址:

1
2
3
4
5
6
7
<repositories>
<repository>
<!-- ID可以自定义,但是要全局唯一 -->
<id>nexus_public</id>
<url>http://nexus.example.com/repository/maven_public/</url>
</repository>
</repositories>

使用maven的插件进行构建(如:mvn clean、mvn install),再添加pluginRepository节点:

1
2
3
4
5
6
7
8
<pluginRepositories>
<pluginRepository>
<!-- ID可以自定义,但是要全局唯一 -->
<id>nexus_public</id>
<name>mirror_from_nexus</name>
<url>https://nexus.pluviose.eu.org/repository/maven_public/</url>
</pluginRepository>
</pluginRepositories>

这样,在maven进行项目依赖包下载时候,会优先到repositories内寻找,找不到,再到全局settings.xml内寻找;所以,你也可以在全局setting.xml内添加上述操作。

当然,在全局内操作,很多人会直接重定向mirror:

1
2
3
4
5
6
<mirror>
<!-- ID可以自定义,但是要全局唯一 -->
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://nexus.example.com/repository/maven_public/</url>
</mirror>

最后的结果:
项目内的使用

与此同时,Nexus上也可以看到操作的效果:
Nexus上展示缓存的包

私有仓库发布

最后,我们在说说私有仓库的发布。我们有时候做了一些工具包,想要分享给团队成员,可以使用Git,也可以直接用Maven发布私有的Release。

也可以使用发布功能,发布快照snapshot,用于备份。

那么我们应该如何操作?如何可以发布到release或者snapshot仓库呢?

首先,我们刚刚定义了
Nexus上定义的release和snapshot

之后,我们需要对releasesnapshot有读写权限的用户,比如admin用户:
管理员默认拥有全部权限

当然,如果你觉得admin用户实在过于超出权限,你也可以手动配置一个角色,赋予release的读写权限:
创建新的角色

之后,创建一个用户并赋予这个用户刚刚的角色权限。

这里为了方便,还是直接使用admin

在本地maven的全局配置settings.xml内,添加用户的密码:

1
2
3
4
5
6
7
<servers>
<server>
<id>nexus_release</id>
<username>admin</username>
<password>passwordForAdmin</password>
</server>
</servers>

设置样子

注意这里的id,这里的id下面会用上。

在项目的pom.xml内,配置上对应的release:

1
2
3
4
5
6
<distributionManagement>
<repository>
<id>nexus_release</id>
<url>http://nexus.example.com/repository/maven_release/</url>
</repository>
</distributionManagement>

注意:

  • 这里的id需要与上面配置servers内的id一样;
  • url需要定向为release,因为group没有push权限。

最后,进行deploy即可:
推送成功

当然,在前台也可以看到:
前台访问看到

如果你不希望release被匿名用户看到,也就是不希望公开。

可以在保留匿名的情况下,给匿名用户重新分配角色,并创建新的group。

Npm/Yarn仓库

这里我们再来看看如何创建Npm仓库。而Yarn理论上就是改版的Npm,所以Yarn可以使用Npm的软件源。

数据存储,我们就用刚刚maven仓库的数据存储了;Nexus会自动进行存储归档并计算文件位置。

镜像中心仓库

我们正式开始镜像npm的仓库。首先创建一个自己的中心仓库:
NPM中心仓库镜像

之后,选择proxy,用来代理中心仓库:

这里推荐中心仓库代理源:

这里我们就使用腾讯云的npm代理源,镜像官方npm仓库

设置NPM镜像的源

记得定向存储到我们的存储地址:
存储地址

保存、创建即可。

组合仓库

我们需要创建一个Group进行归纳,用于归纳包括刚刚镜像的仓库:
创建

还是一样,定向存储到我们的存储地址,并且在成员仓库内,把我们要用的仓库移到其中:
定向存储

保存、创建即可。

使用一个Group的好处是:

  • 方便集合多个仓库(镜像仓库、host仓库)
  • 方便权限管理;比如,想设置一个不存在release的集合专门给匿名登录使用;或者多个不同仓库组合,方便不通情况使用。

私有仓库

用同样的方法,我们创建release的私有库:
创建私有库

最后,不要忘记在Group内进行引入。

需要注意,Group内存在优先级。建议Release排在第一个。

本地接入

本地接入的地址是什么呢?

我们点开我们的Group集合的复制按钮,即可复制我们的接入地址:
复制地址

假设,你复制的URL为:

1
http://nexus.example.com/repository/npm_public/

在终端内直接设置镜像地址:

1
2
3
4
# 设置npm镜像源
npm config set registry http://nexus.example.com/repository/npm_public/
# 设置yarn镜像源
yarn config set registry http://nexus.example.com/repository/npm_public/

最后的结果:
项目内的使用(Yarn)

与此同时,Nexus上也可以看到操作的效果:
Nexus上展示缓存的包(Npm)

数据迁移到内网

到了激动人心的时刻,如何迁移数据到内网呢?

也就是,在公网上缓存好NPM、Maven甚至是pip和yum的软件包;再把数据迁移到内网,供内网设备进行使用。

方法很多,比如你可以直接把整个sonatype-work备份,并迁移到内网替换掉sonatype-work

当然,你也可以用更官方的方法。

备份数据

首先我们看看如何备份数据,在管理员控制台上,打开Task,选择Create Admin - Export databases for backup Task

备份任务

你可以参考我的设置:
备份模版

保存后,我们就可以点击备份:
点击备份

稍等片刻,我们就可以在服务器后台看到了:
备份的数据

这个时候,我们再把之前创建的数据存储也打包到这里:
备份的数据归纳

你可以使用自己喜欢的方法下载到本地;比如:我直接使用scp

1
2
# 当前在,macOS的客户端
scp -r user@host:/home/nexus/myApplication/nexusHostMirror/sonatype-work/nexus3/BackUp ./

内网恢复

接下来,我们看看内网如何恢复数据。

打开内网的设备,在第一次运行Nexus3后,关闭;进入sonatype-work文件夹内,可以看到和服务器上相似的目录结构:
内网Linux设备上的sonatype-work

我们进入db文件夹内,删除内部所有文件:
清除数据库

之后,把刚刚下载下来的文件:

  • .bak文件 -> restore-from-backup文件夹内;
  • 数据存储 -> blobs文件夹内。

最后,启动Nexus,就会发现是我们刚刚的配置状态。
迁移的结果

End

本次演示就到这边。希望这篇文章对你有极大的帮助\(◎◎)/。

另外,其实Nexus不单单可以搭建Maven、Npm的镜像源;实际上,它还可以搭建yum、apy等系统软件源,Python的pip软件包等等。这里就留给大家镜像探索啦。

One More Thing! Nexus的Tasks功能,其实不单单可以用来备份系统文件,它实际上还有很多功能,比如:清理无效数据等等。但是这些大部份团队和中小企业可能用不上,这里就不再过多赘述。

感觉教程有用,欢迎在爱发电支持我们✪ω✪



详解:使用Nexus搭建Maven、NPM私服并迁移到离线环境使用
https://www.mintimate.cn/2023/06/24/hostMirrorByNexus/
作者
Mintimate
发布于
2023年6月24日
许可协议