有没有一种方法可以使用缓存加快npm ci的速度?

75

目前,npm ci是使用CI时安装Node模块的最常见方式。但它实在太慢了。 有没有一种方法可以使用缓存来加速npm ci,或者不完全删除现有程序包(整个node_modules文件夹)?


1
这里有一些有趣的基准测试结果,可以帮助改进npm在CI环境下的速度。此外,**npm ci**的行为是删除node_modules文件夹以完成确定性构建。 - ambianBeing
7个回答

58

NPM缓存位于~/.npm,但在大多数CI中,您只能缓存工作目录内的内容。

为了规避这个问题,您可以使用npm set cache .npm将缓存目录更改为当前目录。现在,NPM缓存将位于./.npm,您可以在CI作业之间缓存此文件夹。

GitLab CI示例:

my-super-job:
  image: node:13-alpine
  script:
    - npm set cache .npm
    - npm ci 
  cache:
    paths:
      - .npm

编辑:刚刚发现您可以将配置设置为命令行标志,因此npm ci --cache .npm应该是相同的。


NPM缓存目录可以设置为环境变量吗? - Frederick Nord

48

tl;dr 不完全是。

npm ci 应该在 CI 中优先使用,因为它尊重 package-lock.json 文件。与 npm install 不同,后者会重写该文件并始终安装新版本。

由于设计原因,该命令始终会清除所有本地软件包,即在开始时删除 node_modules 目录。这是长时间构建的主要原因。而且没有选项可以避免这种烦人的行为。

在本地计算机上,您可以通过添加选项 --prefer-offline 来加快 npm ci,告诉 NPM 忽略缓存最短时间并立即使用本地缓存的软件包,而不是将其与注册表进行验证。

然而,NPM 缓存 位于 Unix 上的 ~/.npm 或 Windows 上的 %AppData%/npm-cache。这些文件夹在大多数 CI 中默认情况下无法缓存。例如,GitLab CI 只有 可用工作区域中的存储库 作为缓存。其他目录,如主目录或系统目录(例如 apt),则未被缓存。因此,此设置可能不会影响您的 CI 构建时间。

在旧版NPM中,选项--progress=false通过移除进度条显著影响构建时间。然而,这个问题似乎已经不存在了,我再也无法测量到显著的差异。
最佳实践,肯定会提高速度,是将包分为生产和开发。通过传递选项--only=production,NPM将忽略开发依赖项。由于上述原因,这不会影响缓存。 更新2021-06:现在可以更改缓存目录 如下评论所指出的,现在可以更改NPM缓存目录的位置。例如,通过在每个命令中传递它作为参数(--cache .npm)或环境变量(npm_config_cache=.npm)。因此,将其更改为存储在存储库中的临时目录的路径,将其添加到CI缓存堆栈,但在部署构建中排除它。然后,您将能够在CI脚本中使用--prefer-offline参数。
总结:
  • 拆分依赖项
  • 设置本地缓存目录
  • 使用npm ci --cache <local cache directory> --prefer-offline --only=production --silent
  • 这将加快过程,但由于命令设计的限制,它不能像npm install那样快速

5
在GitLab CI中,您可以使用以下技术缓存npm ci的结果: https://docs.gitlab.com/ee/ci/caching/#caching-nodejs-dependencies - Saqib Ahmed
链接已更新为 https://docs.gitlab.com/ee/ci/caching/#cache-nodejs-dependencies。 - Jarno
太棒了,谢谢。我在我的CI环境中尝试通过缓存依赖项来加速“npm ci”的速度,花费了太多时间。 - derrylwc

9
你可以让你的 CI 缓存 npm 的缓存目录,然后使用选项 --prefer-offline--no-audit,例如:
npm ci --prefer-offline --no-audit

4

在使用 npm ci 运行时,您可以设置缓存文件夹。

npm ci --cache .npm

然后在你的CI中,缓存.npm文件夹


例如Azure DevOps流水线:https://learn.microsoft.com/en-us/azure/devops/pipelines/release/caching?view=azure-devops#nodejsnpm变量: npm_config_cache: $(Pipeline.Workspace)/.npm步骤:
  • 任务:Cache@2 输入: key: 'npm | "$(Agent.OS)" | package-lock.json' restoreKeys: | npm | "$(Agent.OS)" path: $(npm_config_cache) 显示名称:缓存npm
  • 脚本:npm ci
- undefined

3

3
但通常您希望使用开发依赖项来运行单元测试,是吗? - Ruan Mendes
2
这是正确的@RuanMendes,但是如果你有本地使用或在生产构建期间无关紧要的依赖项(例如在CI管道中),使用上述方法将节省一些软件包安装时间。 - Emiel Koning
1
如果你需要使用dev dependencies来进行测试,你可以运行npm ci,然后在构建/测试阶段之后,运行npm prune --production - Coderer

0

免责声明,该方法不足以用于最终版本构建。

然而,如果您需要尽可能快地运行一些辅助脚本,并且只需要来自 packages-lock.json 的所有软件包,您可以将其与 node_modules 文件夹中的 .package-lock.json 文件进行比较。如果在那里检测到更改,则调用通常的npm ci。在我的情况下,这使得某些任务加快了数百倍。

以下是链接到要点 install.js。此方法适用于 npm v7.0 及以上版本。


0
虽然这并不完全回答了你的问题,但是既然你问到了“在不完全删除现有的npm模块的情况下加快npm构建的方法”,我想分享一种我采用的减少生产环境中构建时间的解决方案。这是通过云缓存技术实现的。我考虑的要点如下:
1. If no change in package.json, don't install npm_modules & fetch it from cache
2. Set up a expiry limit for cache, so even if there is no change in package.json for the time limit you have set, revalidate cache
   and install modules again.


云:GCP
在部署过程中,利用了配置了npm模块的GCP kaniko缓存,并将其作为docker构建过程的一部分。这样做可以节省大约2-3分钟的部署周期。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接