Multistage Docker Builds
So recently I was working on a relatively small Golang microservice. It consumed ZeroMQ streams, performed some calculations on the timeseries data & exposed standarized data via a web API.
I used goczmq as a golang interface to the CZMQ API.
Looking at the cgo tags, goczmq
depends on following libraries.
These are all the dependencies I needed for my app. Hence, my initial Dockerfile
was relatively straightforward.
FROM golang:1.16 as base
RUN apt-get update -qq && \
apt-get install -qq --yes --no-install-recommends \
build-essential \
libczmq-dev \
libzmq3-dev \
libsodium-dev
WORKDIR /go/src/app
COPY . .
RUN go get -d -v ./...
RUN go install -v ./...
CMD ["app"]
While it did the job, the resulting docker image was huge (~1.4GB). The compressed docker image was too around ~500MB. It seemed quite a lot, when the app itself had only 2 package dependencies.
go.mod
require (
github.com/sirupsen/logrus v1.8.1
github.com/zeromq/goczmq v0.0.0-20190622112907-4715d4da6d4b
)
Certainly, it was not ideal.
To tackle this, I switched to a multistage build approach. The idea was to:
- use
golang
docker image to prepare the final build. - throw that build on a lightweight base image (
debian:buster-slim
in this case) for final execution.
With this approach, my final Dockerfile
looked as follows:
# syntax=docker/dockerfile:1
FROM golang:1.16 as base
RUN apt-get update -qq && \
apt-get install -qq --yes --no-install-recommends \
build-essential \
libczmq-dev \
libzmq3-dev \
libsodium-dev
WORKDIR /go/src/app
COPY . .
RUN go get -d -v ./...
RUN go build -o app ./...
# ---- Release ----
FROM debian:buster-slim AS release
RUN apt-get update -qq && \
apt-get install -qq --yes --no-install-recommends libczmq-dev
COPY --from=build /go/src/app/app ./
CMD ["./app"]
The image final size came down to 137MB with compressed size of ~40MB.