by April 10, 2019
onBuilding compact or minimal docker images is nothing new at all. However with multi-stage docker builds there is (now) a very easy way to create such images without too much complications. These kind of multi-stage builds do exist in docker for quite some time already but I didn’t know until just recently.
Moreover golang lends itself very nicely to compile to statically linked executables and is therefore a perfect candidate to be put into stripped down containers.
Using multi-stage builds you can describe docker files that are constructed by multiple different (base-) images that are chained one after another while being able to share artifacts - and this inside just one Dockerfile
.
Usually you had to imitate such a behavior by creating so called “builder” docker images that replicated an isolated build environment, just to inject the resulting build artifacts into the actual runtime docker image at a later point.
This time that we want to package a golang application into a docker container. That’s why we choose the “official” golang
image as our starting point for the first “builder” stage:
# BUILDER IMAGE
# official golang 1.12 base image
# stretch indicate a specific debian version
FROM golang:1.12-stretch AS builder
# switch into build directory
WORKDIR $GOPATH/src/github.com/kongo2002/example
# copy sources into container
COPY . .
# get dependencies
RUN go get -d -v
# compile statically linked go binary
# CGO_ENABLED=0 - disable cgo tool
# GOOS=linux - target linux only
# GOARCH=amd64 - no cross compile
# -s - strip debug and symbol table
# -w - strip dwarf symbol table
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags "-w -s" -o /go/bin/cmd
Now that we have described the building phase of the docker image we can assemble the image components for the actual runtime of the application. We actually have multiple possibilities to go with:
scratch
- empty container (suitable for simple, self-contained applications that won’t need any dependency during runtime at all)alpine
- very popular base image with a very minimal volume footprint (interesting if you need some dependencies after all)We are going to use the alpine
base image in here as we do need some additional runtime dependencies (ca-certificates
for SSL to be specific):
# RUNTIME IMAGE
# lean and functional base image
FROM alpine:3.9
# install ca certificates (for SSL)
RUN apk add --no-cache ca-certificates
# fetch binary we built in the 'builder' stage before
COPY --from=builder /go/bin/cmd /go/bin/cmd
ENTRYPOINT ["/go/bin/cmd"]
You can build the final docker image with a single call to docker build
just as you are used to:
$ docker build -t kongo2002/go-test -f Dockerfile .
In my example this results in a pretty tiny docker image (YMMV):
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
kongo2002/go-test latest 14f8984cd4a1 About an hour ago 11.5MB