Build Fast, Deploy Faster : Creating lighter docker images

Vishwas Srivastava
The Startup

--

This posts tries to address the challenge of keeping the image size low in Docker. With increase in Docker adoption , most of the organisation are moving towards docker based images for their applications. With this fast paced adoption, it is important to keep a check on the size of docker images as it impacts the deployment time on production.

Understanding Normal Docker Builds and its Challenges:

Let’s take a look into Dockerfile of our Golang based app.

FROM golang:latest
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o main .
EXPOSE 8080
CMD ["./main"]

If we build the image using the above Dockerfile, the size of image will be :

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
go-app single-stage cf90e2811d8e About a minute ago 826MB

Understanding the problem with this docker build:

If we look closely, with this approach we have packaged following things with our current build :

  1. Compiler
  2. Linters
  3. Golang Runtime
  4. Debuggers
  5. Configuration
  6. Application

But actually, we should be packaging only the essential things :

  1. Runtime
  2. Configuration
  3. Application

Also the base image golang:latest is it itself 809 MB. That’s huge.

To solve this, we can use multi stage builds introduced with Docker version 17.05 release.

Multi-Stage Builds:

With multi-stage builds we can divide our Dockerfile in two stages:

  1. Builder : In this stage, we will copy our whole application and other necessary configurations(if any) that will be needed to build a golang executable.
  2. Executable : In this, we will just copy the output from our previous stage and just run the application. This last stage will be the part of actual image. To run our app, we can use scratch as base and the final Dockerfile will be something like :
############################
# STEP 1 build executable binary
############################
FROM golang:latest as builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o main .
############################
# STEP 2 build a small image
############################
FROM scratch
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]

If we compare the size of both the docker images, the one which we created earlier and the multi stage build :

REPOSITORY  TAG       IMAGE ID      CREATED        SIZE
go-app single-stage cf90e2811d8e 13 minute ago 826MB
go-app multi-stage 2592d4c8f313 10 minute ago 8MB

It is clearly visible that with this approach we have reduced the size from 826MB to 8MB.

Summary:

This approach comes useful when we have number of application deployed in a containerized environment. Lesser size mean lesser download time resulting in faster deployments.

--

--

Vishwas Srivastava
The Startup

Software Engineer | Fullstack Developer | Docker | Kubernetes | Machine Learning | AI | Augmented Reality