Building Docker Image Faster
During building services, we often need to build docker images. We do it multiple times a day. It can be a time-consuming task. Locally we only notice it a little, but in CI/CD pipelines, it can be a problem.
In this post, I will show you how to speed up the process. I will show you how to use a cache, layer your Dockerfile, and use multi-stage builds, to make your builds faster.
For this, I’ll use a simple Go application. You can use any other application you have. It does not matter which stack, language, or framework you use. The principles are the same.
Everything I do is executed on my local machine. I do not use any CI/CD tools. I use Docker Desktop for Mac.
Cleaning Up
Just to be sure that we are starting from a clean state, we can remove all unused images, containers, volumes, and networks:
$ docker system prune -a
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all images without at least one container associated to them
- all build cache
Are you sure you want to continue? [y/N] y
...gone with the wind...
Starting Point
I started with a simple Dockerfile(Dockerfile_1):
FROM golang:buster
WORKDIR /app
COPY app /app/
ENTRYPOINT [ "/app/app" ]
To be able to use this Dockerfile, I have to build an application first:
$ go build -o app
And then build the image:
$ docker build . -f Dockerfile_1
Sending build context to Docker daemon 22.84MB
Step 1/4 : FROM golang:buster
---> f8c6c6bf3e26
Step 2/4 : WORKDIR /app
---> Running in 62eb8791ace1
Removing intermediate container 62eb8791ace1
---> d586151d2813
Step 3/4 : COPY app /app/
---> 25b4f091cba7
Step 4/4 : ENTRYPOINT [ "/app/app" ]
---> Running in 7853090f8c3b
Removing intermediate container 7853090f8c3b
---> 0e3d3835a61b
Successfully built 0e3d3835a61b
I want to start it, but I need to know the image name. I can use docker images
to find it:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 0e3d3835a61b 48 seconds ago 739MB
excalidraw/excalidraw latest d6392f9c5191 2 days ago 34.8MB
golang buster f8c6c6bf3e26 4 days ago 720MB
moby/buildkit buildx-stable-1 4dc9f4d5bf89 2 weeks ago 168MB
slimdotai/dd-ext 0.8.2 56f11b815b6c 7 months ago 153MB
I can see that the image name is <none>. I can use it to start the container:
$ docker run 0e3d3835a61b
exec /app/app: exec format error
What happens? Go back to the Dockerfile_1 and look at it. There are several issues with it:
I’m building the application for OSX, but I want to run it in Linux.
I do not specify which Go version I’m using. Locally I can have Go 1.16, but the image has the latest Go version(atm it is 1.20).
my application uses port 9999, but I do not expose it.
my image does not have a name and version.
If you are interested in how I do it, check it out on my blog.