Multi-Stage Docker build for Go

Alex Tan
3 min readSep 4, 2017

TL;DR

Dockerize your golang app easily with the new multi-stage builds from Docker 17.05. Reduce deployment steps and produce smaller, optimized builds.

Audience:

  • You want to know how to Dockerize your Golang app
  • You want your Docker image to be as small as possible
  • You want to know how multi-stage docker build works and the pros

References:

  • The example can be found in the Github repo here

Highlights

  • You will first build a docker image using only the Docker golang base image, and observe the outcome. For simplicity, our program will just output ”hello, go”
  • Then, you will learn how to build a more optimized docker image, but requires separate commands
  • Finally, we will demonstrate how multi-stage build can simplify our process

Guide

Setup

You need to have golang and a minimum version of docker 17.05 installed in order to run this demo. You can check the version of your dependencies as shown below: Validating go version: https://gist.github.com/5e74c2ee01840a350ef720e7db5346d5

Validating Docker version: https://gist.github.com/36417cafdeda115742b0fdde99361c0e

The golang program

The main.go contains our application logic. It does nothing but print Hello, go!. https://gist.github.com/f3f011ab9f1567f766c394d5812c3ac3

Now that we have our application, let’s dockerize it!

Method 1: Using the Golang image

The steps in Dockerfile.00 is as follow:

  1. We select the golang:1.9 image
  2. We create a workdir called hello-world
  3. We copy the file into the following directory
  4. We get all the dependencies required by our application
  5. We compile our application to produce a static binary called app
  6. We run our binary https://gist.github.com/e05ad186f077213da49b5253f00a32eb

Let’s build an image called alextanhongpin/hello-world-00 out of it. You can use your Github username instead when building the image. https://gist.github.com/5257c56e80e3587806c36a7afb41c90b

We will run our docker image to validate that it is working: https://gist.github.com/04f18d7ebd5f32d5a9f3c5d61d8ccf8a

Let’s take a look at the image size that is produced: https://gist.github.com/aeae060a1d869dc74aa71e2374fdeb86

We have a 729MB image for a simple Hello, go!! What can we do to minimize it? That brings us to next step…

Method 2: Build locally

The reduce the size, we can try to compile our main.go locally and copy the executable to an alpine image — the size should be smaller since it contains only our executable, but without the go runtime. Let’s compile our main.go: https://gist.github.com/b0a0887082b43d104b42164c6aca378d

Dockerfile.01 contains the step to build our second image: https://gist.github.com/9bbd19a6dfb4f7bdf5e97d157142e38d

All it does is copy our compiled binary to an alpine image. We will build the image with the following command: https://gist.github.com/93ddcb0bfec31964a52e347f778e4d9b

Let’s validate it again as we did before and view the change in the size: https://gist.github.com/7bfdd2df17150aac0f79af12d8f28ced

Let’s take a look at the image size: https://gist.github.com/57b7a974f5650e4ae796b43d77aba22d

We can see that the size has reduced dramatically from 729MB to 6.55MB. This however, involves two different step — compiling the binary locally and create a docker image. The next section will demonstrate how you can reduce this to a single step.

Method 3: Using multi-stage build

Multi-stage buil is a new feature in Docker 17.05 and allows you to optimize your Dockerfiles. With it, we can reduce our build into a single step. This is how our Dockerfile will look like: https://gist.github.com/a730a9bc81d798de806b827191a14379

Let’s build and observe the magic: https://gist.github.com/7327de52ef39e5eeff4df0b73a0d4a36

https://gist.github.com/2a1285794fd911404692f187c3cb7156

You can now build your golang image in a single step. The output is shown below: https://gist.github.com/77dde3e92d1ad6a39ce2092881fa8e93

Originally published at gist.github.com.

--

--