Memoize Docker image
Memoize Docker image#
Specializes Memoize artifact.
A docker build can take minutes or even tens of minutes. If developers need to wait for this build on every push to CI/CD, they won’t use CI/CD for regular feedback. See the “Value” section in Push for feedback.
Let’s say it takes ten minutes to build a particular docker image, and developers need to push their changes for feedback three times a day because the resources they need to test are only available in CI/CD rather than locally. Over the course of a month, this would mean every developer would have to find something else to do while waiting for feedback for:
import pint ureg = pint.UnitRegistry() # All of these are estimates, despite the lack of uncertainties study_period = 1 * ureg.month push_freq = 3 / ureg.day build_time = 10 * ureg.minute total_cost = build_time * (study_period * push_freq).to_reduced_units() print(total_cost.to(ureg.hours))
In order of correctness:
Sorta Correct: Registry cache#
It’s extremely easy to create a separate project for a docker image in GitLab, build your image
there, then push the image to a container repository you can reach from another project. If you’re
having trouble getting to docker caching to work (e.g. trying to use
dive) then this may be a
faster solution, at the cost of making working on the Dockerfile slower and needing to pull a
separate repository to update the image. You may also have to manually update a tag in the pulling
repository, and switch between the repositories while trying to get a new build to work.
For developers who aren’t working on the docker image, this can save them a few seconds per build waiting for the docker caching system to confirm nothing has changed. Still, this is only a second or maybe two; most of the time docker is taking is for pulling the image which they’ll need to do anyways.
Almost Correct: Docker cache#
Docker-based caching lets you cache any stage in the image, not just the whole image. This makes working on the Dockerfile fast, as well as regular work. For an example of how to do this, see Use Docker to build Docker images | GitLab.
If you want to move images across machines, however, you may need to use
--cache-from (which is
How often do you revisit your assumptions? For docker or container images, this is analogous to how often you need to go back and install or change the libraries in your docker image. You should be doing it often, if you have good habits regarding upgrades and solving problems with other’s tools rather than writing your own.
If it takes a long time, then you won’t do it. Although
rules_docker works, it can be incredibly
slow for docker images because of its commitment to the Open Container Initiative
(OCI) format. You’ll see image-spec/serialization.md at v0.2.0 ·
referenced in the
Every time that you need to do the equivalent of RUN in a Dockerfile you have to do this conversion
back and forth; and as long as you have several intermediate stages between RUN you’ll be fighting
this problem. In
rules_docker the equivalent to RUN is
container_run_and_commit_layer (see rules_docker/README.md · bazelbuild/rules_docker).
A Dockerfile is a list of commands. If any earlier command needs to be rerun, then you have to rerun
the rest of the Dockerfile. If you had a tree of dependencies (rather than a linear chain), then
you’d be able to avoid rerunning many of the commands that were actually independent of the earlier
command that was changed. Said another way, bazel leaves potential to actually be faster than
docker, but this depends on you never needing to
RUN anything (if you’re using
A downside to OCI-formatted tarfiles is that it’s not easy to get a shell in them to debug them, like you can “docker run” an image to try to determine what the next install step should be. If you have podman installed (which works with the OCI format) though, this problem goes away. See Podman Installation and podman-load — Podman documentation.
You should likely avoid
rules_docker until you’ve reached the point of not needing
docker save to load an image into the OCI format that was built with all the
non-hermetic RUN commands that are part of your build, then take only deterministic actions within
rules_docker framework. See e.g.
rules_docker/docker/package_managers/README.md to install
apt packages you downloaded before the build with a