无法通过Docker容器访问.Net Core应用程序

4

我对Docker比较新,但是在尝试通过Docker Compose实现时遇到了困难。

Docker Compose文件

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入口点

#!/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

启动项目设置

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

当通过Docker启动时,我的http或https端点似乎都无法访问。CLI输出完全相同,因此似乎是通过Docker运行的。有任何想法我做错了什么吗?

本地运行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:\Users\ddrob\source\Artis.Merchant.API

上述内容运行正常,我可以通过 https://localhost:5001 或者 http://localhost:5000 正常访问它

在运行 docker compose up 后 Docker 输出的结果

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

测试只需对 https://localhost:5001/api/health 进行 HTTP GET 调用,应返回 200。在本地运行正常,Docker 通过非 https 访问时返回 socket hang up,通过 https 访问时出现 client network socket disconnected before secure TLS connection was established

编辑

以下是 docker psdocker 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"
        }
    }
]

我发现检查名为artis_api-app-1的容器和名为artis_api-app的镜像时输出不同。我发现前者唯一相关的部分是网络设置输出。
        "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
                }
            }
        }

编辑2

由于一些问题,这里是我的初始Program.cs入口点。

    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>();
                });
    }

答案

除了接受的答案外,我还需要在我的 appsettings.json 文件中包含 Kestrel 配置,例如:

"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"
    }
  }

需要注意的是,您还可以在docker-compose.yml文件中将appsettings.json配置条目指定为环境变量,以便更好地使用Kestrel,例如: - ASPNETCORE_Kestrel__Endpoints__Http__Url = http://0.0.0.0:5000 同样的功能,只是展示方式不同。 - tr00st
3个回答

4
请在运行容器时为环境变量ASPNETCORE_URLS提供值。
例如,在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

这将使您的应用程序能够监听所有可用的网络接口:相反,它只能通过localhost访问,但请注意,在这种情况下,localhost指的是容器本身。
您也可以在Dockerfile中提供类似的信息。
请注意,为了支持HTTPS,我包含了关于pfx包位置和构建镜像时使用的相应密码的信息。这对于此目的是必要的:请考虑阅读提供的Microsoft文档
关于证书的最后一点说明:正如您在提到的文档中所看到的那样,应用程序使用的证书通常通过docker卷进行挂载,以避免将其包含在Docker镜像中 - 实际上是公开的。这对于POC来说很好,但请不要像这样存储生产证书和密码。

仍然没有运气。尽管进行了环境更改,我仍然得到相同的结果。不确定在从主机端尝试访问时是否应该指定端口。但我的结果是:GET http://localhost/api/health -> Error: connect ECONNREFUSED 127.0.0.1:80GET https://localhost/api/health -> Error: connect ECONNREFUSED 127.0.0.1:443GET http://localhost:5000/api/health -> Error: socket hang upGET https://localhost:5001/api/health -> Error: Client network socket disconnected before secure TLS connection was established - Dillon Drobena
我在按照你的建议重新运行之前删除了整个容器/镜像。我将记录输出并在GitHub或其他地方发布它。 - Dillon Drobena
请查看此处的输出 https://gist.github.com/dillondrobena/41073eda0ca353f4aa5cd518d19ad36e - Dillon Drobena
谢谢你分享,Dillon。请问你能提供一下 artis_api-app-1 容器的 docker inspect 输出吗? - jccampanero
抱歉,Dillon,回复晚了。也许问题可能与docker网络有关,老实说,我已经没有任何想法了。唯一看起来奇怪的是,我希望在您的控制台输出中看到类似于http://[..]:5000的内容,而不是再次出现localhost。也许您的applicationUrl以某种方式覆盖了提供的环境变量。您可以尝试的一件事是更改端口,例如,在applicationUrl或env变量ASPNET_URLS中使用5050和5051端口。另一件事您可以尝试修改您的run_cmd定义 - jccampanero
显示剩余5条评论

4

你的应用程序仅在容器内部(localhost)监听连接。

Now listening on: http://localhost:5000

为了能够通过端口进行传输,应用程序应该监听外部网络适配器。 只需监听 http://0.0.0.0:5000 即可。

谢谢!这最终成为了我的问题,还需要在我的环境文件中包含Kestrel配置。 - Dillon Drobena

1

之前在 Programm.cs 中由于端口设置导致了相同的问题。 请检查 Interface IWebHostBuilder,您可能已经指定了特定的端口,如下所示:

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

将其替换为此内容

public static IHostBuilder CreateWebHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

编辑了我的原始问题,以展示我的Program.cs文件。我在那里没有明确设置任何运行时属性。我的环境配置都在appsettings.json文件中。我的运行时属性都在launchSettings.json中。在我的docker入口点Setup.sh中,我指定使用我的启动配置文件Artis.Merchant.API,它应该将其指向http://localhost:5000https://localhost:5001 - Dillon Drobena

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接