docker asp.net core container starting after a mys

2019-07-28 12:31发布

问题:

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?

回答1:

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


回答2:

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://stackoverflow.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);
  }
}