Nicholas Dille, Haufe-Lexware + Docker Captain -
Docker continues to be the standard tool for building container images. For more than a year Docker ships with BuildKit as an alternative image builder, providing advanced features for secret and cache management. These features help to make image builds faster and more secure. In this session, Docker Captain Nicholas Dille will teach you how to use Buildkit features to your advantage.
How to Improve Your Image Builds Using Advance Docker Build
1. Improve Your Image Builds
Using BuildKit
Nicholas Dille, Haufe.Group
Docker Captain & Microsoft MVP
@nicholasdille
2. Nicholas Dille
Husband, father, ops, automator
since 2003
since 2009
since 2010
since 2017
since 2016
Blogger
Speaker
Microsoft MVP
Docker Captain
Haufe.Group
4. Build engines
Legacy build engine
Default when running docker build
Has been around since the early days
BuildKit powered build engine
Based on
Enabled by environment variable:
Faster and more exible than the legacy build engine
Moby BuildKit
export DOCKER_BUILDKIT=1
5. Multi Stage Builds
Multiple FROM sections in Dockerfile
Last section represents nal image
Copy les between stages
Build intermediate images using --target name
Prerequisites: Docker 17.09
FROM openjdk:8-jdk AS builder
#...
FROM openjdk:8-jre
COPY --from=builder ...
#...
6. Multi Stage Builds - Separation
Separate build and runtime environments
Build environment Runtime environment
Compilers (e.g. javac) Runtime (e.g. java)
Build dependencies Execution dependencies
Build tools (e.g. make) -
Large image Smaller attack surface
This also works in the legacy builder
7. Demo: Multi Stage Builds - Separation
Multi-stage with legacy build system:
Multi-stage with BuildKit:
docker build
--tag hello-world-java:multi
.
DOCKER_BUILDKIT=1 docker build
--tag hello-world-java:multi
.
8. build1 build2
final
Built first
Built afterwards
Multi Stage Builds - Concurrency
Stages can be built in parallel when using BuildKit
build1 and build2 are built at the same time
Concurrency is determined based
on the dependency graph
FROM alpine AS build1
RUN touch /opt/binary1
FROM alpine AS build2
RUN touch /opt/binary2
FROM alpine AS final
COPY --from=build1 /opt/binary1 /opt/
COPY --from=build2 /opt/binary2 /opt/
9. Demo: Multi Stage Builds - Concurrency
Stages have a delay of 10 seconds
Build sequentially using the legacy build engine:
Build in parallel using BuildKit:
Sequential build will take ~20 seconds
Parallel build ~10 seconds
time docker build .
DOCKER_BUILDKIT=1 docker build .
10. Classic Build Cache Warming
How it works
Builds may not run on the same host
Pull an image to warm the cache
Internal build cache is ignored when using --cache-from
Prerequisites
Added in Docker 1.13
Image must be present locally
docker pull myimage:1
docker build --cache-from myimage:1 --tag myimage:2
11. Demo: Classic Build Cache Warming
Build and push image:
Reset Docker:
Pull image:
Build with cache from local image:
Internal build cache is used when image does not exist
docker build --tag localhost:5000/hello-world-java .
docker push localhost:5000/hello-world-java
docker system prune --all
docker pull localhost:5000/hello-world-java
docker build --cache-from localhost:5000/hello-world-java .
12. BuildKit Cache Warming
How it works
Use remote images to warm the cache
Image layers will be downloaded as needed
Same syntax using --cache-from
Prerequisites
Cache information must be embedded during build
Docker 19.03
15. Build Secrets
Do not provide secrets using environment variables
ENV burns variables into image
Build arguments (ARG/--build-arg) are only one option
BuildKit to the rescue
Mount using tmpfs
Temporary les in /run/secrets/
Introduced in Docker 18.09
secrets
16. Demo: Build Secrets
Use experimental syntax in Dockerfile:
Build image with secret from mysite.key:
# syntax=docker/dockerfile:experimental
FROM alpine
RUN --mount=type=secret,id=mysite.key
ls -l /run/secrets
export DOCKER_BUILDKIT=1
docker build
--secret id=mysite.key,src=./mysite.key
--progress plain
.
17. SSH Agent Forwarding
Do not copy secrets into image layers
Bad example:
Layers contain SSH key as well as host and user information
BuildKit to the rescue
Forward the socket
Introduced in Docker 18.09
FROM ubuntu
COPY id_rsa /root/.ssh/
RUN scp user@somewhere:/tmp/data .
RUN rm /root/.ssh/id_rsa
SSH agent
19. Demo: SSH Agent Forwarding without BuildKit
Mount existing SSH agent socket
Create environment variable
Prepare SSH agent:
Forward into build:
ssh-keygen -f id_rsa_test
eval $(ssh-agent -s)
ssh-add id_rsa_test
ssh-add -l
docker run -it --rm
--mount type=bind,src=${SSH_AUTH_SOCK},dst=${SSH_AUTH_SOCK}
--env SSH_AUTH_SOCK
alpine-ssh
20. Persisting Cache Directories
Modern software development relies on countless dependencies
Filling caches takes time
BuildKit to the rescue
can be persisted
Syntax is similar to mounting secrets
Cache directories
# syntax = docker/dockerfile:experimental
FROM ubuntu
RUN --mount=type=cache,target=/tmp/cache
ls -l /tmp/cache
22. Using BuildKit
BuildKit can be used in multiple ways
Uses a client/server architecture (daemon and CLI)
Locally Containerized Rootless
Docker X X experimental
Daemon/CLI Demo X X
Daemonless X Demo X
Daemonless is just a wrapper for daemon/CLI
Build container images without access to Docker
23. Demo: BuildKit locally
Run BuildKit locally
Requires daemon and CLI
Run BuildKit daemon locally:
Run build against daemon:
sudo buildkitd 2>&1 >/tmp/buildkit.log &
buildctl build
--frontend dockerfile.v0
--local context=.
--local dockerfile=.
24. Demo: BuildKit daemonless containerized
Run a containerized BuildKit daemon on-demand:
docker run -it
--privileged
--volume $PWD:/src
--workdir /src
--entrypoint buildctl-daemonless.sh
moby/buildkit build
--frontend dockerfile.v0
--local context=.
--local dockerfile=.
25. Transition to BuildKit
Sometime it is desirable to change context and Docker le
What you are doing today
How to do this using BuildKit
Remember: Context is the path which is packed and sent to the
daemon
$ docker build
> --file Dockerfile
> .
$ buildctl build
> --frontend dockerfile.v0
> --local dockerfile=.
> --local context=.
26. Transition to BuildKit
Publish an image in a registry
Docker has taught us to build and push container images:
BuildKit can directly upload to an image registry:
Read more about
docker build
--tag my_image_name
.
docker push my_image_name
buildctl build
--frontend dockerfile.v0
--local dockerfile=.
--local context=.
--output type=image,name=my_image_name,push=true
pushing to image registries
27. Transition to BuildKit
Pass build arguments to customize the image build
The Docker way
The BuildKit way
docker build
--build-arg name=value
.
buildctl build
--frontend dockerfile.v0
--local dockerfile=.
--local context=.
--opt build-arg:name=value
28. Transition to BuildKit
Use an existing image as build cache
Docker is able to use an local image
BuildKit can use an image in a registry...
...and download helpful layers
docker build
--cache-from my_image_name
--tag my_image_name
.
buildctl build
--frontend dockerfile.v0
--local dockerfile=.
--local context=.
--output type=image,name=my_image_name,push=true
--export-cache type=inline
--import-cache type=registry,ref=my_image_name
29. Summary
BuildKit brings new features to image building
Multi stage builds
Protect secrets using mounts and SSH forwarding
Improve performance by persisting cache directories
Works with and without Docker
Thanks for joining!
, ,
(see QR code for slides and demos)
(see for slides sources)
Tibor Vass Tonis Tiigi Akihiro Suda
here