利用缓存提升构建速度
# 介绍
众所周知,缓存是性能优化一个重要手段。在 CI 领域,利用好缓存能极大提升流水线构建速度!
下面以前端 NodeJS 为例,介绍两种有效的利用缓存提升构建速度的方法。
# no cache
我们先准备一份 package.json
,里面有这些模块:
{
"dependencies": {
"angular": "^1.8.3",
"ant-design-vue": "^3.2.15",
"axios": "^1.3.4",
"bootstrap": "^5.2.3",
"chokidar": "^3.5.3",
"eslint": "^8.36.0",
"express": "^4.18.2",
"husky": "^8.0.3",
"jest": "^29.5.0",
"jquery": "^3.6.4",
"koa": "^2.14.1",
"markdownlint": "^0.27.0",
"nodemon": "^2.0.21",
"nwjs": "^1.4.4",
"react": "^18.2.0",
"ts-jest": "^29.0.5",
"typescript": "^4.9.5",
"vue": "^3.2.47",
"vuepress": "^1.9.9",
"webpack": "^5.76.1"
}
}
npm install
后看 node_modules
文件夹大小约 326 MB。
在流水线上执行 npm install
,流水线配置文件:
master:
push:
- stages:
- name: install
script: npm install
执行效果:
由于没有 cache,会从网络下载资源,耗时约 42s。
# volume cache
云原生构建
利用 Docker 的 volumes (opens new window)
功能,可在配置文件中通过声明 pipeline.docker.volumes,
将构建机上目录 mount 到容器中。构建任务可将下载好的依赖放入构建机缓存,供后续流水线使用。
Node 流水线配置文件:
master:
push:
- docker:
volumes:
- node_modules:copy-on-write
stages:
- name: install
script: npm install
命中缓存后执行效果:
可以看到前面截图中 added 1973 packages from 1072 contributors
字样消失了,无需从网络下载资源,耗时降为 11.5s。
volumes
的缺点是,缓存只在当前构建机有效。云原生构建
会根据项目构建并发情况,动态在3~8台构建机上轮转,后续流水线分配了其他无缓存的构建机,仍会重新从网络下载资源。
Maven 流水线配置文件:
master:
push:
- docker:
#可以去 dockerhub 上 https://hub.docker.com/_/maven 找到您需要 maven 和 jdk 版本
image: maven:3.8.6-openjdk-8
volumes:
- /root/.m2:cow
stages:
- name: build
script: mvn clean package
Gradle 流水线配置文件:
master:
push:
- docker:
#可以去 dockerhub 上 https://hub.docker.com/_/gradle 找到您需要 gradle 和 jdk 版本
image: gradle:6.8-jdk8
volumes:
- /root/.gradle:copy-on-write
stages:
- name: build
script: ./gradlew bootJar
# docker cache
云原生构建
还提供了一种 cache 方式:在一个镜像中 npm install
好依赖,然后缓存这个镜像在当前构建机,并推送到远端镜像源。
后续流水线使用时,若构建机有镜像缓存,则直接使用。若构建机无镜像缓存,则会从远端镜像源拉取下来。
docker:cache
内置任务使用示例:
master:
push:
- stages:
- name: build cache image
type: docker:cache
options:
dockerfile: cache.dockerfile
by:
- package.json
- package-lock.json
versionBy:
- package-lock.json
exports:
name: DOCKER_CACHE_IMAGE_NAME
- name: use cache
image: $DOCKER_CACHE_IMAGE_NAME
commands:
- cp -r "$NODE_PATH" ./node_modules
cache.dockerfile 示例:
# 选择一个 Base 镜像
FROM node:16
# 设置工作目录
WORKDIR /space
# 将 by 中的文件列表 COPY 过来
COPY . .
# 根据 COPY 过来的文件进行依赖的安装
RUN npm ci
# 设置好需要的环境变量
ENV NODE_PATH=/space/node_modules
无镜像,需要构建并推送镜像的效果:
可以看到耗时比直接 npm install
长,有 1.3m。
构建机无镜像,远端有镜像,从远端拉取的效果:
耗时降到约 16.3s。
构建机有镜像,直接使用的效果:
耗时降到 2.2s,效果非常明显!
# 对比
# 缓存
volumes
:缓存在构建机,效果好docker:cache
:缓存在构建机和远端,效果好
# 复杂度
volumes
:配置简单,清晰易懂docker:cache
:配置复杂,涉及配置文件、dockerfile,有一定理解、使用成本
# 跨流水线
volumes
:同一个构建机中,可跨流水线共享缓存,不可跨构建机docker:cache
:流水线执行过程中,流水线独享。缓存镜像构建完成、推送到远端后,可跨流水线、跨构建机使用
# 缓存更新
volumes
:可控制读写权限,适用更多场景docker:cache
:重新构建缓存镜像并推送到远端,其他构建机需重新拉取。
← 利用复用简化配置文件 上传Docker制品 →