Using a Dockerfile to embed revision information in your Go application
When I’m debugging an issue with an application, one of the first questions I ask is what version of the application I’m looking at. If different versions of my app are deployed in multiple environments, it’s always worth checking to make sure the environment I’m looking at is running the code I think it is.
There are a few ways of accomplishing this. Sometimes you can SSH into your server and run
git log -1, but if your application is running in a container that’s not always available. Another option is manually set the version in a file that becomes part of your build, but this is easy to overlook, surprisingly hard to automate, and often doesn’t give you the granularity you need.
Years ago, I wrote scm-status to make it easier to know what version my app was running. scm-status simply grabs any information from source control (either Git or Mercurial) that it can find in your current working directory and outputs it either to STDOUT or a file. As my build processes evolved, I mixed in go-binary, which pipes the output from scm-status and creates a
.go file suitable for including in the compiled application. I tried to automate this as well as I could by writing Makefile targets that would generate the relevant
.go files before running a standard
Recently, I’ve started working with Dockerized applications, and for a while have tried to figure out how to get version information in a Docker image without too much hassle. It turns out that while the process to get there is a little convoluted, I’m pretty happy with the solution I came up with.
First, find a place in your application where your revision information will live. For me, it was in the same package (main) as my binary. Once you know where that file will be, create a file called
revision.go and paste the following:
Next, create a Dockerfile. Here’s an example for an app I’m working on now. Your file may vary slightly, but the gist should be similar.
Finally, wherever we want to see the revision, simply load it, unmarshal it however you prefer, and do whatever you want with it. Here’s an example:
Overall I’m pretty happy with this solution, and I think it’s an improvement over the old Makefile-driven method. By putting this process in a Dockerfile, we don’t have to worry about dependencies or weird artifacts, and we only require the user to run a normal
docker build rather than knowing to run a Makefile target.
It’s worth pointing out also that there’s no reason this has to be a Go application. For a non-Go application, we’d just output the results of
scm-status to a file called
REVISION.json, and either shim that data into a language-specific file or copy that file all the way through to our final container.
By the way, I do most of my writing at Section 411.