5.5 Creating Multi-stage Docker Image Builds

From Oracle Container Runtime for Docker 17.06, it is possible to perform multi-stage builds from a single Dockerfile. This allows you to perform interim build or compilation steps during the creation of the final image, without including all of the build tools and artifacts in the final image. This helps to reduce image sizes, and improves performance. It also allows you to deliver an image containing only the required binary and not all of the layers that were required to produce the binary.

In this section, we provide a very simple example scenario, where the source of a program is built in an interim compiler image and the resulting binary is copied into a separate image to produce the final target image. This entire build is handled by a single Dockerfile.

Create a simple "hello world" style program in C, by pasting the following text into a file named hello.c:

#include <stdio.h>

int
main (void)
{
  printf ("Hello, world!\n");
  return 0;
}

Create a Dockerfile that contains the following text:

FROM gcc AS BUILD
COPY . /usr/src/hello
WORKDIR /usr/src/hello
RUN gcc -Wall hello.c -o hello

FROM oraclelinux:7-slim
COPY --from=BUILD /usr/src/hello/hello hello
CMD ["./hello"]

Note that there are two FROM lines in this Dockerfile. The first FROM statement pulls the latest gcc image from the Docker hub and uses the AS syntax to assign it a name that we can refer to later when copying elements from this temporary build environment to our target image.

In the build environment, the source file is copied into the image and the gcc compiler is run against the source file to produce a hello binary.

The second FROM statement pulls the oraclelinux:7-slim image. This image is used to host the hello binary, which is copied into it directly from the build environment. By doing this, the source, the compiler and any other build artifacts can be excluded from the final image.

To build the new image and run it, try running the following:

$ docker build -t hello-world ./
Sending build context to Docker daemon  35.38MB
Step 1/7 : FROM gcc AS BUILD
 ---> 7d9419e269c3
Step 2/7 : COPY . /usr/src/hello
 ---> ee7310cc4464
Removing intermediate container 1d51e6f16833
Step 3/7 : WORKDIR /usr/src/hello
 ---> 2c0298733ba0
Removing intermediate container 46a09ccc06d6
Step 4/7 : RUN gcc -Wall hello.c -o hello
 ---> Running in f003deeebc20
 ---> 67c85367cac1
Removing intermediate container f003deeebc20
Step 5/7 : FROM oraclelinux:7-slim
 ---> da5e55a16f7a
Step 6/7 : COPY --from=BUILD /usr/src/hello/hello hello
 ---> 8bd284b0d7eb
Removing intermediate container d71eee578325
Step 7/7 : CMD ./hello
 ---> Running in d6051d9e0a9d
 ---> dac5aa2d651d
Removing intermediate container d6051d9e0a9d
Successfully built dac5aa2d651d
Successfully tagged hello-world:latest

$ docker run hello-world
Hello, world!

The hello-world image is generated to contain and run the hello binary, but doesn't contain any of the components that were required to build the binary. The final image has less layers, is smaller and excludes any of the build steps in its history.