Cross Compiled Go with Alpine Linux make a tiny Docker image

Update: Docker has since released Multi-stage builds to address the problem described below. It’s a far better solution, and you can consider the below article deprecated. Check it out!

Golang’s ability to create a self-contained executable makes deployment a breeze. You just copy the single file. No need to worry about versioned dependencies and your dependencies’ dependencies.

Golang’s compiler goes ever further by supporting cross compilation. Know your target architecture? Compile directly for it from your dev box. Linux example below:

brew install go --with-cc-common # Installs go with cross compilation support
GOOS=linux GOARCH=amd64 go build -o someexecutable

Couple this with a minimalist docker image, such as alpine, and you have yourself a tiny 40MB image.

Dockerfile

FROM alpine:3.2
ADD someexecutable /go/bin/someexecutable
ENTRYPOINT /go/bin/someexecutable

Sometimes you’ll need an extra thing or two, like CA certificates to connect to HTTPS with SSL:

Dockerfile

FROM alpine:3.2
RUN apk add --update ca-certificates # Certificates for SSL
ADD tmp/someexecutable /go/bin/someexecutable
ENTRYPOINT /go/bin/someexecutable

Compare this to my original 500MB image built from the golang base image:

Dockerfile

# Start from a Debian image with the latest version of Go installed
# and a workspace (GOPATH) configured at /go.
FROM golang # This line alone will put you at about 300MB.

# Copy the local package files to the container's workspace.
ADD . /go/src/github.com/dimroc/urbanevents/cityservice

# Build the outyet command inside the container.
# (You may fetch or manage dependencies here,
# either manually or with a tool like "godep".)

WORKDIR /go/src/github.com/dimroc/urbanevents/cityservice

RUN wget https://raw.githubusercontent.com/pote/gpm/v1.3.2/bin/gpm && \
      chmod +x gpm && \
      mv gpm /usr/local/bin # GPM

RUN gpm
RUN go install github.com/dimroc/urbanevents/cityservice/cityrecorder
WORKDIR /go/src/github.com/dimroc/urbanevents/cityservice/cityrecorder
ENTRYPOINT /go/bin/cityrecorder

With the new minimalist Dockerfile, pushing incremental changes only sends a trivial 11MB and the image is now a reasonable 40MB. A far cry from the hundred MB sized docker images floating around.

Cross Compiled Go + Alpine Linux + Docker = Win

comments powered by Disqus