skip to Main Content

I am using a docker container with two applications inside. The first application is a web api on .net 7, which launches the second application (using Process Start() method). The second application is Watson lite web server (console application). Watson lite web server listens on 127.0.0.1:9000. After launching the Watson lite web server, the web api .net 7 application sends a post request to http://127.0.0.1:9000/calculate/ – Watson lite web server performs calculations and returns a response. If you test such a configuration on Windows 11, everything works fine, but if you create and run a docker container, the .net 7 web api successfully launches the Watson lite web server, but when the .net 7 web api application tries to send a request to http://127.0.0.1:9000/calculate/ error is returned: Connection refused (127.0.0.1:9000).
What could be the mistake?
The .net 7 web api application starts as a main process through ENTRY POINT docker command.

I tried to run Watson lite, specifying the address 0.0.0.0 instead of 127.0.0.1 or just *. Nothing helps…
May be something wrong with user access rights? I get inside working docker container and with help of curl command everything works fine…

Here is sample code for the Watson lite web server in console application:

internal class Program
{   
    private static CancellationTokenSource cts = new CancellationTokenSource();

    static async Task Main(string[] args)
    {
        // define host:
        string host = "+";

        // define port:
        int port = 9000;

        // set up web server settings:
        WebserverSettings settings = new WebserverSettings(host, port, false);

        // create watson lite web server:
        WebserverLite server = new WebserverLite(settings, DefaultRoute);

        try
        {
            // set up CalculateRoute:
            server.Routes.PreAuthentication.Parameter.Add(WatsonWebserver.Core.HttpMethod.POST, "/calculate/", CalculateRoute);
            // set up StopRoute:
            server.Routes.PreAuthentication.Parameter.Add(WatsonWebserver.Core.HttpMethod.GET, "/stop/", StopRoute);

            // start server:
            server.Start();
            
            // wait for cancellation token to cancel:
            await Task.Delay(-1, cts.Token);                        
        }
        catch (Exception exception)
        {
            // if there some exception occured or calcullation token canceled,
            // then stop the server:            
            server.Stop();
        }
        finally
        {
            // release resources:
            cts.Dispose();
        }
    }

    /// <summary>perform calculations:</summary>
    private static async Task CalculateRoute(HttpContextBase ctx)
    {
        // get task as json string:
        string inputJson = ctx.Request.DataAsString;

        // perform calculations:
        string result = Calculate(inputJson);

        // return the result:
        await ctx.Response.Send(result);
    }

    /// <summary>stop web server</summary>
    private static async Task StopRoute(HttpContextBase ctx)
    {
        cts.Cancel();
        await ctx.Response.Send("Sent stop signal to server");
    }
}

The Web api .net 7 application (main entry point in docker container):

[ApiController]
[Route("solve/")]
public async Task<JsonResult> Post(SolveTaskInputDto calculatorModel, int id, CancellationToken ct)
{
    try
    {
        // create Process object:
        using Process prs = new Process();

        // fill data:
        // path to executable file for console app (with Watson lite web server inside):
        prs.StartInfo.FileName = pathToWatsonLiteExecutableFile;    
        prs.StartInfo.UseShellExecute = false;
        prs.StartInfo.RedirectStandardOutput = true;

        // start Watson Lite web server:
        prs.Start()
        
        // create query body:
        HttpContent httpContent = new StringContent(
        JsonConvert.SerializeObject(calculatorModel),
        // use UTF8 encoding
        Encoding.UTF8,
        // and json data format:
        "application/json");
    
        // send post query:
        HttpResponseMessage response = await _httpClient.PostAsync("http://127.0.0.1:9000/calculate/", httpContent, ct);
    
        response.EnsureSuccessStatusCode();

        // get data as string:
        string result = await response.Content.ReadAsStringAsync(ct);    
        
        // return result:
        return new JsonResult(resultDto);
    }
    catch(Exception exp)
    {
        return new JsonResult(exp.Message);
    }   
}

The Dockerfile:

# ====== .NET CORE SDK ======

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build

COPY my_nuget_packages/*.nupkg /nuget_local/

# Add package sources
RUN echo "
  <configuration>
  <packageSources>
  <clear />
  <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
  <add key="local" value="/nuget_local" />
  </packageSources>
  </configuration>
  " > ${HOME}/CustomNuGet.config

# ====== Web.Api ======

WORKDIR /source_api

ARG api_project_prefix=Web.Api

# ====== restore ======

COPY ${api_project_prefix}/*.sln ./

COPY ${api_project_prefix}/Web.Api/*.csproj ./Web.Api/

ENV DOTNET_CLI_TELEMETRY_OPTOUT=1

RUN dotnet restore
    --configfile ${HOME}/CustomNuGet.config
    --packages /nuget/packages
    Web.Api
    -r linux-x64
    --no-cache
    #/p:PublishSingleFile=true
    #/p:PublishReadyToRun=true 

# copy everything else and build app
COPY ${api_project_prefix}/ .

# ====== build ======

RUN dotnet build
    Web.Api
    -c Release
    -r linux-x64
    --packages /nuget/packages
    --no-cache
    --no-restore
    --self-contained true
    #/p:PublishSingleFile=true
    #/p:PublishReadyToRun=true

# ====== publish ======

RUN dotnet publish
    Web.Api
    -o /published/api
    -c Release
    -r linux-x64
    --packages /nuget/packages
    --no-cache
    --no-restore
    --no-build
    --self-contained true
    #/p:PublishSingleFile=true
    #/p:PublishReadyToRun=true

# ====== Watson.App.Console ======

WORKDIR /source_watson_console

ARG watson_console_project_prefix=Watson.App.Console

# ====== restore ======

COPY ${watson_console_project_prefix}/*.sln ./

COPY ${watson_console_project_prefix}/Watson.App.Console/*.csproj ./Watson.App.Console/

RUN dotnet restore
    --configfile ${HOME}/CustomNuGet.config
    --packages /nuget/packages
    Watson.App.Console
    -r linux-x64
    --no-cache
    #/p:PublishSingleFile=true
    #/p:PublishReadyToRun=true 

# copy everything else and build app
COPY ${watson_console_project_prefix}/ .

# ====== build ======

RUN dotnet build
    Watson.App.Console
    -c Release
    -r linux-x64
    --packages /nuget/packages
    --no-cache
    --no-restore
    --self-contained true
    #/p:PublishSingleFile=true
    #/p:PublishReadyToRun=true

# ====== publish ======

RUN dotnet publish
    Watson.App.Console
    -o /published/cas_console
    -c Release
    -r linux-x64
    --packages /nuget/packages
    --no-cache
    --no-restore
    --no-build
    --self-contained true
    #/p:PublishSingleFile=true
    #/p:PublishReadyToRun=true

# ====== ASPNET CORE RUNTIME ======
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base

RUN addgroup --system --gid 1000 dotnet && adduser --system --gid 1000 dotnet

ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
    DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
    DOTNET_USE_POLLING_FILE_WATCHER=true
    TZ=Europe/Moscow
    LC_ALL=ru_RU.UTF-8
    LANG=ru_RU.UTF-8
    DOTNET_RUNNING_IN_CONTAINER=true
    
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

WORKDIR /app_watson_console

COPY --from=build /published/watson_console ./

WORKDIR /app_api

COPY --from=build /published/api ./
RUN mkdir data

# ====== production ======
EXPOSE 8065

ENTRYPOINT ["./Web.Api"]

And the Docker Compose file:

version: '3.9'


name: web-api
networks: 
  web-api-network: 
  
services:
  seq:
    image: datalust/seq:latest
    container_name: seq-container
    volumes:
      - /seq/data:/data
    environment:
      ACCEPT_EULA: "Y"
    ports:      
      - "15432:80" # for UI
    restart: always
    networks:
      - web-api-network

  web-api:
    image: web-api:12-05-2023-v-1
    container_name: web-api-container-v-1
    volumes:
      - /web/api/data:/app_api/data
    environment:    
      ASPNETCORE_ENVIRONMENT: "Production"
      ASPNETCORE_URLS: "http://+:8065"
      Logging__Console__FormatterName: "Simple"
      SeqSettings__SeqHost: "http://seq:5341"
      PathToWatsonLiteExecutableFile__AppPath: "/app_cas_console/Watson.App.Console"      
    ports:
      - "18065:8065"
    restart: always
    networks:
      - web-api-network
    depends_on:
      - seq

2

Answers


  1. Chosen as BEST ANSWER

    Problem solved: I start console app with Watson web server from my web api application and after Process.Start I IMMEDIATELY send query to Watson web server. But Watson does not have time to start and I get connection refused error. But if to wait for, say, 1 second before sending the query, everything works fine!


  2. Your docker container is listening on 8065 and pushing to 18065. There’s no port 9000 passed to your docker container. Try hitting port 80 for UI and 8065 for api host.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search