gitlab-runner怎么缓存 node_modules 依赖

songxianling1992
  • 1.2k

项目中用到gitlab-runner(v14.2.0)自动部署构建;查看了网上说的缓存 node_modules 的方式;实际效果别不可以

# cache:
#   untracked: true
#   key: "$CI_COMMIT_REF_NAME"
#   paths:
#     - node_modules/

现在每次开始流水线作业的时候;都会先删除 node_modules 依赖;导致整个流程的时间变的很长
image.png
有类似经历的小伙伴吗

回复
阅读 4.3k
3 个回答

你的输出其实是符合期望的,因为gitlab runner在pull代码的时候实际会执行git clean -fdx清空整个项目目录,保证项目工程目录绝对干净,跟版本库保持一致。而缓存会在这个步骤之后将缓存的内容放到指定目录下然后才执行构建。所以你注意后面还有一个restore cache的输出,你的cache又被还原到这里了。

gitlab runner会把cache配置的目录打包成cache.zip文件存储到gitlab runner家目录的cache目录中,按照项目名存放,如果还定义了key,那么还会加一级key目录。如果使用docker,则这个目录为docker容器中的/cache/,因此你需要将这个目录挂载为容器卷才能持久化。在构建前会将cache.zip解压缩放到你的项目工程目录下,然后执行构建,大致是这么个过程。

也就是这个工作原理大致是:

cache:
  paths:
    - my_cache_dir
    - my_cache_dir2

每次构建结束后gitlab-runner会将${CI_PROJECT_DIR}/my_cache_dir${CI_PROJECT_DIR}/my_cache_dir2打包成cache.zip,存放到gitlab-runner缓存目录中去(缓存目录具体见上文说明)。在下一次构建开始的时候(git clean -fdx清理完代码目录之后)会将cache.zip解压缩到你的项目目录中${CI_PROJECT_DIR},然后再运行你的构建脚本定义的构建步骤。

言归正传,我的实践你可以参考。node_modules 太大,而且随着你的npm install增多会有相当多冗余的东西进去(因为CI通常只执行npm i,并不会执行npm uninstall)所以在你更换依赖的时候极容易造成依赖混淆——你的package.json已经删掉某个依赖了,但是node_modules并未删除,并且不会执行更新之类的操作。

我个人的建议是缓存npm cache目录,使用npm ci安装依赖。理由有三:

  • npm ci会先删除node_modules目录,保证不会有冗余的东西在这个目录
  • npm ci会严格遵循package-lock.json锁定的版本号,这样确保和你本地开发的时候版本完全一致,不会有隐含的BUG未被发现
  • npm ci使用npm cache目录,这里面存储的是压缩包,并且版本一致的时候不会向中央仓库发起请求验证版本号之类的,所以速度极快

最后需要注意一点就是gitlab的cache定义的那个路径只能是相对workding directory的,也就是你不能定义绝对路径,你写了也会认为是相对工作目录下的目录(注意这里新版本的gitlab-runner规则修改了,见下面的勘误),因此你必须事先将npm cache目录设置到当前工作目录下才能让cache起作用。

勘误: 似乎最新版本的gitlab-runner修改了这个规则,绝对路径会被识别,但是必须存放在${CI_PROJECT_DIR}目录下,如${CI_PROJECT_DIR}/npm_cache也是有效的。如果你的缓存并不在工作目录下,还有一种方案可以持久化缓存,就是放在gitlab ci的缓存目录,直接使用缓存目录的文件会被直接持久化,并且不会打包成cache.zip再解压,各个executor使用的缓存目录参考官方文档: https://docs.gitlab.com/ee/ci...,这个描述参考这个issue的回复: https://gitlab.com/gitlab-org...

综上,我的.gitlab-ci.yml参考如下:

variables:
  NPM_CONFIG_CACHE: npm_cache
  NPM_CONFIG_REGISTRY: https://registry.npm.taobao.org
  NPM_CONFIG_ELECTRON_MIRROR: https://npm.taobao.org/mirrors/electron
  NPM_CONFIG_SASS_BINARY_SITE: https://npm.taobao.org/mirrors/node-sass
  NPM_CONFIG_PHANTOMJS_CDNURL: https://npm.taobao.org/mirrors/phantomjs
  # 还有其他你需要用到的npm包镜像也可以追加环境变量
  # 这里使用了注入环境变量的方案规避了每次都进行npm config设置npm配置
  # 具体参考npm config相关配置文档: https://docs.npmjs.com/cli/v6/using-npm/config#environment-variables
  # npm cache目录介绍参考npm-cache部分文档: https://docs.npmjs.com/cli/v6/commands/npm-cache
  # 这里通过npm config变量的方式将npm cache目录由默认的~/.npm指定为./npm_cache目录以方便gitlab runner缓存

default:
  cache:
    paths:
      - ${NPM_CONFIG_CACHE}

# ... 省略你的其他的构建步骤

build:
  stage: build
  image: node:14-alpine
  script:
    - node -v
    - npm -v
    - npm ci
    - npm run build
  artifacts:
    name: "build-package"
    paths:
      - dist
    expire_in: 1 day

# ... 其他你的构建步骤

这样只缓存了node_cache目录,这里面存放的其实是npm install或者npm ci下载的包,一般都是tar.gz压缩包,容量比node_modules小的多,而且npm ci会按需安装,你可以检验一下

更多gitlab的实践欢迎访问我们的团队blog: https://blog.dteam.top/tags/g... 。里面的内容都是我在产品环境验证体验过的实践

我说下我们这边目前的做法,仅供参考。

如同 @Feng_Yu 所说,构建生成的 node_modules/ 目录在进入下个阶段(Stage)时也会先被清理。再者,项目构建(install)时安装在 node_moules/ 目录的包非常之多,无论是缓存时的上传还是使用时的下载都非常耗费时间。

于是我们选择了绕路而行,通过 Gitlab 定期流水线(如每天凌晨 5 点)解析 package.json 构建含 node_modules/ 目录基础镜像,再推送至内部的镜像仓库中。

这样项目本身只需要在流水线中使用该镜像作为基础镜像即可更高效的执行构建阶段。

Lionheart
  • 2
新手上路,请多包涵

使用GIT_CLEAN_FLAGS可以跳过删除node_modules

variables:
  GIT_CLEAN_FLAGS: -fdx -e node_modules/
宣传栏