Golang Application

How to create the smallest possible Docker image for your Golang application

1027 VIEWS

·

This blog post will guide you to create the ultimate smallest possible Docker image for your Golang application using the Build flow tool Habitus!

Originally posted on Cloud66.com

In the container ecosystem, there’s a lot of chatter about security and best practices to build the ultimate container image. The main goal is to create an image which is slim, secure, speedy, stable and set.

I didn’t have time to create a slim image, so I created a fat one instead.

Shortcuts are evil and we need to aim for slim images instead of fat ones which cause problems (security, performance, maintaining) in the long run.

Let get started!

Scratch for the win!

The first step is to understand how to create a Docker image with no base like ubuntu or alpine for example. We want the bare minimal. The goal is to isolate our process with NO dependencies or stuff we don’t need. The scratch base image is your answer.

You can use Docker’s reserved, minimal image, scratch, as a starting point for building containers. Using the scratch image signals to the build process that you want the next command in the Dockerfile to be the first filesystem layer in your image.

Each Docker image references a list of read-only layers that represent filesystem differences. Layers are stacked on top of each other to form a base for a container’s root filesystem.

Behold!

The smallest possible Docker image for an executable. The executable should be a static build.

A static build is a compiled version of a program which has been statically linked against libraries. In computer science, linking means taking one or more objects generated by compilers and assembling them into a single executable program.

Steps to take:

  1. Download the smallest hello world app in the world.
  2. Create a Dockerfile
  3. Build the image $ docker build . -t helloworld:smallest
  4. Run the container $ docker run helloworld:smallest
  5. Check the size of each layer $ docker history helloworld:smallest
  6. A docker image of 142 Bytes. Eat that!

The two-stage rocket build.

The 1 million dollar question is? How to build our Golang application, get the executable and put it inside a container in one command?

Unfortunately, you can’t do this in an automated fashion using the standard Docker tooling. Luckily we create a project called Habitus to automate this process.

Basically, we need two stages. Build the artefact using $ go build and copy the executable into our final image. Let create both stages using Dockerfiles and glue them together with the Habitus rocket!
Let start with a simple HTTP service written in Golang:

main.go

We need to build the executable first before we can run it as an isolated process using containers. Enter stage #1!

Stage #1

The responsibility of this stage is to build an image which can build your Golang executable and extract the artefact.

Dockerfile.builder

To build it manually run this command to build it.

$ docker build -f Dockerfile.builde -t builder:latest .

Copy the compiled artifact to your local disk

$ docker container cp [id_of_container]:/go/src/helloworld/helloworld helloworld

Stage #2

The responsibility of this stage is to copy the artefact into the smallest possible image.

Dockerfile.production

To build it manually run this command to build it.

$ docker build -f Dockerfile.production -t helloworld:latest .

Building your rocket to create the smallest possible Docker image

With Habitus you need a build.yml to tell which steps are necessary for the Docker build flow. Habitus give you the power to handle complex build flow without getting into bash hell mess.

build.yml

Build everything in one command! Easy as it gets.

$ habitus

Now we got the smallest possible image with only one layer and it only contains our executable. You can even compress more to use upx to compress the executable even more, up to 40%.

To show all the layers, run the $ docker history command.

$ docker history helloworld

Summary

Creating the smallest possible Docker image for your Golang application is easy with Habitus. Integrate Habitus with your CI/CD pipeline gives you control to create isolated processes with the minimal attack surface.

Good to know we support Habitus in our Buildgrid solution.

Happy welding your containers

Do you think you can beat this Sweet post?

If so, you may have what it takes to become a Sweetcode contributor... Learn More.

Daniël van Gils is a developer advocate at Cloud 66. He helps other developers craft web apps and container based u service architectures.


Discussion

Click on a tab to select how you'd like to leave your comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Menu