A multistage build refers to building docker images in multiple steps. It is based on a dockerfile that includes multiple FROM instructions.
An example of multistage build consists of creating an image with all the development tools to build an application. The second stage consists of copying the compiled application binaries with the necessary runtime tools to a new image.
Multistage builds have many advantages:
- Maintain a single docker file to build multiple images
- Avoid using scripts to create related images
- Copy and reuse artifacts from one stage to another stage
- Allow the built of a specific target in a dockerfile
- Reduce the final image size
Stages are named to be referenced later in the dockerfile. The "AS" keyword is appended to the FROM instruction to name the current stage. The stage name can be used in the subsequent FROM or COPY instructions.
FROM progres:latest AS build
A stage can be based on an external image or on a previous stage.
FROM alpine:latest AS build
...
FROM build AS dev
In multistage dockerfile, you can copy artifacts such as files and directories from previous stages into another stage. Also, you are allowed to copy from external images into the current stage.
The COPY instruction has the --from flag that accepts a stage name or an external image. The syntax of the COPY instruction is as follows:
COPY --from=<stage-name> <source> <destination>
COPY --from=<image-name> <source> <destination>
COPY --from=build /release /app/publish
COPY --from=alpine /data/docs /app/doc
With a multistage dockerfile, you can build a specific stage instead of building all the stages. This is achieved using the --target flag in the docker build
command.
$ docker build --target <stage-name> ...
$ docker build --target build -t myapp-dev:1.2 .
In case of no target is specified during the build, the last stage in the dockerfile is built. Usually, a dockerfile ends with a default or final stage as follows:
FROM alpine:lates AS build
...
FROM build AS production
...
FROM production AS default
The following code is an example of multistage dockerfile generated by the Docker extension in Visual Studio Code. This dockerfile builds and publishes a "Hello World" console application.
# Stage 1
FROM mcr.microsoft.com/dotnet/core/runtime:3.1 AS base
WORKDIR /app
# Stage 2
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ["MyApp.csproj", "./"]
RUN dotnet restore "./MyApp.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "MyApp.csproj" -c Release -o /app/build
# Stage 3
FROM build AS publish
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish
# Stage 4
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]
Stage 1:
- The runtime image mcr.microsoft.com/dotnet/core/runtime:3.1 is pulled from the Microsoft Container Repository and given the name "base". This image is used as the base image to publish our console application.
Stage 2:
- The SDK image mcr.microsoft.com/dotnet/core/sdk:3.1 is pulled from the Microsoft repository and named "build". This image is used to compile and build our application.
- The project file is copied to the /src folder inside the build image.
- The
dotnet restore
command downloads all dependencies referenced in the project file. - The project files are copied to the /src folder inside the build image.
- The project is built to create a and application release binaries. The release binaries are stored in folder /app/build.
Stage 3:
- The image of this stage is based on the "build" image from stage 2. The name of the stage is set to "publish".
- Publish the application binaries to the /app/publish folder.
Stage 4:
- The image used in this stage is based on the "base" image (stage 1) which includes the runtime tools. The stage is named "final".
- Copy the content of the folder /app/publish from stage 3 to the folder /app/publish in the "base" image.
- Run the application when the container starts.
To build the final image destined for production:
$ docker build -t myapp .
The generated images are listed below:
$ docker images
REPOSITORY TAG SIZE
myapp latest 190MB
<none> <none> 709MB
mcr.microsoft.com/dotnet/core/sdk 3.1 708MB
mcr.microsoft.com/dotnet/core/runtime 3.1 190MB
Compare the size of the myapp image (final image) with the sdk image (build image). The <none> image corresponds to stage 3 image (publish image).
The myapp container can be run as follow:
$ docker run --rm myapp