logo
4
7
Login

Improving Build Speed with Cache

Introduction

As we all know, caching is an important technique for performance optimization. In CI/CD, proper use of cache can significantly improve pipeline build speed!

Below we use a frontend NodeJS project as an example to demonstrate two effective caching methods.

No Cache

First, let's prepare a package.json with these modules:

{ "dependencies": { "angular": "^1.8.3", "eslint": "^9.15.0", "jest": "^29.7.0", "koa": "^2.15.3", "next": "^15.0.3", "nuxt": "^3.14.159", "react": "^18.3.1", "vue": "^3.5.13", "webpack": "^5.96.1" } }

Run npm install in pipeline with this configuration:

main: push: "no-cache": docker: image: node:22-alpine stages: - name: npm install script: npm install

Execution result:

no-cache

Without cache, it downloads resources from network, taking about 23s.

Volume Cache

Cloud Native Build leverages Docker's volumes feature. You can declare pipeline.docker.volumes to mount host directories into containers. Build tasks can store dependencies in host cache for future pipelines.

Node pipeline configuration:

main: push: "volume-cache": docker: image: node:22-alpine volumes: - node_modules:copy-on-write stages: - name: npm install script: npm install

After several executions with cache hit:

fit volume cache

The message "added 1973 packages from 1072 contributors" disappears, replaced by "up to date". No network download needed, time reduced to 13s.

The drawback of volumes is that cache only works on current build machine. Cloud Native Build dynamically allocates build machines based on project concurrency. If subsequent pipelines get assigned to different machines without cache, it will download from network again.

Maven pipeline configuration:

main: push: - docker: # Find your required maven and jdk version at https://hub.docker.com/_/maven image: maven:3.8.6-openjdk-8 volumes: - /root/.m2:cow stages: - name: build script: mvn clean package

Gradle pipeline configuration:

master: push: - docker: # Find your required gradle and jdk version at https://hub.docker.com/_/gradle image: gradle:6.8-jdk8 volumes: - /root/.gradle:copy-on-write stages: - name: build script: ./gradlew bootJar

Docker Cache

Cloud Native Build provides another cache method: run npm install in a container, cache the image locally, and push to remote registry.

For subsequent pipelines, if the build machine has cached image, it will be used directly. Otherwise, it will be pulled from remote registry.

Example of built-in docker:cache task:

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

Example cache.dockerfile:

# Choose a base image FROM node:16 # Set working directory WORKDIR /space # Copy files listed in 'by' COPY . . # Install dependencies RUN npm ci # Set required environment variables ENV NODE_PATH=/space/node_modules

First execution without cached image (needs to build and push):

build cache

Takes about 31.5s, similar to direct npm install.

Effect when pulling cached image on new build machine - to be added

Subsequent execution with locally cached image:

local cache

Time reduced to 2.7s - significant improvement!

Comparison

Cache Scope

  • volumes: Cache on build machine, good effect
  • docker:cache: Cache on build machine and remote registry, good effect

Complexity

  • volumes: Simple configuration, easy to understand
  • docker:cache: Complex configuration involving multiple files, higher learning curve

Cross-Pipeline

  • volumes: Shared within same build machine, not across machines
  • docker:cache: Exclusive during pipeline execution. After pushing to remote, can be shared across pipelines and machines

Cache Update

  • volumes: Flexible read/write control, suits more scenarios
  • docker:cache: Need to rebuild and push new image, other machines need to pull again

Complete Example

See .cnb.yml

For actual comparison, check pipeline results in this repository's Cloud Native Build page, or fork to try yourself.

About

利用缓存提升构建速度

Language
Markdown91.2%
Dockerfile8.6%
gitignore0.2%