Minimizing Docker Images with Multistage

When you build your own  docker image from Dockerfile,  each instruction in Dockerfile creates a new layer to your base image with its all dependencies so that even your very tiny application image size may be in 1GiB  size and it is not desirable in the production environment to be such a big size due to the below reasons.

  • Large Images takes longer to Download
  • Large Images takes up more disk space
  • Large Images contains unnecessary components

 

How to Reduce Image Size ?

Answer is multi-stage build. Multi-Stage builds enables you to create smaller container images with better caching and smaller security footprint. In this post, It will be shown you how to minimize your docker image step by step. For this experiment, It is written very simple Go web application.

Let’s create a web application named main.go

package main

import (
    "fmt"
    "log"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
    log.Printf("connection from:%s",r.RemoteAddr)
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Creating a Dockerfile.

FROM  golang:alpine AS builder
WORKDIR /webapps/app
ADD . /webapps/app
RUN go build -o main .
EXPOSE 8080
CMD ["/webapps/app/main"]

Building docker image

tesla@otuken:~/DockerTraining/SimpleHello$ sudo docker build -t gokay/goweb:1 .

 

Figure-1 Image Size 317MB

As you see in the Figure-1 image size that simple application is 317MB.

Multi-Stage Build:

In this section, it will be shown you how to reduce the docker image size with Multi-Stage build. Only thing we need to do is adding some lines in our Dockerfile.

FROM  golang:alpine AS builder
WORKDIR /webapps/app
ADD . /webapps/app
RUN go build -o main .


FROM alpine
WORKDIR /app
ADD . /app
COPY --from=builder /webapps/app/main /app
EXPOSE 8080
CMD ["/app/main"]

 

tesla@otuken:~/DockerTraining/SimpleHello$ sudo docker build -t gokay/goweb:1 .

After building our new image with new Dockerfile image size considerably reduced.

Figure-2 Image size 11MB

As you see docker image size is now 11MB.

If this size enough for you you can skip reading rest of the post. We can even reduce the image size a bit more as we write our application in Go. We can disable the cross-compilation as below.

 

FROM  golang:alpine AS builder
WORKDIR /webapps/app
ADD . /webapps/app
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o main .

FROM alpine
WORKDIR /app
ADD . /app
COPY --from=builder /webapps/app/main /app
EXPOSE 8080
CMD ["/app/main"]

 

Figure-3 Image size 10.9 MB

Reducing More ?

You can use scratch image which is the minimalist image. But I would recommend to use alphine as it the security-oriented Linux distribution.

FROM  golang:alpine AS builder
WORKDIR /webapps/app
ADD . /webapps/app
#RUN go build -o main .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o main .
#EXPOSE 8080
#CMD ["/webapps/app/main"]


FROM scratch
WORKDIR /app
ADD . /app
COPY --from=builder /webapps/app/main /app
EXPOSE 8080
CMD ["/app/main"]

 

tesla@otuken:~/DockerTraining/SimpleHello$ sudo docker build -t gokay/goweb:1 .