Understanding Multistage builds in Docker

A multi-stage build in Docker allows you to use multiple FROM statements in a single Dockerfile to create separate stages for building and running an application. For example, in the first stage, you can use a large image with the SDK (e.g., mcr.microsoft.com/dotnet/sdk) to compile and publish the application. In the second stage, you copy the build output to a lightweight runtime-only image (e.g., mcr.microsoft.com/dotnet/aspnet) to create the final container. This approach significantly reduces the size of the final image because only the application and its dependencies are included, while the build tools and intermediate files are discarded. This results in smaller, more secure, and more efficient images.

Each image used in a multi-stage Docker build can have a specific tag that identifies the exact version or variant of the image. For example, in the build stage, you might use a tagged SDK image like mcr.microsoft.com/dotnet/sdk:9.0 to ensure the application is compiled with a specific .NET version. In the final stage, you can use a lightweight runtime image such as mcr.microsoft.com/dotnet/aspnet:9.0-alpine for optimized hosting. Tags ensure consistency by locking each stage to a specific version of the base image, preventing unexpected changes from upstream image updates. This approach improves reproducibility, as each stage is built with a clearly defined, immutable environment.

Here’s an example of a multi-stage Dockerfile for a .NET 9 Web API application, demonstrating how to use the SDK image for building the application and the runtime image for the final container.


Multi-Stage Dockerfile

dockerfile
# Stage 1: Build the application FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build WORKDIR /src # Copy the project file and restore dependencies COPY ["MyWebApi/MyWebApi.csproj", "MyWebApi/"] RUN dotnet restore "MyWebApi/MyWebApi.csproj" # Copy the rest of the application source code COPY . . WORKDIR "/src/MyWebApi" # Build the application in Release mode RUN dotnet build "MyWebApi.csproj" -c Release -o /app/build # Publish the application to a folder ready for runtime RUN dotnet publish "MyWebApi.csproj" -c Release -o /app/publish /p:UseAppHost=false # Stage 2: Create the runtime image FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS runtime WORKDIR /app # Copy the published application from the build stage COPY --from=build /app/publish . # Expose ports for HTTP (5000) and HTTPS (5001) EXPOSE 5000 EXPOSE 5001 # Set the entry point to run the application ENTRYPOINT ["dotnet", "MyWebApi.dll"]

Explanation of the Dockerfile

  1. Stage 1: Build the Application

    • Base Image: mcr.microsoft.com/dotnet/sdk:9.0
      • Includes the full .NET SDK for building and publishing the application.
    • Steps:
      • Restores dependencies using dotnet restore.
      • Builds the application in Release mode.
      • Publishes the application to the /app/publish folder, optimized for runtime.
  2. Stage 2: Runtime Image

    • Base Image: mcr.microsoft.com/dotnet/aspnet:9.0-alpine
      • A lightweight runtime-only image optimized for production.
    • Steps:
      • Copies the published application from the build stage.
      • Exposes ports 5000 (HTTP) and 5001 (HTTPS) for the app.
      • Sets the entry point to run the .dll file using dotnet.

How to Build and Run

  1. Build the Docker Image:

    bash
    docker build -t mywebapi:latest .
  2. Run the Docker Container:

    bash
    docker run -p 5000:5000 -p 5001:5001 mywebapi:latest
  3. Access the Application:

    • HTTP: http://localhost:5000
    • HTTPS: https://localhost:5001

Benefits of Multi-Stage Builds in This Example

  • Smaller Final Image:
    • The final image only includes the runtime dependencies, reducing its size significantly compared to the build stage.
  • Better Security:
    • No build tools or unnecessary files are included in the runtime image, minimizing the attack surface.
  • Efficient Development Workflow:
    • Clearly separates the build process and runtime environment.

Comments

Popular posts from this blog

Configuring Any .NET 9.0 Program to Run in Docker: A Step-by-Step Guide

Understand .NET 9.0 Blazor Hosting Models

Understanding a Multi-Stage Dockerfile for .NET 9 Application