【发布时间】:2019-06-12 13:05:21
【问题描述】:
我正在开发一个多容器 Docker 应用程序,我想要一个容器使用 Docker Compose 向其他容器的 API 发出 HTTP 请求。我收到拒绝连接错误。
两个容器都运行 ASP.NET Core 2.2。 我使用的 IDE 是 Visual Studio 2017。 我正在使用 Postman 测试 API 调用。
我尝试过的事情:
✔️ 端口暴露在 Dockerfiles
✔️ 端口在 Docker Compose 配置中声明
✔️ 网络在 Docker Compose 中声明,容器连接到它
✔️ Http 请求 URI 使用服务名称,而不是 localhost 或本地 IP 地址
✔️ Http 请求 URI 使用容器端口 (80) 而不是主机端口
✔️ 防火墙已禁用
✔️ 具有自定义子网的自定义网络
✔️ 服务的自定义 IPv4 地址
✔️ Docker Compose 降级到 v2.4(在自定义网络上指定网关)
✔️ 删除并重新创建 serviceB 项目
✔️ 从 HttpClient 的基本用法切换到类型化客户端(自定义服务)
✔️ 从 GetAsync(Uri) 切换到 GetAsync(Uri,HttpCompletionOption,CancellationToken)
Dockerfiles
FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
...
Docker Compose 配置:
version: '3.4'
services:
servicea:
...
ports:
- "51841:80"
- "44364:443"
networks:
- local
serviceb:
...
ports:
- "65112:80"
- "44359:443"
networks:
- local
networks:
local:
driver: bridge
serviceA 控制器操作:
[Route("[action]")]
[HttpGet]
public IActionResult foo()
{
HttpClient client = _clientFactory.CreateClient();
var result = client.GetAsync("http://serviceb:80/api/bar").Result;
var response = result.Content.ReadAsStringAsync().Result;
return new OkObjectResult(response);
}
如果我通过 http://localhost:51841/api/foo 的 Postman 向 serviceA(主机端口 51841)发出 Http Get 请求,我想获得 serviceB 的 Bar 操作的响应。
但我的连接被拒绝
原始异常详情:
System.Net.Http.HttpRequestException: Connection refused ---> System.Net.Sockets.SocketException: Connection refused
at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync(ValueTask`1 creationTask)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
PING
如果我访问 serviceA 的 bash 并对 serviceB(特别是 serviceB 的 :80 端口)执行 ping 操作,它可以工作:
root@serviceA_id:/app# ping -p 80 serviceb
PATTERN: 0x80
PING serviceb(10.168.0.3) 56(84) bytes of data.
64 bytes from ... (10.168.0.3): icmp_seq=1 ttl=64 time=0.117 ms
64 bytes from ... (10.168.0.3): icmp_seq=2 ttl=64 time=0.101 ms
64 bytes from ... (10.168.0.3): icmp_seq=3 ttl=64 time=0.083 ms
--- serviceb ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2057ms rtt min/avg/max/mdev = 0.083/0.100/0.117/0.016 ms
卷曲
我还可以使用 CURL 建立与 API REST 端点的连接,但收到的 Content-Length 为 0
root@serviceA_id:/app# curl -v http://serviceb/api/bar
* Trying 10.168.0.3...
* TCP_NODELAY set
* Connected to serviceb (10.168.0.3) port 80 (#0)
> GET /api/bar/ HTTP/1.1
> Host: serviceb
> User-Agent: curl/7.52.1
> Accept: */*
>
< HTTP/1.1 307 Temporary Redirect
< Date: Mon, 17 Jun 2019 07:11:31 GMT
< Server: Kestrel
< Content-Length: 0
< Location: https://serviceb:44359/api/bar/
<
* Curl_http_done: called premature == 0
* Connection #0 to host serviceb left intact
所以我们可以看到 serviceB 告诉 serviceA 它的请求将被重定向到https://serviceb:44359/api/bar/ 但所需的连接端口是 80(容器端口),而不是 44359(主机端口)
如果我让 curl 跟随重定向,则会出现 Connection Refused(重定向端口已关闭)
root@serviceA_id:/app# curl -v -L http://serviceb/api/bar
* Trying 10.168.0.3...
* TCP_NODELAY set
* Connected to serviceb (10.168.0.3) port 80 (#0)
> GET /api/bar HTTP/1.1
> Host: serviceb
> User-Agent: curl/7.52.1
> Accept: */*
>
< HTTP/1.1 307 Temporary Redirect
< Date: Wed, 19 Jun 2019 08:48:33 GMT
< Server: Kestrel
< Content-Length: 0
< Location: https://serviceb:44359/api/bar
<
* Curl_http_done: called premature == 0
* Connection #0 to host serviceb left intact
* Issue another request to this URL: 'https://serviceb:44359/api/bar'
* Trying 10.168.0.3...
* TCP_NODELAY set
* connect to 10.168.0.3 port 44359 failed: Connection refused
* Failed to connect to serviceb port 44359: Connection refused
* Closing connection 1
curl: (7) Failed to connect to serviceb port 44359: Connection refused
为什么我被重定向到主机端口?
在Startup.cs 我的服务使用app.UseHttpsRedirection(); 所以删除该行解决了问题
如果我仍然需要使用 HTTPS 怎么办? 添加选项以使用容器端口。答案中的更多信息
【问题讨论】:
-
+1m 非常感谢,我几天来一直在调整我的 docker-compose 试图克服这个连接被拒绝的错误!
-
我收到了 Connection Refused 错误并回答了您的问题。查看了您的清单并意识到我没有暴露这个新端口,谢谢!
-
与问题无关 - 但是 - 请不要在异步方法调用中使用 .Result ,使用 await - 这是不好的做法,你会为各种问题做好准备;-)跨度>
标签: c# asp.net docker .net-core docker-compose