Generally GO executables are advertised as statically linked.
And they are, mostly, except for those times where they aren’t.
This is about one of those times, and what I discovered in the process.
For a bit of background, I was attempting to make a docker container for the discovery.etcd.io server. Rather than be dependant on external network connections, I’d modified so it could be used locally. Believing that executables produced by go are always statically compiled I chose the busybox container as the base in order to make as small a container as possible. Container built, I discovered to my chagrin that I got a “file not found”. Upon further investigation (read google), I discovered that the executable was not statically linked but was, indeed dynamically linked.
It turns out that certain go libraries such as
net/http do not have a pure go implementation at this point. The cgo tool allows for go programs to link c-code into go programs. Generally,
--ldflags '-extldflags "-static"' can be added to the compile command and it will output a proper static executable.
It couldn’t be so easy for me. It turns out that I needed to statically compile go and the language libraries as well. In order to do so,
go needs to be compiled without
- Verify that all the dependencies for compiling have been installed.
- Download the source for the Go language by cloning the go repository:
hg clone -u release https://code.google.com/p/go
- Disable cgo:
- Change the working directory to where go was extracted. To compile, then invoke
./all.bash. Windows® folk would use
./all.bat. On my laptop (quad core, 12gb ram) it takes ~11 min for this process.
- Configure PATH to pick up the static go.
- Clone discovery.etcd.io from Github:
git clone http://github.com/nimblestratus/discovery.etcd.io
- Build the discovery service with:
Shell123456#!/bin/sh -eexport GO_EXTLINK_ENABLED=0export CGO_ENABLED=0go run -x --ldflags '-extldflags "-static"' third_party.go install -x --ldflags '-extldflags "-static"' github.com/coreos/discovery.etcd.io
Once I had a statically built discovery service container; I went ahead and built a dynamic one — the only difference is that the static version uses busybox as the base and the dynamic uses ubuntu.
$ docker images |grep discov
discovery-dyn latest e4a63a35ea63 19 seconds ago 291.4 MB
discovery latest 5ec25542651f 19 hours ago 16.35 MB
That is a rather large difference!
In either case, using the discovery service as a docker container requires using the host’s network stack (
--net=host because the code connects to etcd on the loopback address.
$ docker run -d -e DISCOVERY_ADDR=":80" \
--net=host -e DISCOVERY_URL="http://discover.nimblestrat.us" \
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9c02eb25d77f discovery-dyn:latest "/bin/sh -c /usr/loc 4 seconds ago Up 4 seconds discovery
$ curl http://localhost/new
A third option is to run the discovery service in CoreOS as a systemd service. In the discovery service’s github repo, there are systemd service and socket files for doing so.