Multi-stage builds on the catalog service

Let's apply the concept of a multi-stage build to the previously defined Docker image. Multi-stage builds are a new feature that requires Docker 17.05 or higher. Multi-stage builds are useful for anyone who has struggled to optimize Dockerfiles while keeping them easy to read and maintain.

Let's explore how it is possible to apply the multi-stage build process by taking a look at the catalog service Dockerfile:

FROM microsoft/dotnet
COPY . /app
WORKDIR /app
RUN dotnet restore
RUN dotnet build
RUN chmod +x ./entrypoint.sh
CMD /bin/bash ./entrypoint.sh

The previously defined file can be changed in the following way:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /project
COPY ["src/Catalog.API/Catalog.API.csproj", "src/Catalog.API/"]
COPY . .
WORKDIR "/project/src/Catalog.API"
RUN dotnet build "Catalog.API.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "Catalog.API.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Catalog.API.dll"]

As you can see, the Dockerfile now executes three different steps (the first two steps are described together because they use the same Docker image):

  • The builder step uses the mcr.microsoft.com/dotnet/core/sdk image to copy the files and trigger the build of the project using the dotnet build command.
  • The publish step uses the same image to trigger the dotnet publish command for the Catalog.API project.
  • The final step, as the name suggests, executes the published package in the runtime environment using the mcr.microsoft.com/dotnet/core/aspnet:3.1 image. Finally, it runs the service using the ENTRYPOINT command.

It is important to note that this change optimizes the resulting image produced by the Dockerfile. After this change, we don't need the entrypoint.sh file anymore because the Dockerfile directly triggers the execution of the service using the dotnet Catalog.API.dll command.

Using a multi-stage build approach, we should also notice that we cannot trigger the execution of the database migrations because the runtime Docker image doesn't use the Entity Framework Core (EF Coretools. Consequently, we need to find another way to trigger the migrations. One possible option is to unleash the migrations from the Startup file of our service. Furthermore, EF Core provides a way to apply migrations at runtime by using the following syntax in the Configure method:

using Polly;
using Microsoft.Data.SqlClient;

...
public class Startup
{
...
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment()) app.UseDeveloperExceptionPage();

ExecuteMigrations(app, env);
...
}

private void ExecuteMigrations(IApplicationBuilder app,
IWebHostEnvironment env)

{
if (env.EnvironmentName == "Testing") return;

var retry = Policy.Handle<SqlException>()
.WaitAndRetry(new TimeSpan[]
{
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(6),
TimeSpan.FromSeconds(12)
});

retry.Execute(() =>
app.ApplicationServices.GetService<CatalogContext>().Database.Migrate());
}
}
}

The preceding code ensures the execution of the database migrations through the execution of the app.ApplicationServices.GetService<CatalogContext>().Database.Migrate() instruction. Because of the startup times of the msssql container, we need to implement a retry policy by handling SqlException and retrying with an exponential time approach. The preceding implementation uses Polly to define and execute a retry policy. Furthermore, we need to add the Polly dependency by executing the following command in the Catalog.API project:

dotnet add package Polly

Retry policies are really useful in the distributed systems world, to successfully handle failure. Polly and the implementation of resilience policies in a web service will be discussed in the next chapter as part of communication over HTTP between multiple services.

In a real-world application, it is quite unusual to execute migrations during the deployment phase of the service. The database schema rarely changes, and it is essential to separate the implementations related to the service with the changes made in the database schema. For demonstration reasons, we are executing the migrations of the database in every deployment by overwriting the database changes to take the most straightforward approach.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.0.192