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
Please, try providing a value for the environment variable
ASPNETCORE_URLS
when running your container.For example, in docker-compose:
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 thatlocalhost
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.
Having the same issue previously caused by port setting in Programm.cs
Please check
Interface IWebHostBuilder you might have specified specific port like this
and change it with this
Your application is listen for connection inside container (localhost) only.
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
.