2

I am fairly new to Docker, and I'm trying to build a multi project Api solution dockerfile.
After trying out a huge amount of different ways, I have now tried this solution:

https://stackoverflow.com/a/49728860

This is my project layout:

/MainFolder
---Dockerfile
--- Projectname.sln
/Projectname.Api
--- Projectname.Api.csproj
--- code files
/Projectname.Models (api project has dependency of this project)
--- Projectname.Models.csproj
--- code files
/Projectname.Services (api project has dependency of this project)
--- Projectname.Services.csproj
--- code files

This is the current Dockerfile contents:

FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS base
WORKDIR /app
ENV ASPNETCORE_ENVIRONMENT=Production
ENV ASPNETCORE_URLS http://*:5000
EXPOSE 5000

FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS builder
ARG Configuration=Release
WORKDIR /src
COPY *.sln ./
COPY Projectname.Api/Projectname.Api.csproj Projectname.Api/
COPY Projectname.Models/Projectname.Models.csproj Projectname.Models/
COPY Projectname.Services/Projectname.Services.csproj Projectname.Services/
RUN dotnet restore
COPY . .
WORKDIR /src/Projectname.Api
RUN dotnet build -c $Configuration -o /app

FROM builder AS publish
ARG Configuration=Release
RUN dotnet publish -c $Configuration -o /app

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

Note that the solution contains other projects as well, that the API-project is NOT dependent on.

Should I have the Dockerfile in the Api-project folder? Or is it best practice to keep it in the solution folder when you have dependent projects?

After docker build and Docker run -it -p 5000:80 myusername/projectname.api I now get this confirmation:

warn: Microsoft.AspNetCore.Server.Kestrel[0]
      Unable to bind to http://localhost:5000 on the IPv6 loopback interface: 'Cannot assign requested address'.
warn: Microsoft.AspNetCore.Server.Kestrel[0]
      Unable to bind to https://localhost:5001 on the IPv6 loopback interface: 'Cannot assign requested address'.
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /app

But when I browse https://localhost/swagger or http://localhost:5000/swagger, I there is no response.

Annish
  • 2,145
  • 1
  • 14
  • 15
  • 1
    Side note, since you are using same image for “base” and “builder” you do not need to separate them, usually multi staged docker builds have different images for different stages, for example image for building, image for publishing and image for runtime, as you can probably tell, last two images will be significantly smaller than the first. Meaning your “production” image will be less memory than others (which is what you will want all the time) – kuskmen Dec 31 '19 at 11:26
  • Did this resolve your issue:https://stackoverflow.com/a/52718482/11398810? – Rena Jan 01 '20 at 06:00

2 Answers2

2

While I don't know exactly why, the solution to this problem was to explicitly tell the application to run at ports 80 and 443:

Program.cs:

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    webBuilder.UseUrls("http://*:80", "http://*:443");
                });

Then run docker run -it -p 80:80 -p 443:443 myusername/projectname.api

What I don't understand, is that without this, it seemed like my application was running on port 5000, and I used the -p 5000:5000 argument when running docker without luck. When my application is running on port 80, it works. Somewhere in my docker settings, there has to be something that requires the application to be running on port 80 or 443.

Annish
  • 2,145
  • 1
  • 14
  • 15
0

Regarding where to place the Dockerfile in your project, exact placement of the file is up to your preference or team standards. However, I suspect moving the file is to the API project subdirectory will not be a good idea in your case, since your current Dockerfile requires the entire solution to be included in your docker build context. Setting the build context to a different directory than the Dockerfile is not natural for most Docker users I have worked with. Besides, it is already building successfully.

Instead, you you could look into optimizing your multi-stage build by using appropriate runtime image of dotnet core for your final stage, since this image is much smaller than the SDK image.

Here is a page from Microsoft that describes the available dotnet core docker images that you would find helpful: https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/docker/building-net-docker-images?view=aspnetcore-3.1

Regarding being unable to access the application running in your container from a browser on your host, I suspect that your issue is related to an incorrect order of port bindings listed in your docker run command. Try this:

docker run -it -p 5000:5000 myusername/projectname.api

Then you should be able to access http://localhost:5000/swagger from your host.

For reference, docker documentation states that the ports should be listed in the order {host_port}:{container_port}. So if you wanted to bind port 5000 in the container to port 80 on the host, the correct command would be:

docker run -it -p 80:5000 myusername/projectname.api

Using that command, you could access the running application in your container from port 80 on your host.

See the heading Publish or expose port on this page: https://docs.docker.com/engine/reference/commandline/run/

Hope this helps.