Auto Rebuilding Go Programs — With Docker, Glide & Fresh
Developing Go applications can often be a slog, especially if you’re 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.
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?
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
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
# 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
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!