Multi-stage builds¶
See also
Contents
Multi-stage builds¶
In the previous example, our final image contain:
our hello program
its source code
the compiler
Only the first one is strictly necessary.
We are going to see how to obtain an image without the superfluous components.
Multi-stage builds principles¶
At any point in our Dockerfile, we can add a new FROM line.
This line starts a new stage of our build.
Each stage can access the files of the previous stages with COPY –from=….
When a build is tagged (with docker build -t …), the last stage is tagged.
Previous stages are not discarded: they will be used for caching, and can be referenced.
Multi-stage builds in practice¶
Each stage is numbered, starting at 0
We can copy a file from a previous stage by indicating its number, e.g.
COPY --from=0 /file/from/first/stage /location/in/current/stage
We can also name stages, and reference these names
FROM golang AS builder
RUN ...
FROM alpine
COPY --from=builder /go/bin/mylittlebinary /usr/local/bin/
Multi-stage builds for our C program¶
We will change our Dockerfile to:
give a nickname to the first stage: compiler
add a second stage using the same ubuntu base image
add the hello binary to the second stage
make sure that CMD is in the second stage
The resulting Dockerfile is on the next slide.
Multi-stage build Dockerfile¶
Here is the final Dockerfile:
FROM ubuntu AS compiler
RUN apt-get update
RUN apt-get install -y build-essential
COPY hello.c /
RUN make hello
FROM ubuntu
COPY --from=compiler /hello /hello
CMD /hello
Let’s build it, and check that it works correctly:
Before the build¶
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello latest aae25a3dfa28 30 minutes ago 325MB
<none> <none> e43bb6363c1f 42 minutes ago 325MB
ubuntu latest 452a96d81c30 4 weeks ago 79.6MB
# docker build -t hellomultistage .
Sending build context to Docker daemon 3.072kB
Step 1/8 : FROM ubuntu AS compiler
---> 452a96d81c30
Step 2/8 : RUN apt-get update
---> Using cache
---> 01e04143b340
Step 3/8 : RUN apt-get install -y build-essential
---> Using cache
---> 9139dae8927e
Step 4/8 : COPY hello.c /
---> Using cache
---> c803db9440ed
Step 5/8 : RUN make hello
---> Using cache
---> 2d25a58a49f0
Step 6/8 : FROM ubuntu
---> 452a96d81c30
Step 7/8 : COPY --from=compiler /hello /hello
---> d427a7aa53af
Step 8/8 : CMD /hello
---> Running in f338055a571e
Removing intermediate container f338055a571e
---> c8be88f00576
Successfully built c8be88f00576
Successfully tagged hellomultistage:latest
# docker run hellomultistage
Hello, big world!
Comparing single/multi-stage build image sizes¶
List our images with docker images, and check the size of:
the ubuntu base image (79.6MB)
the single-stage hello image (325MB)
the multi-stage hellomultistage image (79.6MB)
We can achieve even smaller images if we use smaller base images.
However, if we use common base images (e.g. if we standardize on ubuntu), these common images will be pulled only once per node, so they are virtually “free.”
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hellomultistage latest c8be88f00576 About a minute ago 79.6MB
hello latest aae25a3dfa28 34 minutes ago 325MB
<none> <none> e43bb6363c1f About an hour ago 325MB
ubuntu latest 452a96d81c30 4 weeks ago 79.6MB