skip to Main Content

I’m pretty new with docker, but running up against a wall trying to get my implementation to work through Docker Compose.

Docker Compose File

version: "3.4"
services:
    app:
        build:
            context: .
            dockerfile: Dockerfile
        ports:
            - 5000:5000
            - 5001:5001

Dockerfile

# syntax=docker/dockerfile:1
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
WORKDIR /app
    
# Install EF tools      
RUN dotnet tool install --global dotnet-ef
ENV PATH="${PATH}:/root/.dotnet/tools"  

# Generate Certificates
RUN dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p TestPassword
RUN dotnet dev-certs https --trust

# Copy everything else and build
COPY . ./
COPY Setup.sh Setup.sh
RUN dotnet restore
RUN dotnet build -c Release -o out

# Build runtime image
RUN chmod +x ./Setup.sh
CMD /bin/bash ./Setup.sh

Setup.sh Entry Point

#!/bin/bash

set -e
run_cmd="dotnet run --project Artis.Merchant.API/Artis.Merchant.API.csproj --launch-profile Artis.Merchant.API"

until dotnet ef database update --project Artis.Models/Artis.Models.csproj; do
>&2 echo "SQL Server is starting up"
sleep 1
done

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

Launch Settings for Project

  "profiles": {
    "Artis.Merchant.API": {
    "commandName": "Project",
    "launchBrowser": true,
    "launchUrl": "swagger",
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "Local"
    },
  "applicationUrl": "https://localhost:5001;http://localhost:5000"
},

So both my http or https endpoints seem to be unreachable when launched through Docker. The CLI output is the exact same, so it appears to be up and running through Docker. Any ideas what I’m doing wrong?

Locally running dotnet run --project Artis.Merchant.API/Artis.Merchant.API.csproj --launch-profile Artis.Merchant.API

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[14]
      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: Local
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:UsersddrobsourceArtis.Merchant.API

The above works fine I and access it as normal through https://localhost:5001 or http://localhost:5000

Output from Docker after running docker compose up

artis_api-app-1  | info: Microsoft.Hosting.Lifetime[14]
artis_api-app-1  |       Now listening on: http://localhost:5000
artis_api-app-1  | info: Microsoft.Hosting.Lifetime[14]
artis_api-app-1  |       Now listening on: https://localhost:5001
artis_api-app-1  | info: Microsoft.Hosting.Lifetime[0]
artis_api-app-1  |       Application started. Press Ctrl+C to shut down.
artis_api-app-1  | info: Microsoft.Hosting.Lifetime[0]
artis_api-app-1  |       Hosting environment: Local
artis_api-app-1  | info: Microsoft.Hosting.Lifetime[0]
artis_api-app-1  |       Content root path: /app/Artis.Merchant.API

Been testing by just making an HTTP GET call to https://localhost:5001/api/health which should just return a 200. Works fine running locally, Docker returns either a socket hang up if accessed through non-https, and client network socket disconnected before secure TLS connection was established when accessed through https

EDIT

For more insight, here are the outputs from docker ps and docker inspect artis_api-app

CONTAINER ID   IMAGE                            COMMAND                  CREATED             STATUS          PORTS                              NAMES
4b71ffc2c5a1   artis_api-app                    "/bin/sh -c '/bin/ba…"   43 minutes ago      Up 23 minutes   0.0.0.0:5000-5001->5000-5001/tcp   artis_api-app-1
[
    {
        "Id": "sha256:505685ec26fb58cc87fe4ec5166f2b3c0978b251953f5e9d431776ad036fc839",
        "RepoTags": [
            "artis_api-app:latest"
        ],
        "RepoDigests": [],
        "Parent": "",
        "Comment": "buildkit.dockerfile.v0",
        "Created": "2022-09-14T18:40:33.7696322Z",
        "Container": "",
        "ContainerConfig": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": null,
            "Cmd": null,
            "Image": "",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null
        },
        "DockerVersion": "",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.dotnet/tools",
                "ASPNETCORE_URLS=",
                "DOTNET_RUNNING_IN_CONTAINER=true",
                "DOTNET_VERSION=6.0.9",
                "ASPNET_VERSION=6.0.9",
                "DOTNET_GENERATE_ASPNET_CERTIFICATE=false",
                "DOTNET_NOLOGO=true",
                "DOTNET_SDK_VERSION=6.0.401",
                "DOTNET_USE_POLLING_FILE_WATCHER=true",
                "NUGET_XMLDOC_MODE=skip",
                "POWERSHELL_DISTRIBUTION_CHANNEL=PSDocker-DotnetSDK-Debian-11"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "/bin/bash ./Setup.sh"
            ],
            "ArgsEscaped": true,
            "Image": "",
            "Volumes": null,
            "WorkingDir": "/app",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 6245959471,
        "VirtualSize": 6245959471,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/uqv91vlzccv2h5y9g9mzcbl2a/diff:/var/lib/docker/overlay2/dewh3h889lvrnae1qq1gaucga/diff:/var/lib/docker/overlay2/21fioiit0habui6whx5x56e4w/diff:/var/lib/docker/overlay2/xgcecdok1kdh5je5ti1xuyylx/diff:/var/lib/docker/overlay2/yxu9wlvfmwn5rp9lth9f9ff8s/diff:/var/lib/docker/overlay2/imqsg5pxz3xkzxsaucymd3xv5/diff:/var/lib/docker/overlay2/etcmf1dskml8g13hg0eeqgtrq/diff:/var/lib/docker/overlay2/3orhtkibg5z6eeosvazrsjjwx/diff:/var/lib/docker/overlay2/e903ebed1d7345de8bd1780dbb132091b2930ddaa014ef4f399ce87be1e105d4/diff:/var/lib/docker/overlay2/d49d1b24c5821a65689ef573e9f6e7f4562cc2cd7fb4d1e8f2f6144f31cbb1dc/diff:/var/lib/docker/overlay2/bbf9acaefd8441bb31972a56526870d63995056b59f59166560007d0a114b2f9/diff:/var/lib/docker/overlay2/2306e276d00d98d2aaff2af655cba48efe5b4c0c072f54b6883818a5063d2623/diff:/var/lib/docker/overlay2/7ab5f58b84e23d093901394612c6cc71f8b704f8408fcaab10ed9c2b799e71e4/diff:/var/lib/docker/overlay2/2cd518af44aa8bae9adaa6460d9c88a761f2fdccdd36b69ddcd4809d26fbb2a0/diff:/var/lib/docker/overlay2/ea6cf0ad92836e7d871430c029e19c688f5b7caeaee475f1b7fb18b215511cd9/diff:/var/lib/docker/overlay2/892c98581f39f02c87c8ca05c73b98bdc29ab6862ebb2e70ee8c7006d1f90ccf/diff",
                "MergedDir": "/var/lib/docker/overlay2/u0v4b4saadkhe84ztlc2blsnj/merged",
                "UpperDir": "/var/lib/docker/overlay2/u0v4b4saadkhe84ztlc2blsnj/diff",
                "WorkDir": "/var/lib/docker/overlay2/u0v4b4saadkhe84ztlc2blsnj/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:b45078e74ec97c5e600f6d5de8ce6254094fb3cb4dc5e1cc8335fb31664af66e",
                "sha256:5ec686cbc3c7aea55c4f80c574962f120e5f81ed0b7ccacbbde51f8d819f8247",
                "sha256:e487c4dad54c656811ed2064a764f7240bb3b5936d497ec757991f9344be616d",
                "sha256:64d665f70cc187b1c5a5c1fc8d0a4431ea0f0069c1985ce330c55523466c22b2",
                "sha256:efccb7d95dee67a40c57966066356e47a301cc6028db923ab58fe4f21564fefb",
                "sha256:b4d89feca49ab5217ab7d09079ab1f07c618570d0822ad74ccce634313cb0c91",
                "sha256:d5471ff23747a10089f58397f39c2f66c6c8937687dd2b3592ab6fe09c6756d0",
                "sha256:08affa1f86cb7d7262620e2517bc3308598db5d047135c5b7db00994d22f6701",
                "sha256:6f1bf9eb1c14548bc0b119efb283637880394c2cae2117de367238ab3b7fcb80",
                "sha256:be544cd1ea837e49241d66815afaeddfe79b3d869972f64451e3c2dcdf8c10eb",
                "sha256:7c2ba258f59487f4dacf912a4f2c0a7598e2b4082283555fd5ca127da145cdc9",
                "sha256:3bcd28ee7f58f12baceeb1ab2c097675ca35e66f3e350869a73d5bc508fcfd57",
                "sha256:11f2bbe76890bfd069e0f1b75dd4acd35dc33f071f061e5c6751e5af7723e897",
                "sha256:d5f7661f5ac2f07ba5836972af96f89358381cbb5399342ef6fd03b6306fe000",
                "sha256:5879a59c7c2aba6ffcc9535592116cc92e7efd4d7acff9f7716e1e2ac167c3e8",
                "sha256:fdedee5f422b0f13237dd54f9894c64c4323f48d7d06e10a7640991ce9dd62ce",
                "sha256:01104bf88915fea5e113a37e57b450afa15ec647816e61b17bea508082a8ee32"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

I noticed the outputs are different when I inspect artis_api-app-1 instead of the Image named artis_api-app. I figured the only relevant portion of the former is the network settings output.

        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "174a63cebce5ca8ff7c6b218a73c90e5b813caad60b008777a1fdb031c595ced",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {
                "5000/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "5000"
                    }
                ],
                "5001/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "5001"
                    }
                ]
            },
            "SandboxKey": "/var/run/docker/netns/174a63cebce5",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "",
            "Gateway": "",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "IPv6Gateway": "",
            "MacAddress": "",
            "Networks": {
                "artis_api_default": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "artis_api-app-1",
                        "app",
                        "4b71ffc2c5a1"
                    ],
                    "NetworkID": "dc60832ac2b09450067b3edeb1cd944ad9c7c4805a674da5dba456654db49125",
                    "EndpointID": "cd04c2b9261b8ec3a095a6c3db6001484e41a41ca0dd3c32a72c093cf3787e7b",
                    "Gateway": "172.22.0.1",
                    "IPAddress": "172.22.0.3",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:16:00:03",
                    "DriverOpts": null
                }
            }
        }

EDIT 2

Here is my initial Program.cs entry point since I’ve had some questions regarding that.

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.ConfigureAppConfiguration((context, configBuilder) =>
                    {
                        string assemblyName = Assembly.GetExecutingAssembly().GetName().Name;
                        string envName = context.HostingEnvironment.EnvironmentName;
                        configBuilder.Sources.Clear();
                        configBuilder.AddJsonFile($"{assemblyName}.appsettings.json", optional: false, reloadOnChange: true);
                        configBuilder.AddJsonFile($"{assemblyName}.appsettings.{envName}.json", optional: true, reloadOnChange: true);
                    });
                    webBuilder.UseStartup<Startup>();
                });
    }

Answer

Along with the accepted answer I also needed to include a Kestrel configuration in my appsettings.json file such as this:

"Kestrel": {
    "Endpoints": {
      "Http": {
        "Url":  "http://0.0.0.0:5000"
      },
      "Https": {
        "Url": "https://0.0.0.0:5001"
      }
    },
    "EndpointDefaults": {
      "Url": "https://0.0.0.0:5001",
      "Protocols": "Http1"
    }
  }

3

Answers


  1. Please, try providing a value for the environment variable ASPNETCORE_URLS when running your container.

    For example, in docker-compose:

    version: "3.4"
    services:
        app:
            build:
                context: .
                dockerfile: Dockerfile
            ports:
                - 5000:5000
                - 5001:5001
            environment:
                - ASPNETCORE_ENVIRONMENT=Local
                - ASPNETCORE_URLS=https://+:5001;http://+:5000
                # please, review the provided path, according to your
                # setup I am unsure whether it is exact or not
                - ASPNETCORE_Kestrel__Certificates__Default__Path=${HOME}/.aspnet/https/aspnetapp.pfx
                # consider use an env varible to provide the password, to avoid
                # putting under version control system sensitive information
                - ASPNETCORE_Kestrel__Certificates__Default__Password=TestPassword
    

    This will allow your app to listen in all the network interfaces available: on the contrary, it will only be accesible through localhost but be aware that localhost in that context is the container itself.

    You could provide an analogous information in the Dockerfile as well.

    Please, notice that in order to support HTTPS I included information about the location of the pfx bundle and the corresponding password you used when building the image. It is necessary for that purpose: consider read the provided Microsoft documentation.

    A final word about the certificates: as you can see in the mentioned documentation, the certificates used by the application are usually mounted through a docker volume to avoid including it in your Docker image – making it public in practice. It is fine for a POC but please, never store your production certificates and passwords like this.

    Login or Signup to reply.
  2. Having the same issue previously caused by port setting in Programm.cs
    Please check
    Interface IWebHostBuilder you might have specified specific port like this

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
          WebHost.CreateDefaultBuilder(args)
              .UseStartup<Startup>()
              .UseWebRoot("")
              .UseUrls(urls: "http://localhost:5000");
    

    and change it with this

    public static IHostBuilder CreateWebHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                    });
    
    Login or Signup to reply.
  3. Your application is listen for connection inside container (localhost) only.

    Now listening on: http://localhost:5000
    

    To be able to pass-through the port, the application should listen external network adapter. It is enough to listen http://0.0.0.0:5000.

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