Docker BuildKit : Faster Builds, Mounts and Features

It was like any other day working on micro-services project, running on Docker environment. In general, we’ve had worked on making our Image Builds more efficient, secure, and faster following basic aspects that significantly affect building and working with Docker.

  • Understanding Docker layers and structuring the Dockerfile to maximize their efficiency.
  • Reducing the weight of the Docker image, by being specific about our Base Image Tags which comes up with minimal packages.
  • Bringing the multi-stage builds concept, etc.

But keeping the spirits of being highly productive and improving more, I landed upon Docker BuildKit. Docker BuildKit is the next generation container image builder, which helps us to make Docker images more efficient, secure, and faster. It has been lingering in the background of Docker builds for some time. Moreover to enable and unleash some massive performance is to set the DOCKER_BUILDKIT=1 environment variable when invoking the docker build command, such as:

$ DOCKER_BUILDKIT=1 docker build .

In this post, we’ll walk through with some of its powerful features which I have explored and came up to these results as below :

Parallelism

Considering a Multi-stage Build, when BuildKit comes across a multi-stage build it get concurrency, it analyzes the docker file and create a graph of the dependencies between build steps and uses this to determine which elements of the build can be ignored; which can be executed in parallel; and which need to be executed sequentially. For example, the stages as build 1 and build 2 can be run in parallel as they don’t depend on each other however the final build stage depends on the other two so it will be build afterwards when both the other two stages are completed.

FROM alpine AS build1
RUN touch /tmp/dee.txt
RUN sleep 10

FROM alpine AS build2
RUN touch /tmp/sandy.txt
RUN sleep 10

FROM alpine AS final
COPY --from=build1 /tmp/dee.txt /tmp/
COPY --from=build2 /tmp/sandy.txt /tmp/

And for more checks I’ve added a delay of 10s to the build 1 and build 2 stages. When using the legacy build engine it executes top to bottom so it will execute two separate sleep commands of 10s which accumulates to a wait time of 20s, nevertheless using BuildKit it will execute both the sleep commands at the same time and therefore only accumulate a sleep time of 10s.

Hence, the standard Docker comes without concurrency in which build command performs builds on Dockerfile sequentially, which means it reads and builds each line or layer of the Dockerfile at a time and as a result it took 49.135s. Whereas BuildKit, allows for parallel build processing resulting in better performance and faster build times thus it only took 27.2s to build it.

Build Secrets Volumes

Sometimes we need some secret keys or password to run our build, just like credentials to access AWS S3 repository. In classic Docker builds there is no good way to do this; the obvious methods are insecure, and the workarounds are hacky. Therefore, BuildKit adds support for securely passing build secrets, which allows build container to access secure files such as secret access keys without baking them into the image.

# syntax = docker/dockerfile:1.2  

FROM python:3

RUN pip install awscli

RUN --mount=type=secret,id=aws,target=/root/.aws/credentials aws s3 cp s3://walletprodtest/terraform ./ --recursive

To build the image,

$ DOCKER_BUILDKIT=1 docker build --secret id=aws,src=/root/.aws/credentials .

The way BuildKit secrets work is that a file with the secret gets mounted to a temporary location during the RUN command, e.g. /root/.aws/credentials. Since, it’s only mounted during a particular RUN command, it doesn’t end up embedded in the final image.

BuildKit mount types doesn’t end only with secret, we have few more :

  • Cache Mount : Sick of re-downloading all external dependencies every time when there’s a change to only one of them, the cache mount can help us save time in the future. Inside of our Dockerfile, add a mount flag, specifying which directories should be cached during the step.

RUN --mount=type=cache,target=/var/lib/apt/lists …

  • SSH Mount : This mount type allows the build container to access SSH keys via SSH agents, with support for passphrases.

RUN --mount=type=ssh … etc.

Using these mount types features, makes it easier for us to pass build-time secrets, handle SSH credentials, and cache directories in between builds even if the layer needs to be rebuilt.

Remote Cache

This feature is an upgraded version from the --cache-from which was having problems such as images need to be pre-pulled, no support for multi-stage builds, etc. With BuildKit, you don’t need to pull the remote images before building since it caches each build layer in your image registry. Then, when you build the image, each layer is downloaded as needed during the build.

For example, keeping the Dockerfile same as used in Build Secrets Mount, we build the image using,

$ DOCKER_BUILDKIT=1 docker build -t dgupta9068/demo --secret id=aws,src=/root/.aws/credentials --build-arg BUILDKIT_INLINE_CACHE=1 .

$ docker push dgupta9068/demo

Now will make a small change in the Dockerfile by adding one more RUN command which will create a test directory. After this, prune the Docker system and try to rebuild the image using

$ DOCKER_BUILDKIT=1 docker build --cache-from dgupta9068/demo .

To use an image as a cache source, cache metadata needs to be written into the image on creation which was done by setting --build-arg BUILDKIT_INLINE_CACHE=1 when building the image.

After that, for the second fresh image build, we used dgupta9068/demo image as cache source by which BuildKit automatically pulled up all the cached layers and just executed the last RUN layer of creating a directory. And hence, ended in a faster build of the Docker Image.

In Conclusion

I hope this article will give a kickstart to use and knowing more about BuildKit and its features which will help you to simplify and speed up your Docker build workflows. If you want to learn more about fasten-docker-build , besides BuildKit – do checkout !

Till then keep building 🙂

Reference – GIF

Blog Pundit:  Abhishek Dubey

Opstree is an End to End DevOps solution provider

Author: Deepak Gupta

Devops Enthusiast | Learner | Cloud | IAC | Orchestration

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s