Docker: doing this will create huge images, how to avoid it.

When we create images we tend to think linear:

  1. Install any compilers and compile dependencies i need
  2. Copy the source code to the image
  3. Compile the code
  4. Do various other things and finally the CMD statement

While the procedure is not wrong it can drive to large images which have a lot of content they don't need for the compiled software to run, for example the compiler and the compile dependencies are not needed to run the container and are used only once, to build the image.

Lets see both the “bad” and the “good” way to create an image to make things clear

Lets prepare some directories for this test.

$ cd 
$ mkdir -p ~/docker_test/bad_way
$ cd ./docker_test/bad_way

Now we will create a small program in Go, save it as hello.go

package main
import "fmt"
func main() {
fmt.Println("hello world")

Now the Dockerfile

FROM golang
WORKDIR /hello
COPY hello.go .
RUN GOOS=linux go build -a -installsuffix cgo -o hello .
CMD ["./hello"]

And finally build and the image

$ docker build -t bad .

Lets see how big it is!

$ docker images ls
Image for post
Image for post

As we can see the image is quit big for a such simple executable we created! is 855MB, and this is not because of the executable but because of the golang image we used that has go installed along with other things that we don't really need.

In the current directory enter:

$ mkdir ../good_way
cp ./Dockerfile ../good_way/
cp ./hello.go ../good_way/

Edit Dockerfile to match this

FROM golang AS compiler
WORKDIR /hello
COPY hello.go .
RUN GOOS=linux go build -a -installsuffix cgo -o hello .
FROM alpine
COPY --from=compiler /hello/hello .
CMD ["./hello"]

Save file and enter:

$ docker build -t good .

This will build an image named good. Lets discuss what we did, this Dockerfile has two FROM statements, the first one creates a temporary image named “compiler” which will used only to compile hello.go

The second FROM statement will create our actual image that will use the alpine image as a base, which is a really tiny image, the second COPY statement copy the hello executable from the compiler image into the the final image that we build, using this method we create a very small image that has only the executable that we need without the go compiler.

Note: in this case the executable does not need any other external libraries or dependencies, there might be other cases that in your final image you will need to copy/install additional files but still the image will be smaller.

Lets see how big is the “good” image

$ docker image ls
Image for post
Image for post

We can see that the good image is only 7.6MB much smaller than the bad one which is 855MB!

I hope you found this article interesting :)

Written by

DevOps engineer, loves Linux, Python, cats and Amiga computers

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store