自从搭建了npm内网镜像加速,我才发现淘宝源原来这么快...

2,295 阅读11分钟

哈喽大家好啊,我是广州小井。有关注笔者专栏的都知道,最近我升级了打包流程,把构建步骤放到容器中执行了。因为做了环境隔离无法复用cache,导致了目前装包时间疯狂飙升。由此,我萌生了加速装包的念头!

起因

要说为什么要搭建内部npm镜像,那肯定是希望在装包中一飞冲天!当然,最主要的原因还是因为最近升级了容器内构建导致了npm的缓存被隔离掉了,从而导致每次项目装包过程都需要从头来过,这样让原本就不快的项目构建流程雪上加霜~

如果错过了上一篇文章的小伙伴可以戳链接去了解了解!

既然是出于构建稳定性的考虑隔离掉npm的缓存,所以当我需要面对构建加速的优化命题时,我把目标投向了装包加速。众所周知!npm官方源的下载速度特别感人,所以国内基本上都是通过伟大的淘宝源来实现npm装包加速来缩短这个蛋疼的装包时间。

基于以上这一点呢,机灵的我马上就想出了:搭建一个内部的npm镜像站点来给“装包提速”再上一个台阶。毕竟走内网,还能比不过外网吗?所以,我又给自己提了个需求:搭建一个按需缓存的内部npm镜像源,实现公司内所有前端项目依赖包安装的内部镜像加速。具体可以看下图:

npm内部加速图.png

于是,笔者就开启了长达两天的踩坑之旅了...

方案调研

想搞内部npm镜像加速,怎么搞?当然是...google啦,难道问gpt吗?(也不是不行哈哈哈)于是我基于自己的经验积累,开始调研起了 cnpm.org 这个项目...

image.png

1. cnpm

相信 cnpm.org 大家并不陌生了,毕竟很多企业在以前要搞私有npm包的时候都是通过私有化部署 cnpm.org 来做的(我们就是这样)!为什么说是以前?因为现在的 cnpm.org 已经转移到 cnpm-core 了。

image.png

好吧,感兴趣的自己点链接去 github 看看吧,我们接着往下看。

当然我也有理由相信,大部分的团队私有化部署 cnpm 都是来做 npm 私仓的,很少用来做内部npm镜像加速。毕竟,我们也是这么干的哈哈哈。所以,我就从这个最熟悉的老朋友入手,先试着用 cnpm.org 来搞npm内部镜像试试。

其实 cnpm.org 自身就是一个以做npm镜像源为出发点的项目,所以要用它来实现内部npm镜像简直简单到不能再简单了。直接改动它的配置文件的一个配置就行了。如下代码块(拷自cnpm的配置文件):

// sync mode select
// none: do not sync any module, proxy all public modules from sourceNpmRegistry
// exist: only sync exist modules
// all: sync all modules
syncModel: 'exist', // 'none', 'all', 'exist'

从注释中很轻易地就理解了三个配置值的作用:

  • none 就是不去官方源同步任何模块

  • exist 仅同步已有的模块

  • all 同步全部模块

如果是仅仅私有化部署来做内部npm私仓,直接配置成 none 就行了。如果把这个属性值配置成 exist 会怎么样呢?它会自动的帮我们同步我们下载过的包的全部版本(注意是全部版本,后文会提到)。

比如说我们通过这个服务下载过 vue,他会自动到我们配置的镜像源中同步该包的所有版本。如下图所示(太多了,根本截图不完):

image.png

好,那现在我们站在巨人的肩膀上,很轻松就实现了内部npm镜像的需求了,甚至连代码都没敲几行...那是不是就意味着调研阶段就结束了呢?并不是这样的,因为此时的我想了一下:我需要申请多大的磁盘资源来存放包呢?

所以,cnpm的这种同步方案并不完全是我设想的那种按需缓存的内部npm镜像源。我只是简单的想了下,依赖的依赖有其他的依赖,每个依赖都要把全部版本同步下来,那对于机器的成本好像也一下子就变多了。(虽说磁盘不值钱,但我穷啊)

2. verdaccio

经过各种搜搜搜,我找到了一个跟我的需求比较契合的开源项目——verdaccio。这个项目对比 cnpm 有什么特别之处(省流):npm私仓、可私有化部署、按需缓存模块

没错,相较于 cnpm 的自动同步而言,verdaccio 并不会自动同步,仅仅作为一个可缓存已安装模块的代理而已。比如我们的打包机需要安装 vue@3.0.0,如果 verdaccio 中有一个这个版本的 vue,便会直接从内网下载;如果没有,则会去外网npm源(可配置)拉取到部署 verdaccio 的当前机器中,再被下载到打包机。

用 verdaccio 做私有化部署也非常简单,官方文档有详细的介绍,这里就不直接展开了。感兴趣的可以快速看看 verdaccio 的 readme ,然后自己本地部署一个玩玩~

那简单介绍一下我的情况,我直接将 verdaccio 作为依赖安装到一个项目里的,然后通过自定义的 config.yaml 来做一些简单的配置完成私有化部署。安装完成后,在 pjs 中配置一个 start 命令来启动服务:

"scripts": {
  "start": "verdaccio --config config.yaml"
},

yaml 文件就是直接 copy 官方的回来随便改一下:

image.png

好吧,这些都不是重点。重点是要看 verdaccio 的装包成果。当我首次安装一个 vue 时,它会通过配置中的上游源(首次安装肯定没缓存)去下载对应的包,这时候我们看看它下载完后有什么:

image.png

如上图所示,简单明了,除了当前的版本和一个 pjs 文件,没有任何多余的内容,简直完美契合完的需求!奶思!

总结:其实,cnpm 和 verdaccio 的功能差异我们可以从 cnpm 的出发点思考从而得出答案。cnpm 的目的就是要做第二个npm源,所以它去同步每一个包的全部版本的行为是很有必要的。但对于我而言,我仅需要服务公司内的前端团队,把整个npm源站的包同步过来难免有点小题大做了,对机器资源也是一个较大的开销,毕竟我也只是为了做内网镜像加速而已。所以,我最后选择了私有化部署 verdaccio 来实现本次的需求。

这里不得不提一句,十分感谢淘宝源为我们这些普通开发提供了稳定而又提速的npm装包服务,做为白嫖党的我还是得不断地道谢,十分感谢他们的巨大贡献~

verdaccio上机测试

调研好了方案,当然是要去验证它,使用它,然后顺便拿高绩效给公司的基建做贡献啊!于是我马不停蹄的在其中一台测试的机器上安装了 verdaccio,并通过其启动了一个代理服务作为npm内部镜像源...(此时,项目的线上打包机和这台测试用的机器都在广州)

照理来说,两台机器之间没有跨区域,内网下载,应该能比用外网的源快个不少吧?可惜,测试的结果出乎意料...省流:最终测试结果,内部npm镜像并没有比淘宝源有较大的速度提升。具体的测试验证过程我们接着往下看吧~

本次测试对比仅针对淘宝源,毕竟淘宝源基本上是我们国内装包加速的首选了吧,大街小巷的人都知道。又因为 npm官方源 慢的不是一丁半点,所以就没有跟内部源做对比的必要了。

大概看看测试的恒定操作条件:

  1. 删除 node_modules

  2. 清除 npm cache:npm cache clean –force

  3. 各自恒定的 package-lock 文件

  4. 保证 verdaccio 中已缓存本次需要的所有模块

这里提一嘴 package-lock 相关,本次实验中都是采用第二次安装后产生的数据。第二次安装后是指:首次安装前删除了 package-lock 文件,安装后新生成了当前源package-lock 文件后再安装。具体这样做的原因是有两点:

  1. package-lock 中的 resolved 字段会干扰实验。比如第一次用淘宝源安装后,该字段会是一个淘宝源下的地址,这个时候即使我们配置的 .npmrc 中的 registry 为其他源都无效,会被覆盖掉...(详细的自行搜索了解哈)

  2. 没有 lock 文件的时候,即使 verdaccio 中已经有了当前要下载的 npm包,但是它还是要发一条请求给到上游源(实验中上游设置成淘宝源)。虽然都是 304 的请求,但每一个包都要发一条,其实也是会影响实验结果的。更何况不会有经常删除 lock 文件的行文存在。

其他测试条件:

  • 机器:2核4g

  • 项目依赖数:533

ok,在上述的条件、操作下,我进行了不下 10 次的对比实验。以下是其中一次的截图:

image.png

image.png

其中内部源最快是22s,淘宝源最快是23s,整体测试过程中,总体差距真的不大,平均下来的差距仅仅只有1s左右。一开始我也怀疑是这台测试机的性能比较低,跑IO跑满或者是网速慢的问题。但是在运维大佬的指引下,查看了下载中的网络、CPU使用情况,并没有发现机器负载过高,网速也还可以(偶尔快得惊人)...如下图是通过 iftop 查看的:

image.png

由于机器、运维这块我并不熟悉,所以我也并有没在上面的方向去继续深挖到底是哪里的问题导致的内网速度跟淘宝源差不多,甚至只有微弱的优势...我无聊地看回了 jenkins 中输出的装包日志,好像发现了端倪!找到了一个并非我配置在 registry 中的域名:registry.npmmirror.com 而是 cdn.npmmirror.com !

当配置了淘宝源,无 lock 文件时,首次安装日志中,淘宝源域名的出现情况:

image.png

右上角可以看到,搜索结果总共有 572 个。当首次安装完,产生了 lock 文件后,再次搜索淘宝源域名的情况:

image.png

右上图可以发现,淘宝源地址的搜索结果仅有 8 个,取而代之的是多个 cdn.npmmirror.com 的域名。比如我们搜一下 cdn 开头的域名:

image.png

此时,我不禁感叹 cdn 加持就是快啊。在感叹的同时,又由衷的感谢淘宝源给我们带来的便利,感谢各位贡献淘宝源的大佬,小弟感激不尽。在阿里云开发者社区中看到这样一段话和数据:

image.png

讲到这里,基本上宣告本次的调研和想法就要“泡汤”了。在包加速的路上没办法走下去,另外一个搭建内部npm镜像源的出发点就是稳定性了,以防哪天淘宝源出现抽风、抖动情况...啊呸,淘宝源会抽风,你内部搭建的源就不会?emmm好吧,我放弃了,就乖乖用淘宝源吧!

写在最后

当然,本次做的努力基本上是不会以产出成果的方式沉淀下来了,毕竟从速度、稳定性出发,淘宝源都非常优秀了(比起我内部自己搭一个)。并且难以想象他们在这一块投入的心血有多大...研发团队、机器成本、好多好多的cdn站点...但是有些时候收获不一定是要有结果的,过程才是重要的。

其实我们内部的 cnpm 是16年的时候 fork 下来私有化部署的,版本相对也比较落后了。在调试使用 cnpm 做内部源的时候,也碰到了很多小问题,通过查 github 中的 issue,更新某些文件成最新代码一点一点解决问题。过程中算是积累了一些做调研、解决问题、分析可行性、机器成本预算等等的经验,虽然没有实质性的产出,结果也还算ok吧,最后就将这次经历写成文章纪念一下。感谢你的阅读,文中内容如有错误,请指出,共同学习进步,感激不尽。