logo
0
0
WeChat Login
Forkfromexamples/ecosystem/use-cache, aheadmain1 commits

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%