需求与背景
需求
- 公司开发环境与外网隔绝,需要搭建内网私有npm库,确保私密性
- 确保npm服务快速、稳定,减少开发人员和CI服务器的重复下载量并提高下载速度
- 控制npm模块质量和安全,对于下载、发布npm包有对应的权限管理
当下流行的解决方案
-
使用 Nexus 搭建 npm 私服
Nexus仓库管理平台支持范围广泛,可用于maven、npm等仓库。目前Nexus Repository Manager更新到了3,查看官方文档
-
使用 Sinopia 搭建 npm 私服
Sinopia是一个零配置的私有的带缓存功能的npm包管理工具。
搭建过程
使用Nexus
-
下载解压
官方下载链接](help.sonatype.com/repomanager…),执行:
$ wget https://download.sonatype.com/nexus/3/latest-unix.tar.gz #下载 $ sudo mv latest-unix.tar.gz /opt/nexus3.tar.gz #移动到 /opt 目录 $ sudo tar -xzvf nexus3.tar.gz #解压 复制代码
注意运行Nexus需要Java 8 运行时环境(JRE),请自行安装。
-
创建运行用户
单独创建一个 nexus 用户用来运行
# 创建用户、指定用户目录、授权 $ sudo useradd -d /home/nexus -m nexus $ sudo chown -R nexus:nexus /home/nexus $ sudo chown -R nexus:nexus /opt/nexus-3.15.2-01 $ sudo chown -R nexus:nexus /opt/sonatype-work/ 复制代码
修改运行用户配置项:修改 /opt/nexus-3.15.2-01/bin 目录下的配置文件nexus.rc为
run_as_user="nexus"
-
运行
修改端口指8073并开放iptables防火墙,对/opt/sonatype-work/nexus3/etc/nexus.properties文件进行修改:
# Jetty section application-port=8073 application-host=0.0.0.0 # nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jetty-http.xml,${jetty.etc}/jetty-requestlog.xml # nexus-context-path=/ # Nexus section # nexus-edition=nexus-pro-edition # nexus-features=\ # nexus-pro-feature 复制代码
启动服务,以下为nexus服务命令:
# 启动 nexus 服务 $ sudo service nexus start # 重启 nexus 服务 $ sudo service nexus restart # 停止 nexus 服务 $ sudo service nexus stop # 查看 nexus 服务状态 $ sudo service nexus status 复制代码
查看日志检查服务状态:
$ tail -f /opt/sonatype-work/nexus3/log/nexus.log 复制代码
至此,nexus 服务已搭建完毕!可使用默认账号admin/admin123 登录ip:8073后对npm仓库进行管理
-
仓库管理
创建仓库,npm 仓库有三种,这三种我们都需要创建
-
npm(proxy) - 代理npm仓库
将公共 npm 服务器的资源代理缓存,减少重复下载,加快开发人员和CI服务器的下载速度。
创建时需填写Name(
npm-external
)和Remote Storage(公有库地址,填写官方或淘宝镜像,https://registry.npmjs.org/
)。该仓库地址为:
http://ip:8073/repository/npm-external/
-
npm(hosted) - 私有npm仓库
用于 上传自己的npm包 以及第三方npm包。
创建时只需填写Name(
npm-internal
)。该仓库地址为:
http://ip:8073/repository/npm-internal/
请注意:发布包时请将registry设为该地址。
-
npm(group) - npm仓库组
用于将多个内部或外部 npm 仓库统一为一个 npm仓库。可以新建一个npm仓库组将 上面两个刚刚创建的两个 npm 仓库都添加进去。这样可以通过这个 npm仓库组,既可以访问 公有npm仓库 又可以访问自己的 私有npm仓库。
创建时需填写Name(
npm-all
),然后选择需要添加到组里的 其他 npm 仓库(npm-external
和npm-internal
)。该仓库地址为:
http://ip:8073/repository/npm-all/
请注意:安装包以及卸载包时请将registry设为该地址。
-
用户管理
将包发布到nexus npm仓库需要设置一下 Nexus Repository Manager 的权限。否则无法登陆到我们的私服。在Security->Realms栏目里,将
npm Bearer Token Realm
选入Active。
之后我们需要在Security->Users栏目里添加用户,只有这样添加的用户才可以发布包。经测试,在客户端使用 npm adduser
创建的用户没有发布权限。
使用Sinopia
-
服务器上安装node
Node 官网已经把 linux 下载版本更改为已编译好的版本了,我们可以直接下载解压后使用,下载链接
我们选择node-v11.9.0-linux-x64.tar.gz这个版本:
$ wget https://nodejs.org/dist/v11.9.0/node-v11.9.0-linux-x64.tar.gz #下载 $ tar xf node-v11.9.0-linux-x64.tar.gz #解压 $ cd node-v11.9.0-linux-x64/ #进入解压目录 $ ./bin/node -v #执行node命令 查看版本 v11.9.0 复制代码
使用ln命令设置软链接到/usr/local/bin目录,/usr/local/bin是给用户放置自己的可执行程序的地方:
ln -s /usr/local/node-v11.9.0-linux-x64/bin/npm /usr/local/bin/ ln -s /usr/local/node-v11.9.0-linux-x64/bin/node /usr/local/bin/ 复制代码
-
服务器上安装Sinopia
使用默认npmjs.org registry,在服务器上安装sinopia:
$ npm install -g sinopia $ sinopia -bash: sinopia: command not found #测试sinopia命令,此时会报找不到命令 复制代码
找不到命令,原因在于环境变量没设置,修改/etc/profile文件,在末尾添加以下内容:
export NODE_HOME=/usr/local/node-v11.9.0-linux-x64 #Node所在路径 export PATH=$NODE_HOME/bin:$PATH 复制代码
执行命令 source /etc/profile使生效,再次运行sinopia:
$ Sinopia doesn't need superuser privileges. Don't run it under root. warn --- config file - /root/.config/sinopia/config.yaml warn --- http address - http://localhost:4873/ 复制代码
修改iptables设置,开放4873端口:
$ iptables -I INPUT 4 -p tcp -m state --state NEW -m tcp --dport 4873 -j ACCEPT #允许 4873 端口 $ service iptables save #保存 iptables 规则 复制代码
【坑1】访问虚拟机的npm仓库地址被拒绝
除了开放iptables的4873端口外,还需要在sinopia的配置文件末尾加上:
listen: 0.0.0.0:4873 复制代码
重新启动
$ Sinopia doesn't need superuser privileges. Don't run it under root. warn --- config file - /root/.config/sinopia/config.yaml warn --- http address - http://0.0.0.0:4873/ 复制代码
这样我们就能通过自己虚拟机的ip:4873访问了
配置文件在/root/.config/sinopia/config.yaml,相关配置字段意义在文件中都有注释
-
使用守护进程启动(pm2)
安装:npm install -g pm2
启动:pm2 start sinopia
-
用户管理
配置文件中关于鉴权的默认配置为:
auth: htpasswd: file: ./htpasswd //保存用户的账号密码等信息 # Maximum amount of users allowed to register, defaults to "+inf". # You can set this to -1 to disable registration. max_users: 1000 //默认为1000,改为-1后,禁止注册 复制代码
添加用户的方法:在客户端终端运行
npm adduser --registry http://xxxxx:4873/
,设置相应的用户名、密码、邮箱后即可登录、发布包。如果将配置项max_users设为-1,表示禁用 npm adduser 命令来创建用户,需手动在htpasswd文件中添加用户信息来初始化用户。
客户端使用
使用 nrm 管理registry
$npm install -g nrm
$ nrm ls
* npm ---- https://registry.npmjs.org/
cnpm --- http://r.cnpmjs.org/
taobao - https://registry.npm.taobao.org/
nj ----- https://registry.nodejitsu.com/
rednpm - http://registry.mirror.cqupt.edu.cn/
npmMirror https://skimdb.npmjs.com/registry/
edunpm - http://registry.enpmjs.org/
$ nrm add ynpm http://XXXXXX:4873 # 添加私服的npm镜像地址
$ nrm use ynpm # 使用私服的镜像地址
复制代码
安装包
npm install lodash # sinopia发现本地没有 lodash包,就会从 官方镜像下载
npm --loglevel info install lodash # 设置loglevel 可查看下载包时的详细请求信息
复制代码
[storage]$ ls
#下载过之后,私服的storage目录下回缓存安装包
[storage]$ ls
lodash
复制代码
rm -rf node-modules # 删除目录
npm insatll lodash # 第二次安装就会从缓存下载了,速度很快
复制代码
发布包与撤销发布包
在项目根目录下运行$ npm publish
发布新包。
运行$ npm unpublish 包名 --force
撤销发布包。
$ npm publish
+ @shawn280/[email protected]
复制代码
查看发布的包,已成功发布:
作用域scope管理发布包
经常有看到@xxx/yyy
类型的开源npm包,原因是包名称难免会有重名,如果已经有人在 npm
上注册该包名,再次 npm publish
同名包时会告知发布失败,这时可以通过 scope
作用域来解决
-
定义作用域包
修改package.json中包名称:
{ "name": "@username/project-name" } 复制代码
需要注意的是,如果是发布到官方registry,scope一定要是自己注册的用户名,而如果是发布到自己的npm私服,scope可以不是用户名
-
发布作用域包
作用域模块默认发布是私有的
发布到官方registry时,直接
npm publish
会报错,原因是只有付费用户才能发布私有 scope 包,免费用户只能发布公用包,因此需要添加access=public
参数;发布到自己的npm私服时,不加
access=public
参数也可以发布npm publish --access=public 复制代码
-
使用作用域包
npm install @username/project-name var projectName = require('@username/project-name') 复制代码
总结
-
以上的情况并没有考虑在遇到一些黑客攻击的情况下,所以为了尽量保证代码的安全,可以在前端加一层 Nginx 然后配置 SSH 公钥来作为双层验证。
-
两种解决方案的对比:
- 都缓存了从proxy仓库下载过的包,没有同步整个proxy仓库的包。
- 存储方式:nexus使用blob store,sinopia直接保存包文件到storage目录。
- nexus管理界面可以清缓存;sinopia貌似没有命令和工具,但是可以删除通过删除storage目录下包目录的方式清除缓存。
- 使用nexus时,
发布包
和安装、卸载包
需要设置不同的registry,而Sinopia一直用同一个就可以。 - nexus管理页面上展示了更多的包信息,相比而言sinopia也就是nodejs风格的包主页上信息较少。
- sinopia更适合前端工程,优点是配置简单,对环境依赖少(仅node就够了),并且支持在windows系统下运行;nexus支持仓库种类最多,是用户群体最大的一个仓库平台,maven、docker、npm、gradle均支持,java需要maven仓库、android需要gradle仓库、运维需要docker仓库,前端需要npm仓库,如果公司已有nexus平台管理这些仓库,接入新的仓库会较方便。
- sinopia 的权限管理比较弱,对用户权限,发布权限,下载权限控制不是很得心应手;缓存优化不足,经常会在安装共有包的时候处于挂起状态。
One more thing...
sinopia 在15年的时候就停止更新了,继而由 verdaccio 提供更新升级,两者用法基本上都是一致的。对于企业级的应用来说,技术选型时请慎重选择sinopia,建议选择verdaccio。
参考文档
juejin.im/entry/5bceb…
zhuanlan.zhihu.com/p/35907412
segmentfault.com/a/119000000…
www.cnblogs.com/LittleSix/p…
huang-x-h.github.io/2016/06/09/…
segmentfault.com/a/119000001…