0

I am trying to "dockerize" this clean architecture template for .net Core 3. I use the docker pull request here as the base for my proff of concept app. This is a .net core 3 webapi project with an Angular front end as the client app.

WHAT I HAVE:

  1. The base code from the pull request works locally.
  2. An initial problem I had to overcome was setting the cert for identity server 4 in a local non development env, I had to mount a volume with the cert and reference it from the appsettings.json file like
  "IdentityServer": {
    "Key": {
      "Type": "File",
      "FilePath": "/security/mycert.pfx",
      "Password": "MyPassword"
    }
  }
  1. I set up a CI/CD pipeline in azure to build the project and deploy the image to an azure container registry
  2. I set up a CI/CD release to deploy the docker image to a Web App for Containers (Linux) web app. Both these steps work properly

MY PROBLEM:

The web app loads and runs the container and the angular front end is shown. However, it appears that the web api is not running. Any attempt to hit an endpoint of the web api returns the following error in the browser console:

GET https://.....azurewebsites.net/_configuration/CleanArchitecture.WebUI 404 (Not Found)

Error: Uncaught (in promise): Error: Could not load settings for 'CleanArchitecture.WebUI' Error: Could not load settings for 'CleanArchitecture.WebUI'

CleanArchitecture.WebUI is the name of the assembly that is the entry point in the dockerfile:

ENTRYPOINT ["dotnet", "CleanArchitecture.WebUI.dll"]

All other aspects of the front end work properly, only calls to "backend" api fail. Another issue is that if I get the docker logs from the azure container, there are no errors shown.

WHAT I TRIED

  1. I tried to add "dotnet CleanArchitecture.WebUI.dll" to the startup command of the container in the container settings of the web app, but that just throws an error that it can't find CleanArchitecture.WebUI.dll
  2. I have tried to increase the logging level ("LogLevel": "Default": "Debug") to get more details, but no additional error details are shown in the docker logs.
  3. It might be an error loading the Identity Server 4 certificate, but there are no errors to confirm this problem.

Here is my docker compose file that is used by the azure pipeline: version: '3.4'

services:
  webui:
    image: ${DOCKER_REGISTRY-}webui
    build:
      context: .
      dockerfile: src/WebUI/Dockerfile
    environment:
      - "UseInMemoryDatabase=false"
      - "ASPNETCORE_ENVIRONMENT=Production"
      - "ConnectionStrings__DefaultConnection=myconnection"
      - "ASPNETCORE_Kestrel__Certificates__Default__Password=mypass"
      - "ASPNETCORE_Kestrel__Certificates__Default__Path=/security/mycert.pfx"
    ports:
        - "5000:5000"
        - "5001:5001"
    volumes: 
        - mcpdata:"/security:/"
    restart: always

mcpdata is the name of the azure file share that gets mounted and contains the actual cert

here is my azure-pipeline.yml for the CI/CD:

trigger:
- staging

resources:
- repo: self

variables:
  # Container registry service connection established during pipeline creation
  dockerRegistryServiceConnection: '****'
  imageRepository: 'cleanarchitecture'
  containerRegistry: '****.azurecr.io'
  dockerComposeFilePath: '$(Build.SourcesDirectory)docker-compose.Production.yml'
  tag: '$(Build.BuildId)'
  
  # Agent VM image name
  vmImageName: 'ubuntu-latest'

stages:
- stage: Build
  displayName: Build and push stage
  jobs:  
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)
    steps:
    - task: Docker@2
      displayName: Build and push an image to container registry
      inputs:
        command: buildAndPush
        repository: $(imageRepository)
        dockerComposeFile: $(dockerComposeFilePath)
        containerRegistry: $(dockerRegistryServiceConnection)
        tags: staging

QUESTION?

Can someone help me figure out why it appears like my web api is not running but no errors are thrown. At a minimum I would be happy if someone could help me see the errors in the docker logs.

thanks in advance

J King
  • 4,108
  • 10
  • 53
  • 103
  • It seems CleanArchitecture.WebUI.dll is not generated? or it resides in the different folder. Is the app built in docker before push to ACR? You can check the answers to [this thread](CleanArchitecture.WebUI.dll) to check the logs. – Levi Lu-MSFT Oct 09 '20 at 10:42
  • @LeviLu-MSFT the build happens in the azure ci/cd pipeline and is pushed to Azure container reg. When I run "docker-compose -f 'docker-compose.Production.yml' up --build" locally, the container works. The other weird thing is that no errors are shown in logs, you would think that if Cleanarchitecture was missing docker would gereate an error trying to run it. – J King Oct 09 '20 at 14:08

2 Answers2

2

I tried to repeat, with "clean architecture" using the following (note, I'm using zsh on MacOS, but the same should work on Windows/Linux too):

take clean_architecture  
dotnet new --install Clean.Architecture.Solution.Template
dotnet new ca-sln 

The documentation suggests, clicking F5 in Visual Studio will start the template, although I had to do:

cd src/WebUI/ClientApp
npm install

At this point the app starts locally by hitting F5. Note, what happens here is that ASP.Net Core forwards requests to the dev server, so effectively, this does ng serve --port 53543 AND starts Asp.Net Core (Kestrel in my case) on port 5001, browsing to http://127.0.0.1:53543 provides the angular page directly. Browsing to https://localhost:5001 brings up the same angular page, as forwarded by ASPNetCore to Angular. All very confusing... Detailed more here

Note in Startup.cs the following lines of code exist, these are usually set based on the environment variable ASPNETCORE_ENVIRONMENT

            if (!env.IsDevelopment())
            {
                app.UseSpaStaticFiles();
            }

            -- and within "app.UseSpa"
                if (env.IsDevelopment())
                {
                    spa.UseAngularCliServer(npmScript: "start");
                }

Anyway, it looks like you've got that environment variable set to Production, which should just serve the built files from the ClientApp\dist folder (rather than forwarding to the dev server) that suggests that if you see the Angular, then the .Net Core service is running... I'll try and rebuild the Dockerfiles first...

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
ENV ASPNETCORE_URLS=https://+:5001;http://+:5000
WORKDIR /app
EXPOSE 5000
EXPOSE 5001

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
RUN apt install -y nodejs
WORKDIR /src
COPY ./src/WebUI/WebUI.csproj src/WebUI/
COPY ./src/Application/Application.csproj src/Application/
COPY ./src/Domain/Domain.csproj src/Domain/
COPY ./src/Infrastructure/Infrastructure.csproj src/Infrastructure/
RUN dotnet restore "src/WebUI/WebUI.csproj"
COPY . .
WORKDIR "/src/src/WebUI"
RUN dotnet build "WebUI.csproj" -c Release -o /app/build

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

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

Then build and run as follows:

# build takes a while
docker build -f ./src/WebUI/Dockerfile -t clean-architecture .

# note, this fails first time, because I set up as clean_architecture so the entry point is incorrect
docker run --rm -it -p 5000:5000 -p 5001:5001 clean-architecture

# run the container and override the entrypoint
docker run --rm -it --entrypoint /bin/bash clean-architecture 

# From within the container...
root@93afb0ad21c5:/app# dotnet clean_architecture.WebUI.dll 

# note, in .Net 3.1, you can also do this directly, as follows:
root@93afb0ad21c5:/app# ./clean_architecture.WebUI 

Now there is a problem with LocalDB: System.PlatformNotSupportedException: LocalDB is not supported on this platform.

Switch appsettings.Production.json to be "UseInMemoryDatabase": true

The problem then appears to be certificates...

I created a certificate using:

dotnet dev-certs https -ep ./https/clean-arch.pfx -p anything   

For IdentityServer, I change appSettings.Production.json as follows:

  "IdentityServer": {
    "Key": {
      "Type": "File",
      "FilePath": "/app/https/https/clean-arch.pfx",
      "Password": "anything"
    }
  }

and then running on Linux, probably means running Kestrel, which means we need to provide HTTPS certs there too, which I did by setting the following in Program.cs

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.ConfigureKestrel((context, options) =>
                    {
                        options.AllowSynchronousIO = true;
                        options.Listen(IPAddress.Loopback, 5000, listenOptions =>
                        {
                            listenOptions.UseConnectionLogging();
                            listenOptions.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http1AndHttp2;
                        });
                        options.Listen(IPAddress.Any, 5001, listenOptions =>
                        {
                            listenOptions.UseConnectionLogging();
                            listenOptions.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http1AndHttp2;
                            listenOptions.UseHttps(new System.Security.Cryptography.X509Certificates.X509Certificate2("/app/https/https/clean-arch.pfx", "anything"));
                        });
                    });
                    webBuilder.UseStartup<Startup>();
                });

At each stage I built the app in docker using...

\clean_architecture $ docker build -f ./src/WebUI/Dockerfile -t clean-architecture  .  
/clean_architecture $ docker run --rm -it -v /Users/monkey/src/csharp/clean_architecture/:/app/https/ -p 5000:5000 -p 5001:5001 --entrypoint /bin/bash clean-architecture     

... and once running in bash (in docker), I used the following to start the application:

root@c5b4010d03be:/app# ./clean_architecture.WebUI 

Good luck, hope that helps. Note, if it works in Docker, on your machine, it should work in Azure. I'll look at getting it going in Azure another day. Happy to upload my code to GitHub if it would help?

0909EM
  • 4,761
  • 3
  • 29
  • 40
  • thanks for the effort, I was able to figure out the problem on my end, see the answer below – J King Oct 10 '20 at 18:04
0

thanks to 0909EM for the huge effort in answering the question, but the solution was different.

I figured out what was going on. There are two issues.

  1. The docer-compose.override.yml file looks like:
version: '3.4'

services:
  webui:
    environment:
      - "ASPNETCORE_ENVIRONMENT=Development"
      - "SpaBaseUrl=http://clientapp:4200"

  clientapp:
    image: ${DOCKER_REGISTRY-}clientapp
    build:
      context: src/WebUI/ClientApp
      dockerfile: Dockerfile
    depends_on:
      - webui
    restart: on-failure

  db:
    ports:
      - "1433:1433"

notice the line dockerfile: Dockerfile in the src/webui/clientapp context. This dockerfile was overwriting the proper docker file in src/webui during the azure pipeline build. For some reason when I run the following command locally: docker-compose -f 'docker-compose.Production.yml' up --build it does not pull in the docker-compose.override settings, but the override settings do get used in the azure pipeline build.

Therefore, the angular dockerfile is the only one built and that image does not contain the .net core web api project. Which explains why I see the front end but cannot get to the api endpoints and also why the dockerfile has no .net core errors.

I was able to fix this in two ways.

First: rename the dockerfile in src/webui/clientapp to Dockerfile.clientapp and change the line in the docker.overrride file to dockerfile: Dockerfile.clientapp

SECOND: just remove the docker override file from the online repository that the azure pipeline pulls from.

As a result the proper dockerfile is used and the web api project is in the image.

  1. The second issue: Now that the proper image is running, the .net core web api throws an error about loading the cert for identity server. This confirms my suspicion. Because this issue is not related to my original question about getting the web api running in the container, i have opened another question about it.
J King
  • 4,108
  • 10
  • 53
  • 103