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 cgo support
.
- 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:
export CGO_ENABLED=0
- 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:
123456#!/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.
1 2 3 |
$ 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.
1 2 3 4 5 6 7 8 9 10 11 12 |
$ docker run -d -e DISCOVERY_ADDR=":80" \ --net=host -e DISCOVERY_URL="http://discover.nimblestrat.us" \ --name=discovery discovery-dyn 9c02eb25d77f45720727582be8e1210bdf7b9460440b20bd83ac06721be3c961 $ 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 http://discover.nimblestrat.us/6826ebc801ffa10d8ecc445ccb1289af |
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.
1 comment
Dieter Reuter (@Quintus23M)
May 26, 2015 at 4:10 pm (UTC -5) Link to this comment
Matt, I think since GO 1.4 your infos are a little bit outdated. They’ve changed some settings to get real static binaries…