http://blog.scottlogic.com/cprice/assets/hot-reloading.gif

Auto Rebuilding Go Programs — With Docker, Glide & Fresh

Craig Childs

--

Introduction

Developing Go applications can often be a slog, especially if the Go application is a service in a larger ecosystem, that you need to run locally during development.

Every time we make a change to our code we’d have to kill our Docker container, re-build our Go binary and re-run the Docker container. This is inefficient and could easily be automated. Let’s get cracking shall we?

I’m making the assumption you know the basics of how Go programs work and how to develop them. I’m also assuming you know what Docker & Containers are.

Setting Up Fresh & Glide

The problem we’re facing here is that we are left to re-build the binary when we alter the code. This would be tedious and waste so much time.

We need something that can just re-build the binary on the fly when files are changed. Luckily someone has already thought about this, say hello to: Fresh.

Fresh is a Go program which you can include and run outside and inside the Docker container, it’ll watch for files, re-build your binary and execute it.

By default it’ll look for a file called runner.conf in this instance we’re using the default one they provide.

Run go get github.com/pilu/fresh and run fresh to get started.

It works great and certainly does what we need it to.

I love Fresh, my only gripe with it, is that it doesn’t seem to pick up new files; though simply saving or “touching” an existing file will trigger the re-build.

Now in comes Glide, go get on steroids. It’s a package manager comparable to npm or Composer. I highly recommend this for Go developers, it makes life easier!

In essence it’s a beautiful wrapper around “go get”, with a configuration file, version locking and caching. Why wouldn’t you use it?

Due to Fresh’s limitations, it can only watch so many files at once, so we’ll have to ignore our Glide vendor folder. Booooooo.

A simple workaround for this would be to run glide update && touch main.go this way we’ll trigger Fresh’s watchers to re-build once we’ve updated our Glide dependencies, cool right?

Just add vendor to the “ignored key” in your Fresh config, like so (This will keep Fresh from complaining about too many files to watch):

ignored: assets, tmp, vendor

This setup so far would work brilliantly if you’re working outside of the Docker ecosystem, so next we’ll look at integrating this into a simple Dockerfile solution that you can run alongside other Docker containers.

Setting Up Docker

If you haven’t already installed & configured Docker, then please do.

Instead of running the commands directly in the terminal; we’ll build a Docker image based off the official golang:alpine image. Leaving the only commands we’ll need to run manually being glide update && touch main.go and the typical one off “Docker run” when we add/remove/update Glide dependencies, with no need to rebuild binaries or containers ourselves.

Create a Dockerfile with the following contents:

# create image from the official Go image
FROM golang:alpine
RUN apk add --update tzdata \
bash wget curl git;
# Create binary directory, install glide and fresh
RUN mkdir -p $$GOPATH/bin && \
curl https://glide.sh/get | sh && \
go get github.com/pilu/fresh
# define work directory
ADD . /go/src/project_folder
WORKDIR /go/src/project_folder
# serve the app
CMD glide update && fresh -c runner.conf main.go

Replace “project_folder” with the folder name of your project.

And in order for Glide to function properly outside of the Docker image, your project must be in your $GOPATH like it is in the Dockerfile, so $GOPATH/src/project_folder

Run the following command in your project root (wherever your Dockerfile & main.go files are) to boot up the container:

docker run -it --volume=$(PWD):/go/src/project_folder --name=my_project_name image_name

And if we’re running it alongside other containers in a Docker Network we’ll need to add the network flag to this command, so if you had a network called my_app it’ll be:

docker run -it --volume=$(PWD):/go/src/project_folder --name=my_project_name --network=my_app image_name

Note: we mount a volume from our working directory to a folder within our containers $GOPATH, this will allow our watcher running inside the container to pick up changes in our local filesystem.

This should be everything you need to get started writing awesome auto rebuilt Go programs with managed dependencies running on a Docker container. *High Five*

Note: I do not recommend you use this setup for production applications!

Good luck, have fun!

--

--

Craig Childs

Multi-pronged web developer with a passion for cutting edge tooling! https://craigchilds.dev