قالب وردپرس درنا توس
Home / Tips and Tricks / What are Multi-Stage Docker Builds? – CloudSavvy IT

What are Multi-Stage Docker Builds? – CloudSavvy IT



With multi-stage Docker builds, you can create Docker files with multiple FROM explanations. This means you can create images derived from different bases, reducing the size of your final build.

Docker images are created by selecting a base image with the FROM statement. You then add layers to that image by adding commands to your Docker file.

Multi-phase builds allow you to split your Dockerfile into multiple sections. Each stage has its own FROM statement so you can include more than one image in your builds. Stages are built sequentially and can refer to their predecessors so you can copy the output from one layer to the next.

Multi-phase builds in action

Let̵

7;s take a look at how to create a multi-stage build. We are working with a barebones PHP project that uses Composer for its dependencies and Sass for its stylesheets.

Here’s a multiphase Docker file that includes our full build:

FROM node:14 AS sass
WORKDIR /example
RUN npm install -g node-sass
COPY example.scss .
RUN node-sass example.scss example.css
 
FROM php:8.0-apache
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY composer.json .
COPY composer.lock .
RUN composer install --no-dev
COPY --from=sass /example/example.css example.css
COPY index.php .
COPY src/ src

You immediately notice that we have two FROM statements that split our Dockerfile into two logical sections. The first stage is dedicated to assembling the Sass, while the second focuses on combining everything in the final container.

We use the node-sass implementation of Sass. We therefore start with a Node.JS base image, within which we install node-sass worldwide from npm. We then use node-sass to compile our stylesheet example.scss in the pure CSS example.css. The high-level summary of this stage is that we take a base image, run a command and get an output that we want to use later in our build (example.css).

The next stage introduces the base image for our application: php8.0-apache. The last FROM statement in your Dockerfile defines the image that your containers will run. Our earlier node image is ultimately irrelevant to our application’s containers – it is used purely as a convenience tool during build.

Then we use Composer to install our PHP dependencies. Composer is PHP’s package manager, but it is not included with the official PHP Docker images. We therefore copy the binary file to our container from the special Composer image.

We had none FROM statement to do this. Since we are not running commands for the Composer image, we can use the --from flag with COPY to refer to the image. Usually, COPY copies files from your local build context to your image; with --from and an image name, it will create a new container with that image and then copy the specified file from it.

Later we use Dockerfile COPY --from again, this time in a different form. At the top we wrote our first FROM statement as FROM node:14 AS sass. The AS clause created a named stage called sass.

We now reference the temporary container created by this phase with COPY --from=sass. This allows us to copy our built-in CSS to our final image. The rest of the steps are routine COPY edits, used to get our source code from our local working directory.

Advantages of multiphase builds

Multiphase builds allow you to create complex build routines with a single Docker file. Before they were introduced, it was common for complex projects to use multiple Docker files, one for each phase of their build. These then had to be orchestrated by manually written shell scripts.

With multiphase builds, our entire build system can be contained in one file. You don’t need wrapper scripts to take your project from raw codebase to the final application image. A regular customer docker build -t my-image:latest . is sufficient.

This simplification also offers opportunities to improve the efficiency of your images. Docker images can grow large, especially if you use a language runtime as a base.

Take the officer golang image: it’s almost 300MB. Traditionally, you could copy your Go source code to the image and use it to compile your binary file. You then copy your binary file back to your host computer before starting a new build. This one would use a Docker file with a lightweight base image such as alpine (about 10 MB). You would add your binary back in, resulting in a much smaller image than if you had used the original golang base to run your containers.

Multi-phase builds make this type of system much easier to implement:

FROM golang:latest
WORKDIR /go
COPY app.go .
RUN go build -o my-binary
 
FROM alpine:latest
WORKDIR /app
COPY --from=build /go/my-binary .
CMD ["./my-binary"]

In eight lines, we managed to establish a procedure that previously required at least three files – one golang Dockerfile, a alpine Docker file and a shell script to manage the steps in between.

Conclusion

Multi-phase builds can greatly simplify the construction of complex Docker images. They let you use multiple interconnected build steps that can push output artifacts forward.

The model also promotes construction efficiency. The ease with which you can reference various base images helps developers ensure that the final output is as small as possible. You will benefit from lower storage and bandwidth costs which can be significant when using Docker in a CI / CD system.


Source link