TIQETS.ENGINEERING

Make Your Docker Builds Swim Faster

How we speed up Docker builds at Tiqets

Raditya Surya
Tiqets Engineering

--

There are multiple ways to and methods for building efficient docker images. You can find the guidelines of those best practices on the official docker website here.

Here’s how I did it.

Understand your Docker build context

First, you need to understand your current build context. Build context is the current working directory where you build your docker file is. This is where your Dockerfile usually is by default, but you can also specify where the context is with the file flag (-f ). All the documents and directories inside the working directory are sent to the Docker daemon as the build context.

When you run docker build command, you will see this message on your terminal that shows how big your docker build context is:

Sending build context to Docker daemon  666.8MB

Including files and directories that are not necessary to build docker images results in a larger build context and larger image size. This can increase the time to build the image, time to pull and push it, and the container runtime size.

To check the build context, use docker-show-context: It shows where time and bytes are being spent when building a docker context.

Unfortunately, this only shows the top 10 biggest files and directories on your docker build context. I forked the repository and added a change to show all the files in your Docker build context — get it here.

$ cd ~/path/to/project/using/docker
$ docker-show-context
Scanning local directory (in tar / on disk):
24 / 1057 (62 / 216 MiB) (0.0s elapsed) .. completed
Excluded by .dockerignore: 1033 files totalling 153.98 MiBFinal .tar:
24 files totalling 61.83 MiB (+ 0.02 MiB tar overhead)
Took 0.04 seconds to build
Top 10 directories by time spent:
40 ms: .
1 ms: example
Top 10 directories by storage:
61.83 MiB: .
0.00 MiB: example
Top 10 directories by file count:
23: .
1: example
Top 10 file extensions by storage:
57.10 MiB:
4.71 MiB: .exe
0.01 MiB: .pprof
0.01 MiB: .md
0.01 MiB: .go
0.00 MiB: .sum
0.00 MiB: .mod
0.00 MiB: .sh
0.00 MiB: .gitignore
0.00 MiB: .dockerignore

Use .dockerignore

So, what is .dockerignore ?

Before the docker CLI sends the context to the docker daemon, it looks for a file named .dockerignore in the root directory of the context. If this file exists, the CLI modifies the context to exclude files and directories that match patterns in it. This helps to avoid unnecessarily sending large or sensitive files and directories to the daemon and potentially adding them to images using ADD or COPY.

Sometimes I have accidentally included some binaries or something which I did not intend to include, and this will make the build time longer. After you got some insights from the docker-show-context, you can start ignoring those files when you build you Dockerfiles.

Here is an example .dockerignore file:

# comment
*/temp*
*/*/temp*
temp?
big_files_that_is_not_really_necessary

Multi-stage build

Multi-stage builds are a new feature requiring Docker 17.05 or higher on the daemon and client. This enables you to create multiple containers that are readable and easier to maintain. You can read the multi-stage builds feature on the docker site here.

Here is the example of multi-stage Dockerfile:

FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]

And you can build the docker image by targeting the stage name like this:

$ docker build --target builder -t alexellis2/href-counter:latest .

Lint your Dockerfile

If you have read the Best practices for writing Dockerfiles, you know about hadolint, a linter for Dockerfiles.

You can run hadolint locally:

hadolint <Dockerfile>
hadolint --ignore DL3003 --ignore DL3006 <Dockerfile> # exclude specific rules
hadolint --trusted-registry my-company.com:500 <Dockerfile> # Warn when using untrusted FROM images

Or you can use the Docker image from hadolint, just pipe your Dockerfile to the docker run command

docker run --rm -i hadolint/hadolint < Dockerfile

Author

Raditya Surya is a Software Engineer on the Infrastructure Team at Tiqets.

--

--