You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker环境下Consul服务发现返回容器ID而非主机名(Ocelot+Consul)

问题:Docker Compose + Consul + Ocelot 服务发现返回容器ID而非服务名

在Docker Compose环境中使用Consul实现服务发现时,基于Ocelot的API网关调用服务时,Consul返回的是容器ID而非预期的主机名,导致连接失败。

实际请求地址:

http://609d55ad0741:54301/api/Credential/v1/Authorize/SignIn

预期请求地址:

http://credential-api:54301/api/Credential/v1/Authorize/SignIn

已排查内容

  • 服务已在Consul中正确注册
  • docker-compose.yml已正确定义主机名
  • 从其他容器可通过ping credential-api访问该服务
  • Ocelot已配置为使用Consul进行服务发现
  • 各服务健康检查均通过

日志信息

2025-03-21 22:13:19       requestId: 0HNB8HBKSCMDC:00000001, previousRequestId: No PreviousRequestId, message: 'EndpointRateLimiting is not enabled for /api/Credential/v1/{everything}'
2025-03-21 22:13:19 info: Ocelot.Authentication.Middleware.AuthenticationMiddleware[0]
2025-03-21 22:13:19       requestId: 0HNB8HBKSCMDC:00000001, previousRequestId: No PreviousRequestId, message: 'No authentication needed for path '/api/Credential/v1/Authorize/SignIn'.'
2025-03-21 22:13:19 info: Ocelot.Authorization.Middleware.AuthorizationMiddleware[0]
2025-03-21 22:13:19       requestId: 0HNB8HBKSCMDC:00000001, previousRequestId: No PreviousRequestId, message: '/api/Credential/v1/{everything} route does not require user to be authorized'
2025-03-21 22:13:19 info: Ocelot.ServiceDiscovery.ServiceDiscoveryProviderFactory[0]
2025-03-21 22:13:19       requestId: 0HNB8HBKSCMDC:00000001, previousRequestId: No PreviousRequestId, message: 'The UseServiceDiscovery mode of the route ':credential-api:^(?i)/api/Credential/v1(?:|/.*)$' is enabled.'
2025-03-21 22:13:19 info: Ocelot.ServiceDiscovery.ServiceDiscoveryProviderFactory[0]
2025-03-21 22:13:19       requestId: 0HNB8HBKSCMDC:00000001, previousRequestId: No PreviousRequestId, message: 'Getting service discovery provider of Type 'Consul'...'
2025-03-21 22:13:19 info: Ocelot.Provider.Polly.PollyResiliencePipelineDelegatingHandler[0]
2025-03-21 22:13:19       requestId: 0HNB8HBKSCMDC:00000001, previousRequestId: No PreviousRequestId, message: 'The ResiliencePipeline`1 pipeline has detected by QoS provider for the route with downstream URL 'http://609d55ad0741:54301/api/Credential/v1/Authorize/SignIn'. Going to execute request...'
2025-03-21 22:13:19 info: Ocelot.Requester.Middleware.HttpRequesterMiddleware[0]
2025-03-21 22:13:19       requestId: 0HNB8HBKSCMDC:00000001, previousRequestId: No PreviousRequestId, message: '102 (unknown) status code of requestURI: .'
2025-03-21 22:13:19 warn: Ocelot.Responder.Middleware.ResponderMiddleware[0]
2025-03-21 22:13:19       requestId: 0HNB8HBKSCMDC:00000001, previousRequestId: No PreviousRequestId, message: 'Error Code: ConnectionToDownstreamServiceError Message: Error connecting to downstream service, exception: System.Net.Http.HttpRequestException: Connection refused (609d55ad0741:54301)
2025-03-21 22:13:19        ---> System.Net.Sockets.SocketException (111): Connection refused
2025-03-21 22:13:19          at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
2025-03-21 22:13:19          at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
2025-03-21 22:13:19          at System.Net.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|285_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken)
2025-03-21 22:13:19          at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)
2025-03-21 22:13:19          --- End of inner exception stack trace ---
2025-03-21 22:13:19          at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)
2025-03-21 22:13:19          at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2025-03-21 22:13:19          at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2025-03-21 22:13:19          at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(QueueItem queueItem)
2025-03-21 22:13:19          at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
2025-03-21 22:13:19          at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
2025-03-21 22:13:19          at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2025-03-21 22:13:19          at Ocelot.Provider.Polly.PollyResiliencePipelineDelegatingHandler.<>c__DisplayClass5_0.<SendAsync>b__2>d.MoveNext()
2025-03-21 22:13:19       --- End of stack trace from previous location ---
2025-03-21 22:13:19          at Polly.ResiliencePipeline.<>c__10`1.<ExecuteAsync>b__10_0>d.MoveNext()
2025-03-21 22:13:19       --- End of stack trace from previous location ---
2025-03-21 22:13:19          at Polly.Outcome`1.GetResultOrRethrow()
2025-03-21 22:13:19          at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
2025-03-21 22:13:19          at Ocelot.Provider.Polly.PollyResiliencePipelineDelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
2025-03-21 22:13:19          at Ocelot.Requester.TimeoutDelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
2025-03-21 22:13:19          at Ocelot.Requester.MessageInvokerHttpRequester.GetResponse(HttpContext httpContext) errors found in ResponderMiddleware. Setting error response for request path:/api/Credential/v1/Authorize/SignIn, request method: POST

Consul服务目录信息

[{"ID":"24f5f410-b0c2-cf16-b707-1af8f3909d79","Node":"609d55ad0741","Address":"172.18.0.15","Datacenter":"dc1","TaggedAddresses":{"lan":"172.18.0.15","lan_ipv4":"172.18.0.15","wan":"172.18.0.15","wan_ipv4":"172.18.0.15"},"NodeMeta":{"consul-network-segment":"","consul-version":"1.20.4"},"ServiceKind":"","ServiceID":"credential-api-http1","ServiceName":"credential-api","ServiceTags":["http1"],"ServiceAddress":"credential-api","ServiceWeights":{"Passing":1,"Warning":1},"ServiceMeta":{},"ServicePort":54301,"ServiceSocketPath":"","ServiceEnableTagOverride":false,"ServiceProxy":{"Mode":"","MeshGateway":{},"Expose":{}},"ServiceConnect":{},"ServiceLocality":null,"CreateIndex":61,"ModifyIndex":61}]

期望Consul使用Docker Compose定义的容器名/服务名注册服务,让Ocelot能正确解析并调用,避免因容器ID导致的连接失败。


解决方案

从Consul服务目录信息可以看到,ServiceAddress已经正确设置为credential-api,但Ocelot默认优先使用Consul节点的地址而非服务自身的ServiceAddress,这是问题根源。

1. 修改Ocelot配置,强制使用服务的ServiceAddress

在Ocelot的ocelot.json中,针对Consul服务发现配置添加UseServiceAddress字段并设为true:

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/Credential/v1/{everything}",
      "DownstreamScheme": "http",
      "UpstreamPathTemplate": "/api/Credential/v1/{everything}",
      "UpstreamHttpMethod": ["POST"],
      "ServiceName": "credential-api",
      "UseServiceDiscovery": true,
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      }
    }
  ],
  "GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
      "Type": "Consul",
      "Host": "consul",
      "Port": 8500,
      "UseServiceAddress": true  // 添加此行,强制使用服务注册的地址
    }
  }
}

该配置会让Ocelot直接使用Consul中服务注册的ServiceAddress,而非节点的容器ID地址。

2. 确认服务注册时的Consul配置

如果服务通过Consul客户端注册,确保注册代码显式指定ServiceAddress为Docker Compose的服务名。以.NET为例:

var consulClient = new ConsulClient(cfg => cfg.Address = new Uri("http://consul:8500"));
await consulClient.Agent.ServiceRegister(new AgentServiceRegistration
{
    ID = "credential-api-http1",
    Name = "credential-api",
    Address = "credential-api",  // 显式设置为Docker Compose服务名
    Port = 54301,
    Tags = new[] { "http1" },
    Check = new AgentServiceCheck
    {
        HTTP = "http://credential-api:54301/health",
        Interval = TimeSpan.FromSeconds(10)
    }
});

3. 验证Consul DNS服务(可选)

若上述方法无效,可尝试让Ocelot通过Consul DNS解析服务名:

  • 在docker-compose.yml中暴露Consul的DNS端口(8600)给网关容器
  • 修改Ocelot服务发现配置为DNS类型,指定Consul的DNS地址

但通常第一种方法即可解决问题,因为你的服务已经正确注册了ServiceAddress。


内容的提问来源于stack exchange,提问作者Chinh Trần

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 20:20:54