Docker plugin delete cached layer in multistage build

Hi,
I am using multi stage build in drone to make final image tiny. The problem is every time drone delete first stage container even when I’m using drone-volume-cache plugin.

Do you have a pipeline definition you can share?
I have used multi stage build without issues (transferring contents from first image to the final image).

Sure.

pipeline:
  restore-cache:
    image: drillster/drone-volume-cache
    restore: true
    mount:
      - /drone/docker
    volumes:
      - /tmp/cache:/cache
  build:
    when:
      branch: master
    image: plugins/docker
    tags: latest
    username: xxxx
    password: xxxx
    registry: my.docker.registery
    repo: my.docker.registery/myimage
    tags: latest
    storage_path: /drone/docker
    build_args:
      - VERSION=master
  rebuild-cache:
    image: drillster/drone-volume-cache
    rebuild: true
    mount:
      - /drone/docker
    volumes:
      - /tmp/cache:/cache
  ssh:
    image: appleboy/drone-ssh
    host: my.host
    username: xxxxx
    password: xxxxx
    port: 22
    script:
      - cd /my/project/path
      - sudo docker pull my.docker.registery/myimage:latest
      - sudo docker-compose up -d

The problem is every time drone start building docker image from first step.

I’m encountering a similar issue, I’m not trying to make use of volume-cache though. I assumed the reason the docker plugin added the ability to prevent removing images after build was because the images would otherwise be persisted.

I setup a .drone.yml that I thought would intelligently re-use the cached build layers but it does not appear to: https://github.com/aaa-ncnu/docker-drupal-starter/blob/908a725f245381f6b42be00bbba394f36d11abae/.drone.yml

Every build step completely rebuilds the image.

I found the following which seems extremely applicable to our issues: How to test built image before pushing it to registry?

I’m sitting here with two potential ways this works in my mind, and I don’t know which is correct (yet) if either:

  1. The docker plugin is not using the host volume for the images built. Which means after the step is complete, the images are destroyed with the container destructuion, making the need to purge/prune/rm/etc unnecessary. I have a hard time accepting this since the docker plugin contains code to cleanup the docker images (which my purge: false yml config should prevent).
  2. The docker plugin is using the host volume, but there’s a cleanup occurring somewhere else. I have a hard time reconciling this in my mind taking into account @bradrydzewski’s comment in the “How to test built image before pushing it to registry?” post.

I assumed the reason the docker plugin added the ability to prevent removing images after build was because the images would otherwise be persisted.

The purpose of the Docker plugin is to provide a safe way to build and publish images without having to grant your entire pipeline privileged capabilities, which would otherwise allow a bad actor to essentially root your host machine with a pull request.

The Docker plugin does not get any special treatment from Drone in terms of caching or persistence. Every pipeline execution is ephemeral, which is a design constraint that every plugin must consider, including the Docker plugin.

So layer caching is not a goal of this particular plugin, but with that being said, one could create their own custom Docker plugin that focuses on layer caching and performance.

Thank you @bradrydzewski, that clarifies things for me. I’m having difficulty thinking of the need to ever remove an image in the docker plugin in that case, however I’m sure there’s probably a use case that was thought of at some point necessitating its purpose.

I’m having difficulty thinking of the need to ever remove an image in the docker plugin

I believe it removes dangling / unused image layers, reducing the size of the docker storage directory. This can be useful if you are caching the docker storage directory (eg with volume-cache plugin) since it will reduce the cache size which would otherwise have unbounded growth.

1 Like

Per @bradrydzewski’s feedback, I dug back into the plugins/docker code and found a configuration PLUGIN_STORAGE_PATH. This configuration dictates the location of the non-privileged Docker storage from the plugin.

I was able to set the property storage_path to be a child of the workspace.base on each pipeline step that needed access to the cache layers.

workspace:
  base: /drone
  path: src/stuff/here

pipeline:
  container-name:
    image: plugins/docker
    # Other properties omitted
    storage_path: /drone/docker-storage

This allows caching between pipeline steps without setting the repo as trusted in Drone’s configuration (and also not needing drillster/drone-volume-cache).

Working example: https://github.com/aaa-ncnu/docker-drupal-starter/blob/master/.drone.yml

1 Like

“So layer caching is not a goal of this particular plugin”

This seems crazy to me. Drone is docker based. Why would layer caching not be a priority when you want to build docker images through it? Without caching, each commit takes minutes longer than it should. I’ve been trying to find an easy way to cache layers but most examples are outdated and there is not a single document example on the official docs about it. I’m trying out cody’s example now :slight_smile:

2 Likes

@codycraven thanks for the help, I didn’t know about purge: false. Is there a way to purge it after copying the cache, without triggering a full build? That’s the only thing I have to figure out now.

I think a common misconception is a) that you must use this plugin to build and publish images and b) there can only be a single docker plugin. Neither of these are true. Think of plugins like npm modules. You can have multiple modules that tackle the same problem differently, but with different features, design choices and tradeoffs. This is, in fact, encouraged :slight_smile:

You can cache Docker layers with Drone; just not with this plugin. The primary goal of this particular Docker plugin is isolation from the host machine, which means it was created under certain constraints, including the ephemeral nature of Drone pipelines. If isolation is not a requirement for you, and you need to maintain state, you can mount the host machine docker socket which will take advantage of the host machine Docker cache.

For example:

pipeline:
  build:
    image: docker
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    commands:
       - docker build -t .
       - docker publish ...

Why would layer caching not be a priority when you want to build docker images through it?

Snapshotting Docker layers and restoring is not a simple problem to solve, although maybe it is easier if you only consider a small subset of use cases and usage patterns, and don’t consider all the edge cases (like dind defaulting to the vfs storage driver and wondering why each repository caches 25 GB of layers). You also need to consider the fact that any solution we implement needs to work universally; it cannot work only for a subset of installations under a subset of usage patterns for a subset of hosting providers.

My goal is to provide something universal. Other teams are encouraged to create plugins with different design goals that may trade portability, simplicity or scalability for performance.

So here is my challenge to anyone reading this thread – create a Docker plugin that can universally replace plugins/docker for thousands of Drone installations, and can perform caching in a way that is fast, secure, works across multiple machines, does not introduce vendor-lockin (eg only works with s3), is efficient with space and does not deplete storage, and is optional. Build that, and I would absolutely, emphatically adopt this as the official Docker plugin.

And if that doesn’t work, consider creating something more targeted that can live alongside plugins/docker as an alternative. It might not work for everyone, but it might also be valuable to a subset of the Drone community and we will certainly welcome giving developers choice :slight_smile:

2 Likes

Thanks for all of this! I have a few ideas. Maybe set a storage limit, and it will purge repo caches that are oldest as it reaches the storage limit. Maybe if I learn go I can attempt it.