Docker introduced multi-stage builds in May of 2017. In simplest terms, these are Dockerfiles with more than one
FROM statement. With a small tweak, you can build multi-stage Dockerfiles on CircleCI 2.0.
What Are Multi-Stage Docker Builds?
Docker’s new multi-stage builds allow Dockerfiles to become much more powerful, offering complex builds with a single Dockerfile. An example use case might be when you would normally have one Dockerfile for building the source of your application and then another where you would run and test it. Here’s an example
Dockerfile to solve this problem with multi-stage builds, borrowed from docs.docker.com:
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
RUN apk --no-cache add ca-certificates
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
The first “stage” of this Dockerfile builds the Go application using the official Go image from the Docker Library. The second stage then copies the newly built binary from the first stage (with
COPY --from=0) and places it in a custom Alpine image. The result is an Alpine-based Docker image that anyone can use to run this Go app, without any of the extra software that was used to build the app in the first place, such as the Go toolchain.
To copy the binary we built to the second stage, we used
COPY --from=0 since we didn’t name the stage, so instead we used its index number. Instead, we can name a stage within the
FROM statement and then use that name later. So we could start with
FROM golang:1.8.3 as compile-stage and the in the
COPY statement we would use
Building on CircleCI 2.0
The Dockerfile example above can be built with CircleCI 2.0 however a config change needs to be made depending on which
executor you’re using. This is because the version of Docker being used by default (as of this writing) on CircleCI is Docker 17.03.0 Community Edition (CE). Multi-stage builds is supported by Docker v17.05.0-ce and newer. Both the
docker executors allow us to request a newer Docker version.
We can set the Docker version to use for the remote-engine right under
Remember, when using the
docker executor and
setup_remote_docker, this just provides you with a remote Docker engine to connect to. Your base Docker image that your build is using still needs to have a Docker client installed. More on this and which versions of Docker are supported can be found here.
machine executor now supports specifying an image similar to how the
docker executor works, but with our base VM images. The default image isn’t sufficient for our use case but the “edge” image is:
This provides us with a machine VM with Docker v17.06.0-ce. More on the
machine executor here.
With Docker v17.05.0-ce and newer, you can now build multi-stage Dockerfiles on CircleCI 2.0.