< index

Tiny GoLang Docker Containers via CircleCI

· 3 minute read

Docker has emerged as the dominant Linux container format, exposing the benefits of LXC in a way that’s easy to build, use and share. But docker containers can be HUGE! For example a simple golang hello world service packaged with the official golang:onbuild base image:

$ docker pull quay.io/robtuley/go-docker-example
$ docker images quay.io/robtuley/go-docker-example
REPOSITORY                          TAG     IMAGE ID      CREATED         VIRTUAL SIZE
quay.io/robtuley/go-docker-example  latest  69cefc2776a2  22 minutes ago  523.1 MB

523.1 MB?!? A docker history quay.io/robtuley/go-docker-example gives the detail. While I accept some of the layers might be cached on a system with similar base images already in use, that’s a crappy thing to rely on for acceptable deploy size.

For golang applications like this the container can be reduced ~100x to 5.8MB by bundling a statically linked binary into a scratch docker container, described here using CircleCI to perform the build and push pipeline (the code).

A golang application can be compiled into a statically linked binary via (note this changed in 1.4):

CGO_ENABLED=0 go build -a -installsuffix nocgo .

If you are compiling a statically linked binary, that’s the only artifact you need in your docker container so you can use the empty scratch container as a base image:

FROM scratch
COPY go-docker-circleci /staticbinary
ENTRYPOINT ["/staticbinary"]
EXPOSE 8080

If you need some orchestration to build your container with the pre-compile step my normal go-to tool is CircleCI. This circle.yml will do the trick:

machine:
  services:
    - docker

dependencies:
  pre:
    - CGO_ENABLED=0 go build -a -installsuffix nocgo .
  override:
    - docker build -t quay.io/robtuley/go-docker-circleci .
    - docker run -p 8080:8080 quay.io/robtuley/go-docker-circleci:
        background: true
    - sleep 10

test:
  override:
    - "curl http://localhost:8080/"
    - "[[ $(curl http://localhost:8080/) = 'Hello World!' ]]"

deployment:
  hub: 
    branch: master
    commands:
      - docker login -e $DOCKER_EMAIL -u $DOCKER_USERNAME -p $DOCKER_PWD $DOCKER_URL
      - docker push quay.io/robtuley/go-docker-circleci

In the machine section the need for docker is declared. In dependencies the golang app is compiled and docker container built and started. In test, the docker image is validated via curl, before deployment pushes the image into a Quay.io repository.

$ docker pull quay.io/robtuley/go-docker-circleci
$ docker images quay.io/robtuley/go-docker-circleci
REPOSITORY                          TAG     IMAGE ID      CREATED        VIRTUAL SIZE
quay.io/robtuley/go-docker-circleci latest  f27ce77f2bcf  3 minutes ago  5.806 MB

That’s it!

References

Hello World on CircleCI code (github)

Circle CI Build

Docker Repository on Quay.io

Related...