Small Docker Images For Go Apps

Brian DeHamer

February 5, 2015

Share +

Over the past year we've written a fewarticles about optimizing your Docker images -- usually with an emphasis on creating the smallest possible image. Unfortunately, when using an interpreted language like Ruby (which has been used for many of the CenturyLink projects) there is only so much fat you can trim from your images. To run a Ruby application in a container you still need the Ruby interpreter and a whole host of OS packages and Ruby Gems installed. Even with our best effort to optimize the images, our Docker-packaged Ruby applications often weigh-in at 400+ MBs.

Go

We've now started to use Go for some of our latest projects which, among other things, gives us the ability to package our apps into really compact images. With a Go application, you can compile your code into a self-contained, statically-linked binary that has absolutely no external dependencies. Your application code along with the Go runtime and any imported packages are all compiled together into one binary.

The primary benefit of a statically-linked binary is that it allows you to deploy your application by simply copying the binary. When deploying with Docker this means you can build an image for your application that contains nothing but the app itself. You can go from using ubuntu (130 MB) or debian (85 MB) as your base image to something like busybox (2 MB) or even scratch (0 bytes).

If you take your 4 MB, statically-linked Go binary and package it in a Docker container using scratch as your base you'll end-up with a 4 MB image -- that's 1/100th the size of our packaged Ruby app!

Assuming that we're currently in the directory containing our hello.go source file and Dockerfile, the first -v flag will mount our project directory into the golang-builder container as /src. The script inside golang-builder is hard-coded to look for your Go code at /src.

The second -v flag mounts the Docker API socket into the container. Since the golang-builder code needs to interact with the Docker API in order to build the final image, it needs access to /var/run/docker.sock.

The golang-builder will set-up the appropriate GOPATH for your project, resolve your dependencies, compile your code and then issue a docker build against your project's Dockerfile.