Developer Experience: Demand to support engineering teams has risen, and there is a shift from traditional DevOps to workflow improvements.

The future of AI-driven development. Join the discussion around insights on low code's and AI's roles in building mission-critical apps.

Has GenAI transformed how you work? Or has the hype cycle played out? Tell us your thoughts on its impact inn your organization.

DevEx Roundtable: Join the discussion about the evolving needs of the developer from streamlined workflows to infrastructures investments.

How to Create the Smallest Docker Image for Your Golang App

This post will guide you through creating the ultimate, smallest possible Docker image for your Golang application using the Build flow tool Habitus!

By  · Tutorial
Comment (3)
Save
12.8K Views

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 that is slim, secure, speedy, stable, and set.

alt 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, maintenance) in the long run.

let's 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 minimum. 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 that 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:

  • download the smallest hello world app in the world.
  • create a dockerfile:
  • build the image $ docker build . -t helloworld:smallest
  • run the container $ docker run helloworld:smallest
  • check the size of each layer $ docker history helloworld:smallest

a docker image of 142 bytes. eat that!

the two-stage rocket build

the 1 million dollar question is, "how do we 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 created a project called habitus to automate this process.

alt

basically, we need two stages. build the artefact using $ go build and copy the executable into our final image. let's create both stages using dockerfiles and glue them together with the habitus rocket!

let's 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 that can build your golang executable and extract the artifact.

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 artifact into the smallest possible image.

dockerfile.production

to build it manually, run this command:
$ 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 gives you the power to handle a complex build flow without getting into bash hell.

build.yml

build everything in one command! easy as it gets.

$ habitus


$ docker images | grep helloworld

now we've got the smallest possible image with only one layer, and it only contains our executable. you can even compress it more using upx , which could save up to 40% more space.

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. integrating habitus with your ci/cd pipeline gives you the control to create isolated processes with minimal attack surface.

good to know we support habitus in our buildgrid solution.

happy welding your containers.

Published at DZone with permission of Daniel van Gils. See the original article here.

Opinions expressed by DZone contributors are their own.


Comments