Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local Cache Not Being Reused Across Builds with Local File System Caching in Docker Buildx #2890

Open
aditya-kashyapp opened this issue Jan 7, 2025 · 0 comments

Comments

@aditya-kashyapp
Copy link

Current Implementation:

In our CI/CD pipeline setup with Jenkins, we want to leverage Docker Buildx for efficient image builds while utilizing a centralized cache stored on a local file system (e.g., /mnt/cache, which mounted in the Jenkins Worker Pod via a PV/PVC).
We are running Docker Inside Jenkins Worker Pod, but with the switch of Kubernetes form Docker to Containerd, the time taken to build docker image inside the Jenkins Worker image was increased significantly.
So we decided to switch to docker buildx from normal docker build command for building the docker images for which we could leverage the buildx cache export and import feature(--cache-from & --cache-to).

Here for the a build pipeline, if a build is successful, for the next subsequent build on the same pipeline the Layers are Cached and is exported into a Central Cache Location which we have mounted as a PV which is an EFS for cache retrieval and storing.

We are experiencing an issue where Docker Buildx is not reusing cache effectively across multiple builds, despite using the local file system for caching. Specifically, when building different components(Build Pipeline) with the same dependencies (i.e., using the same base image and requirement.txt for Python Package installation), the layers cached are not picked-up during build, even though there are cached blobs available in the file system.
We configure DOCKER_BUILDKIT and CACHE_PATH as environment variables in the Pod Spec.
Jenkins dynamically creates and destroys worker pods for each build

Challenges:

Cache Not Utilized Across Components:

  • For Example: (Build Pipeline are referred as components for simplicity)
    If component1 is built using base image1 and after a successful build, its layers are exported to a central cache, when building component2 (with a shared base image1), it does not reuse the cached layers which were exported by component1 build.
    This results in redundant downloads and builds of already existing layers.

  • Cache Not Reused After Build Failures::
    When a build fails, the cached layers from the previous successful build are not reused for the next subsequent build.
    This leads to repeated rebuilding from scratch for subsequent attempts.


This is our Build.sh script for building the docker image where we are creating the Docker Builder and Image for each build:

# Check if DOCKER_BUILDKIT is set to 1
if [ "$DOCKER_BUILDKIT" == "1" ]; then
    echo "DOCKER_BUILDKIT is set to 1. Using buildx to Build Image"

    # Create and use a Buildx builder (if not already created)
    if ! docker buildx inspect mybuilder &>/dev/null; then
        echo "Creating buildx builder:"
        docker buildx create --name mybuilder --use \
        --driver-opt network=host \
        --buildkitd-flags '--allow-insecure-entitlement network.host'
    fi

    # Define the buildx command
    cmd="docker buildx build --progress=plain \
    --tag ${DOCKER_IMAGE_NAME}:${TAG} \
    --build-arg BUILDKIT_INLINE_CACHE=1 \
    --build-arg DEPENDENCIES=${DEPENDENCIES} \
    --cache-from=type=local,src=${CACHE_PATH},mode=max \
    --cache-to=type=local,dest=${CACHE_PATH},mode=max \
    --output type=docker \
    -f $current_folder/Dockerfile \
    ${ROOT_FOLDER}"

These are the values of the Constants:
${CACHE_PATH} = /mnt/cache
${DEPENDENCIES} = dependencies
${ROOT_FOLDER} = /home/jenkins/agent/workspace/
Cache Path and Dependencies are constant for every build pipeline but the root folder location changes w.r.t Build pipeline.


Jenkins Logs where the Build Layers are Cached when for the same build pipeline if the previous build is successful, the layers are being cached.

11:06:40  DOCKER_BUILDKIT is set to 1. Using buildx to Build Image
11:06:41  Creating buildx builder:
11:06:41  mybuilder
11:06:41  Building the docker image
11:06:41  Docker build command -> docker buildx build --progress=plain \
    --tag dockerregistry/test_comp_01--master:9 \
    --build-arg BUILDKIT_INLINE_CACHE=1 \
    --build-arg DEPENDENCIES=dependency \
    --cache-from=type=local,src=/mnt/cache,mode=max \
    --cache-to=type=local,dest=/mnt/cache,mode=max \
    --output type=docker \
    -f /home/jenkins/agent/workspace/test_comp_01/build/docker/Dockerfile \
    /home/jenkins/agent/workspace/test_comp_01

11:06:41  #1 [internal] booting buildkit
11:06:41  #1 pulling image moby/buildkit:buildx-stable-1
11:06:46  #1 pulling image moby/buildkit:buildx-stable-1 4.7s done
11:06:46  #1 creating container buildx_buildkit_mybuilder0
11:06:46  #1 creating container buildx_buildkit_mybuilder0 0.7s done
11:06:46  #1 DONE 5.4s
11:06:46  
11:06:46  #2 [internal] load build definition from Dockerfile
11:06:46  #2 transferring dockerfile: 848B done
11:06:46  #2 DONE 0.0s
11:06:46  
11:06:46  #3 [auth] sharing credentials for dockerregistry
11:06:46  #3 DONE 0.0s
11:06:46  
11:06:46  #4 [internal] load metadata for dockerregistry/base_images/pipeline_python:3.9
11:06:47  #4 DONE 0.2s
11:06:47  
11:06:47  #5 [internal] load .dockerignore
11:06:47  #5 transferring context: 2B done
11:06:47  #5 DONE 0.0s
11:06:47  
11:06:47  #6 [internal] load build context
11:06:47  #6 DONE 0.0s
11:06:47  
11:06:47  #7 [1/7] FROM dockerregistry/base_images/pipeline_python:3.9@sha256:4a7229b949861d5faf05742825440825ce559d37e3bf1562d06612b3c82c30f4
11:06:47  #7 resolve dockerregistry/base_images/pipeline_python:3.9@sha256:4a7229b949861d5faf05742825440825ce559d37e3bf1562d06612b3c82c30f4 0.0s done
11:06:47  #7 DONE 0.0s
11:06:47  
11:06:47  #8 importing cache manifest from local:8963504665153960020
11:06:47  #8 inferred cache manifest type: application/vnd.oci.image.index.v1+json done
11:06:47  #8 DONE 0.0s
11:06:47  
11:06:47  #6 [internal] load build context
11:06:47  #6 transferring context: 1.36MB 0.1s done
11:06:47  #6 DONE 0.1s
11:06:47  
11:06:47  #9 [3/7] COPY lib /
11:06:47  #9 CACHED
11:06:47  
11:06:47  #10 [4/7] WORKDIR /app
11:06:47  #10 CACHED
11:06:47  
11:06:47  #11 [5/7] RUN /bin/bash /app/build/system/linux/pre-build.sh
11:06:47  #11 CACHED
11:06:47  
11:06:47  #12 [6/7] RUN /bin/bash /app/build/system/linux/build.sh
11:06:47  #12 CACHED
11:06:47  
11:06:47  #13 [2/7] COPY . /app
11:06:47  #13 CACHED
11:06:47  
11:06:47  #14 [7/7] RUN /bin/bash /app/build/system/linux/post-build.sh
11:06:47  #14 sha256:1b13d4e1a46e5e969702ec92b7c787c1b6891bff7c21ad378ff6dbc9e751d5d4 1.05MB / 49.56MB 0.2s
11:06:52  #14 sha256:1b13d4e1a46e5e969702ec92b7c787c1b6891bff7c21ad378ff6dbc9e751d5d4 3.15MB / 49.56MB 5.3s
11:06:58  #14 sha256:1b13d4e1a46e5e969702ec92b7c787c1b6891bff7c21ad378ff6dbc9e751d5d4 4.19MB / 49.56MB 10.4s

Logs for build where layers are not cached, if the same base docker image is being used, but for a different pipeline, the cache is not getting picked-up.

15:09:10  Login Succeeded
15:09:10  Building docker version: 1.0.0
15:09:10  /bin/bash /home/jenkins/agent/workspace/test_comp_02/build/docker/build.sh dockerregistry/test_comp_02-master 11 dependency

15:09:10  DOCKER_BUILDKIT is set to 1. Using buildx to Build Image
15:09:10  Creating buildx builder:
15:09:10  mybuilder
15:09:10  Building the docker image
15:09:10  Docker build command -> docker buildx build --progress=plain     --tag dockerregistry/test_comp_02-master:11     --build-arg BUILDKIT_INLINE_CACHE=1     --build-arg DEPENDENCIES=dependency     --cache-from=type=local,src=/mnt/cache,mode=max     --cache-to=type=local,dest=/mnt/cache,mode=max     --output type=docker     -f /home/jenkins/agent/workspace/test_comp_02/build/docker/Dockerfile     /home/jenkins/agent/workspace/test_comp_02
15:09:10  #1 [internal] booting buildkit
15:09:11  #1 pulling image moby/buildkit:buildx-stable-1
15:09:16  #1 pulling image moby/buildkit:buildx-stable-1 4.7s done
15:09:16  #1 creating container buildx_buildkit_mybuilder0
15:09:16  #1 creating container buildx_buildkit_mybuilder0 0.7s done
15:09:16  #1 DONE 5.4s
15:09:16  
15:09:16  #2 [internal] load build definition from Dockerfile
15:09:16  #2 transferring dockerfile:
15:09:16  #2 transferring dockerfile: 848B done
15:09:16  #2 DONE 0.1s
15:09:16  
15:09:16  #3 [auth] sharing credentials for dockerregistry
15:09:16  #3 DONE 0.0s
15:09:16  
15:09:16  #4 [internal] load metadata for dockerregistry/base_images/pipeline_python:3.9
15:09:16  #4 DONE 0.1s
15:09:16  
15:09:16  #5 [internal] load .dockerignore
15:09:16  #5 transferring context: 2B done
15:09:16  #5 DONE 0.0s
15:09:16  
15:09:16  #6 [1/7] FROM dockerregistry/base_images/pipeline_python:3.9@sha256:4a7229b949861d5faf05742825440825ce559d37e3bf1562d06612b3c82c30f4
15:09:16  #6 resolve dockerregistry/base_images/pipeline_python:3.9@sha256:4a7229b949861d5faf05742825440825ce559d37e3bf1562d06612b3c82c30f4 0.0s done
15:09:16  #6 DONE 0.0s
15:09:16  
15:09:16  #7 importing cache manifest from local:1644044781612389336
15:09:16  #7 inferred cache manifest type: application/vnd.oci.image.index.v1+json done
15:09:16  #7 DONE 0.0s
15:09:16  
15:09:16  #8 [internal] load build context
15:09:16  #8 transferring context: 1.36MB 0.1s done
15:09:16  #8 DONE 0.1s
15:09:16  
15:09:16  #6 [1/7] FROM dockerregistry/base_images/pipeline_python:3.9@sha256:4a7229b949861d5faf05742825440825ce559d37e3bf1562d06612b3c82c30f4
15:09:17  #6 sha256:6ed7e04c7b7dd24a9f05503af94fc72b9dfac39645fb67045c408465288d8850 130B / 130B 0.1s done
15:09:17  #6 sha256:dc82e32b6cd89e10a0df6743a20300199f54b7b21691cc5f101ef162c29c8ace 88B / 88B 0.1s done
...
15:09:27  #6 DONE 10.2s
15:09:27  
15:09:27  #6 [1/7] FROM dockerregistry/base_images/pipeline_python:3.9@sha256:4a7229b949861d5faf05742825440825ce559d37e3bf1562d06612b3c82c30f4
15:09:27  #6 extracting sha256:ad5739181616b815fae7edc6bba689496674acbcf44e48a57fc7cc13a379b3a2
15:09:37  #6 extracting sha256:ad5739181616b815fae7edc6bba689496674acbcf44e48a57fc7cc13a379b3a2 8.9s done
15:09:37  #6 DONE 19.1s
...
15:10:52  #6 extracting sha256:6ed7e04c7b7dd24a9f05503af94fc72b9dfac39645fb67045c408465288d8850 12.7s done
15:10:52  #6 DONE 94.9s
15:10:52  
15:10:52  #9 [2/7] COPY . /app
15:11:05  #9 DONE 12.9s
15:11:05  
15:11:05  #10 [3/7] COPY lib /
15:11:17  #10 DONE 12.5s
15:11:17  
15:11:17  #11 [4/7] WORKDIR /app
15:11:32  #11 DONE 13.0s
...
15:12:42  
15:12:42  #15 exporting to docker image format
15:12:42  #15 exporting layers
15:15:34  #15 exporting layers 176.1s done
15:15:34  #15 preparing layers for inline cache 0.1s done
15:15:34  #15 exporting manifest sha256:c7c82a72ea0574e526101450ef8da0fc00bfb32661fec92d29c269b36bf8885a 0.0s done
15:15:34  #15 exporting config sha256:ac577346d3ba5542fc5751eef1590cc0ea5feed066bdfb107f782d9e71ba4c16
15:15:34  #15 exporting config sha256:ac577346d3ba5542fc5751eef1590cc0ea5feed066bdfb107f782d9e71ba4c16 0.0s done
15:15:34  #15 sending tarball
15:16:01  #15 ...
15:16:01  
15:16:01  #16 importing to docker
15:19:22  #16 DONE 199.7s
15:19:22  
15:19:22  #15 exporting to docker image format
15:19:22  #15 sending tarball 224.7s done
15:19:22  #15 DONE 400.9s
15:19:22  
15:19:22  #17 exporting cache to client directory
15:19:22  #17 preparing build cache for export
...
15:20:09  #17 writing layer sha256:dc82e32b6cd89e10a0df6743a20300199f54b7b21691cc5f101ef162c29c8ace done
15:20:09  #17 writing config sha256:cca08bbb77cbbfc140f3aecc665d2878216212fab6eba749c14e233eee9baea0 0.1s done
15:20:09  #17 preparing build cache for export 49.7s done
15:20:09  #17 writing cache manifest sha256:348775a542004ef0e5182bea7cacb525784fccea7eae60c285ea169cb6fc9bd8 0.1s done
15:20:09  #17 DONE 49.7s
15:20:09  make[1]: Leaving directory '/home/jenkins/agent/workspace/test_comp_02'

Questions:

  • How can we configure Docker Buildx to write to a shared cache directory while preserving previously cached layers and avoiding cache overwrites?
  • What cache configuration or flags should I use to ensure that cache data from different builds does not overwrite each other?
  • Is there a way to manage cache efficiently in a multi-project build environment, especially when using different base images or Dockerfiles?
  • How can we optimize the caching mechanism to reduce image pull times and reuse shared layers across builds?
  • How can we reuse cached layers after a build failure?
  • Are there any potential pitfalls I should be aware of when using a shared cache, such as concurrency issues or the need for cache invalidation?
  • Do we have any dependency on the ${ROOT_FOLDER} which is present at the end of the Docker Build Command for Cache Export and Import as this would change with every build pipeline and if having this different cause any CONTEXT difference?

I have found this warning on the Docker Docs, not sure what is exactly meant by this, does the cache location is over-ridden, but I can see layers in the Cache Folder which are present for older build
As a general rule, each cache writes to some location. No location can be written to twice, without overwriting the previously cached data. If you want to maintain multiple scoped caches (for example, a cache per Git branch), then ensure that you use different locations for exported cache.

Expected Result:
Multiple Docker builds should be able to share a cache directory where:

  1. Layers from different builds are stored in a non-overlapping way, ensuring old layers are retained.
  2. New or modified layers from each build are added to the cache without invalidating or removing previous builds' cache data.
  3. The cache is shared across builds, significantly reducing redundant pulling and speeding up subsequent builds.

Steps tried:

  1. Made Sure the Cache were being exported and were upto date.
  2. Used Cache Mode min, max, but caching only worked on mode=max
  3. Tried to Export Cache to a Temporary Directory During each build, specify a temporary directory under the central cache directory and Merge Temporary Cache into Central Directory After the build, move the contents of the temporary cache into the central directory used rsync, but for all build, weather success or failure, the layers were not cached.
  4. Use BuildKit Inline Cache(--build-arg BUILDKIT_INLINE_CACHE=1)
  5. Used a Centralized Cache Persistence Across Pods to ensure uniformity

Note: We want to use Local Cache export/import.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant