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
相关产品推荐
相关产品推荐

