Building and using Golang applications with Docker

Golang is a great language able to create statically linked binaries for different platforms such as Linux (ELF binaries) or Mac OS (Mach-O binaries). These binaries are often very small in size, and the language is getting increasingly popular in the microservices world because of their portability and the speed of deployment it enables: deploying a self-sufficient 10 MB Docker image on dozens of servers is just more convenient and fast than a 1.5 GB image full of libs. Golang and containers are two technologies that go perfectly well together, and shipping or managing infrastructures using Go programs is a breeze.

Getting ready

To step through this recipe, you will need the following:

  • A working Docker installation
  • A Golang application source code

How to do it…

Let's say our application code is checked in src/hello. We'd like to begin by at least compiling the program, either for the Linux platform or for the Mac operating system.

Using the golang Docker image to cross-compile a Go program

We can compile our program sharing the code folder, and setting the work directory to it:

$ docker run --rm -v "${PWD}/src/hello":/usr/src/hello -w /usr/src/hello golang:1.7 go build -v

This way, even on a Mac OS system, we can generate a proper ELF binary:

$ file src/hello/hello
src/hello/hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

That said, if we explicitly want a Mac binary, we can pass the standard Go environment variables GOOS and GOARCH so even a Linux machine can build a Mac binary:

$ docker run --rm -v "${PWD}/src/hello":/usr/src/hello -w 
/usr/src/hello -e GOOS=darwin -e GOARCH=amd64 golang:1.7 go build -v

Confirm we have a Mach-O executable and not an ELF binary:

$ file src/hello/hello
src/hello/hello: Mach-O 64-bit executable x86_64

Using the golang Docker image to build and ship a Go program

Now if we want to build our program right from a Dockerfile and generate a Docker image out of it, that would translate like the following:

FROM golang:1.7
COPY src/hello /go/src/hello
RUN go install hello
ENTRYPOINT ["/go/bin/hello"]

Just build that image and execute it:

$ docker build -t hello .
$ docker run -it --rm hello

Using the scratch Docker image

Now, it's a bit of a waste of space to have a 675 MB+ image for the very often small Golang application that often is only a few MB, and it takes time to deploy on servers. Here comes the scratch image: it just doesn't exist. We start from nothing, copy the binary, and execute it. Our build process (Makefile, build process, and CI) builds the app with the golang image, but does not ship the compiled application with it, saving usually 95–99% of the space, depending on the size of our binary:

FROM scratch
COPY src/hello/hello /hello
ENTRYPOINT ["/hello"]

This generates the smallest image imaginable. Think only a few megabytes.

Using the Alpine Linux alternative for a Go program

The main problem with the scratch image solution is the impossibility to debug it easily from inside the container, and the impossibility to rely on external libraries or dependencies such as SSL and certificates. Alpine Linux is this small image (~5 MB) that can greatly help us if we'd like to access a shell (/bin/sh is available) and a package manager to debug our application. This is how we'd do it:

FROM alpine:latest
RUN apk --update --no-cache add ca-certificates openssl && 
    rm -rf /var/cache/apk/*
COPY src/hello/hello /bin/hello
ENTRYPOINT ["/bin/hello"]

Such an image usually is only a handful of megabytes more than the application binary, but helps greatly for debugging.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.138.202