21 Days of Docker-Day 7-Use multi-stage builds with Dockerfile

$ docker history single-stage -H
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
3b5798ed94c5        17 minutes ago      /bin/sh -c #(nop)  CMD ["./helloworld"]         0B
5c20d8f64a63        17 minutes ago      /bin/sh -c GOOS=linux go build -a -installsu…   11.2MB
3d9633eb783b        17 minutes ago      /bin/sh -c #(nop) COPY file:38b29480b8d44119…   73B
f23ad3b0175a        17 minutes ago      /bin/sh -c #(nop) WORKDIR /tmp                  0B
52b59e9ead8e        2 weeks ago         /bin/sh -c #(nop) WORKDIR /go                   0B
<missing>           2 weeks ago         /bin/sh -c mkdir -p "$GOPATH/src" "$GOPATH/b…   0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PATH=/go/bin:/usr/loc…   0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV GOPATH=/go               0B
<missing>           2 weeks ago         /bin/sh -c set -eux;   dpkgArch="$(dpkg --pr…   327MB
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV GOLANG_VERSION=1.13.1    0B
<missing>           3 weeks ago         /bin/sh -c apt-get update && apt-get install…   182MB
<missing>           4 weeks ago         /bin/sh -c apt-get update && apt-get install…   145MB
<missing>           4 weeks ago         /bin/sh -c set -ex;  if ! command -v gpg > /…   17.5MB
<missing>           4 weeks ago         /bin/sh -c apt-get update && apt-get install…   16.5MB
<missing>           4 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           4 weeks ago         /bin/sh -c #(nop) ADD file:770e381defc5e4a0b…   114MB
  • Just for our own testing, let’s try to run it
$ docker run single-stage
hello world
  • What we can do to make this image efficient, by using multi-stage build
  • Let’s create a new directory
$ mkdir multi-stage
$ cd multi-stage/
  • Copy the helloworld program here
$ cp ../single-stage/helloworld.go .
  • Our new Dockerfile will look like this
FROM golang:1.13.1 as multistage
COPY helloworld.go .
RUN GOOS=linux go build -a -installsuffix cgo -o helloworld .
CMD ["./helloworld"]

FROM alpine:3.10.2
COPY --from=multistage /tmp/helloworld .
CMD ["./helloworld"]
  • NOTE: By default, the stages are not named, and you refer to them by their integer number, starting with 0 for the first FROM instruction. However, you can name your stages, by adding an AS <NAME> to the FROM instruction(FROM golang:1.13.1 as multistage). 
  • Let’s try to dissect it
* We are starting with a minimal base image, alpine. The second FROM instruction starts a new build stage with the alpine:3.10.2 image as its base
* The COPY --from=multistage line copies just the built artifact from the previous stage into this new stage. The Go SDK and any intermediate artifacts are left behind, and not saved in the final image.
  • Build the image
$ docker build -t multistage .
Sending build context to Docker daemon  3.072kB
Step 1/9 : FROM golang:1.13.1 as multistage
---> 52b59e9ead8e
Step 2/9 : WORKDIR /tmp
---> Using cache
---> f23ad3b0175a
Step 3/9 : COPY helloworld.go .
---> Using cache
---> 3d9633eb783b
Step 4/9 : RUN GOOS=linux go build -a -installsuffix cgo -o helloworld .
---> Using cache
---> 5c20d8f64a63
Step 5/9 : CMD ["./helloworld"]
---> Using cache
---> 3b5798ed94c5
Step 6/9 : FROM alpine:3.10.2
3.10.2: Pulling from library/alpine
9d48c3bd43c5: Pull complete
Digest: sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb
Status: Downloaded newer image for alpine:3.10.2
---> 961769676411
Step 7/9 : WORKDIR /root
---> Running in db17523022f1
Removing intermediate container db17523022f1
---> 3f3b74557317
Step 8/9 : COPY --from=multistage /tmp/helloworld .
---> 34393f907c7c
Step 9/9 : CMD ["./helloworld"]
---> Running in 7c71358c0db9
Removing intermediate container 7c71358c0db9
---> b536984e0b37
Successfully built b536984e0b37
Successfully tagged multistage:latest
  • Check the size of the image
$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
multistage          latest              b536984e0b37        3 seconds ago       7.59MB

YAY win, less than 8MB 🙂

As you can see using multi-stage build, we can significantly reduce the size of the image.

  • Test it
$ docker run multistage
hello world


This is a good place, to stop for Day 7

Please follow me with my Journey

This time to make learning more interactive, I am adding

  • Slack
  • Meetup

Please feel free to join this group.



Meetup Group

If you are in the bay area, please join this meetup group https://www.meetup.com/100daysofdevops/