build(docker): speed up image rebuilds via cache reuse
Note: this isn't high priority, it's not for a ticket, I'm just working on this repo, and I noticed that the docker builds could be sped up a lot, and docker builds happen a lot both in CI and in local dev tasks, like running tests, dev server, etc. So speeding them up should be a win for developer experience!
This reduces the time and network traffic needed to rebuild the container images, by using docker's build-time cache mounts to re-use cached dependencies downloaded by previous builds.
(It also reduces the stored size of the dev image, which also helps speed up build time by reducing the time to export the image after building.)
This works by using --mount=type=cache on RUN commands that install
packages (or do cachable build work, like npm build
):
https://docs.docker.com/reference/dockerfile/#run---mounttypecache
This excludes files within the cache mount from the output image, but
makes the cached files available to the build the next time it runs,
which allows package managers to skip downloading packages on subsequent
builds.
As a result, npm install
takes ~10s with a hot cache vs ~35s with no
cache. Rebuilding the dev image after changing package.json with no
dependency change takes ~20s now, vs ~70s on master.
Also, npm run build
now takes ~10s to rebuild, vs 17s on master.
And the apt-get install stage now does not download the apt lists, nor any package data when it needs to re-install:
#14 [cypress cypress 2/5] RUN --mount=type=cache,target=/var/lib/apt/lists --mount=type=cache,target=/var/cache rm -f /etc/apt/apt.conf.d/docker-clean && apt-get update && apt-get install -y libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
#14 0.183 Hit:1 http://deb.debian.org/debian bookworm InRelease
#14 0.201 Hit:2 http://deb.debian.org/debian bookworm-updates InRelease
#14 0.222 Hit:3 http://deb.debian.org/debian-security bookworm-security InRelease
#14 0.246 Reading package lists...
#14 0.515 Reading package lists...
#14 0.521 Building dependency tree...
#14 0.579 Reading state information...
[...]
#14 1.378 debconf: delaying package configuration, since apt-utils is not installed
#14 1.387 0 upgraded, 268 newly installed, 0 to remove and 0 not upgraded.
#14 1.387 Need to get 0 B/136 MB of archives.
#14 1.387 After this operation, 551 MB of additional disk space will be used.
I expect it should also improve CI build times, because the main build
CI job pull and pushes build cache from the local registry, so after this it should no longer need to download packages from external sources on every run. I've not done it here, but I think we could leverage cache_from
in the compose file to have compose services re-use the build
job's build cache in CI stages after build
.
Edit: I just checked the CI build cache behaviour, it's not able to re-use build cache at the moment, as for some reason the auto devops build script is pulling from an image tag that doesn't exist, and not pushing the cache to the same tag:
#9 importing cache manifest from registry.gitlab.developers.cam.ac.uk/uis/devops/iam/activate-account/frontend/speed-up-docker-rebuilds:0000000000000000000000000000000000000000
We should be able to improve this in principle, but at the moment it helps with local builds anyway.