2
votes

i have a docker container with asp.net core and a container with mysql. Now i need to wait with asp.net core container for the mysql container is up and ready (both container are starting through docker-compose.yml).

Something like https://github.com/jwilder/dockerize , wait-for-it.sh or wait-for seems not to work for asp.net core container.

Have someone a clue how to solve the problem?

UPDATE

this is my dockerfile:

FROM microsoft/aspnetcore-build:2.0 AS build-env
WORKDIR /app

# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore test.csproj

# Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out test.csproj



# Build runtime image
FROM microsoft/aspnetcore:2.0
WORKDIR /app
COPY ./entrypoint.sh ./app/
RUN chmod +x ./app/entrypoint.sh
CMD /bin/bash ./app/entrypoint.sh

COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "test.dll"]

and this is my entrypoint.sh:

#!/bin/bash

set -e
run_cmd="dotnet run --server.urls http://*:80"

until dotnet ef database update; do
>&2 echo "SQL Server is starting up"
sleep 1
done

>&2 echo "SQL Server is up - executing command"
exec $run_cmd

if i start the container with docker-compose i get the error msg:

Unhandled Exception: System.FormatException: Value for switch '/app/entrypoint.sh' is missing.
test          |    at Microsoft.Extensions.Configuration.CommandLine.CommandLineConfigurationProvider.Load()
test          |    at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
test          |    at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
test          |    at Microsoft.AspNetCore.Hosting.WebHostBuilder.BuildCommonServices(AggregateException& hostingStartupErrors)
test          |    at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
test          |    at test.Program.Main(String[] args) in /app/Program.cs:line 19

How can i combine the ENTRYPOINT and the CMD /bin/bash ./app/entrypoint.sh command in one file. I think i need the ENTRYPOINT for the dotnet applicatioin to run and the cmd for the entrypoint.sh to wait for the database container. Is there a solution get both to run?

2
You will have seen docs.docker.com/compose/startup-order already I imagine. You should NOT try wait for your database, launch the asp.net container anyway and it will start working when the database is accessible. Otherwise you should fix your application to startup and wait without a database.BookOfGreg
the problem is that the asp.net do at startup a migration (entity.framework) of the database and if the database is not ready the asp.net application gives me an error msg that the connection to the database is not working.eldios1981
It sounds more like entity framework is setup incorrectly. It should migrate only on command rather than attempt it instantly.BookOfGreg
if i first start the containers. the database is empty (no table are there). asp.net core application exit with code 139 MySql unable to connect the database host. If i start the application again its works. so i think database is not up and running at the first time. If i do docker-compose down --volumes the tables are deleted and i get the error againeldios1981

2 Answers

1
votes

https://docs.docker.com/compose/aspnet-mssql-compose/

The docker docs list how to get an ASP.net container to wait for it's databases by having the entrypoint.sh poll for DB connectivity.

#!/bin/bash

set -e
run_cmd="dotnet run --server.urls http://*:80"

until dotnet ef database update; do
>&2 echo "SQL Server is starting up"
sleep 1
done

>&2 echo "SQL Server is up - executing command"
exec $run_cmd
1
votes

Instead of having a script that postpones the startup of your dotnet application from outside you can also check and wait for your db connection from within your dotnet application.

In the following, you can see how host.Run() is postponed until waitForDb() returns.

In my example below, I put the logic for checking whether the SQL DB (which is running in a docker container) has already booted inside a service called DbHealthChecker (actually, the whole method waitForDb could be in a dedicated service). This health checker uses one of the techniques described in this stackoverflow thread to check whether the db is running.

public class Program
{
  public static void Main(string[] args)
  {
    var host = CreateWebHostBuilder(args).Build();

    Task.Run(async () => {
      using (var scope = host.Services.CreateScope())
      {
        var services = scope.ServiceProvider;

        await waitForDb(services);

        // ... other initializations, e.g. seeding db ...
      }
    }).Wait();

    host.Run();
  }

  private static async Task waitForDb(IServiceProvider services)
  {
    // create your own connection checker here
    // see https://stackguides.com/questions/19211082/testing-an-entity-framework-database-connection

    var healthChecker = services.GetRequiredService<DbHealthChecker>();
    var maxAttemps = 12;
    var delay = 5000;

    for (int i = 0; i < maxAttemps; i++) {
      if (healthChecker.isConnected()) {
        return;
      }
      await Task.Delay(delay);
    }

    // after a few attemps we give up
    throw new HttpException(503);
  }
}