带有 Spring Boot 的 Docker 和 Eureka 无法注册客户端

Posted

技术标签:

【中文标题】带有 Spring Boot 的 Docker 和 Eureka 无法注册客户端【英文标题】:Docker and Eureka with Spring Boot failing to register clients 【发布时间】:2017-11-23 19:47:09 【问题描述】:

我有一个使用 Spring Boot + Docker Compose + Eureka 的非常简单的演示。

我的服务器在端口 8671 上运行,具有以下应用程序属性:

server:
  port: 8761
eureka:
  instance:
    prefer-ip-address: true
  client:
    registerWithEureka: false
    fetchRegistry: false
  server:
    waitTimeInMsWhenSyncEmpty: 0

我的 Eureka 客户端在端口 9000 上运行,具有以下应用程序属性:

server:
  port: 9000
spring:
  application:
    name: user-registration
eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true

当我在父 maven 项目中启动我的 docker.compose 文件时,这是我的 docker-compose 文件的内容:

eureka-server:
  image: rosenthal/eureka-server
ports:
   - "8761:8761"
user-registration:
  image: rosenthal/user-registration
  ports:
   - "9000:9000"
  links:
   - eureka-server

当我首先启动 eureka 服务器来运行我的应用程序时,然后是客户端通过

mvn spring-boot:run 

服务器成功注册了我的客户端(我称之为用户注册)。

当我通过 docker-compose 运行我的应用程序时,客户端无法注册并显示以下输出:

 DiscoveryClient_USER-REGISTRATION/0fd640cbc3ba:user-registration:9000: 
 registering service...
 user-registration_1  | 2017-06-21 04:36:05.120 ERROR 1 --- [nfoReplicator-0]        
 c.n.d.s.t.d.RedirectingEurekaHttpClient  : Request execution error
 user-registration_1  | 
 user-registration_1  | com.sun.jersey.api.client.ClientHandlerException: 
 java.net.ConnectException: Connection refused (Connection refused)

我的第一个假设是运行 docker-compose 在等待服务器启动时遇到了竞争条件,但我的 eureka 客户端似乎有一个心跳试图调用它配置的服务器。这意味着它无法找到我注册的 Eureka 服务器(并且正在运行,我可以在 localhost:8671 上导航到它)。

我在这里缺少什么?一切都在本地运行良好,spring-boot 使用它自己的嵌入式 tomcat 容器启动。一旦我开始使用 docker-compose,它就不想工作了。

编辑

我意识到我的问题,我相信。所以docker不在localhost上运行,它在我启动docker时分配的公共IP上运行。导航到这个 ip + 端口会显示我为 Eureka Server 运行的服务。客户端仍然没有注册。

所以,我对我的 eureka 客户端的 application.yml 文件进行了更改:

serviceUrl:
  defaultZone: http://192.168.59.103:8761/eureka/

该 IP 是我的 docker 守护程序正在运行的 IP。现在,当我执行 docker-compose 时,它​​会错过第一次注册,但第二次心跳会接收到我的客户端。

如何确保客户端等到服务器完全启动?我在我的案卷撰写文件中使用了正确的 docker“链接”字段,但它没有像我希望的那样工作。另外,我如何才能看到 defaultZone 文件是我的 DOCKER_HOST IP?

最终结果

生成的 docker-compose 文件对我来说一切正常:

eureka-server:
  image: thorrism/eureka-server
  ports:
   - "8761:8761"
user-registration:
  image: thorrism/user-registration
  ports:
   - "9000:9000"
  links:
   - eureka-server
  environment:
    EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: http://eureka-server:8761/eureka

【问题讨论】:

很好地解决了我工作了好几个小时的问题! 【参考方案1】:

设置环境属性以覆盖 eureka.client.serviceUrl.defaultZone 以匹配 docker compose 文件中的服务名称。

eureka-server:
  image: rosenthal/eureka-server
  ports:
   - "8761:8761"
user-registration:
  image: rosenthal/user-registration
  ports:
   - "9000:9000"
  environment:
   - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: http://eureka-server:8761/eureka

这将覆盖打包的application.properties 中的属性。

注意: 如 cmets 中所述,您不需要撰写文件中的 links 部分。我删除是这样的。有关这方面的信息,请参阅 https://docs.docker.com/compose/networking/。

【讨论】:

由于 docker-compose 指令为整个内容创建了一个default network,因此不需要使用链接。他们也是deprecated。 好点,但这不是问题:)。将为答案添加注释。 (我只是使用了问题中的配置并添加了环境的东西)。 是的,答案很好,只是一个旁注;-) 您的回复中的一些反馈说明:正如您所说,语法在环境中有点偏离。新的默认区域不需要“-”或引号。此外,我尝试按照您的建议删除“链接”属性(并注意作为旧版?),但直到我添加回链接标签后它才起作用。【参考方案2】:

如果对您有用,这就是我为生产环境配置它的方式(docker-compose 文件版本 2):

version: '2'
services:
  eureka-server:
    image: rosenthal/eureka-server
    expose:
    - "8761"
  user-registration:
    image: rosenthal/user-registration
    container_name: user-registration
    ports:
    - "9000:8080"
    environment:
      server.port: 8080
      eureka.client.enabled: 'true'
      eureka.host: eureka-server
      eureka.instance.preferIpAddress: 'true'

来自docs,这就是expose 所做的:

公开端口而不将它们发布到主机 - 它们只能被链接的服务访问。只能指定内部端口。

由于所有东西都在同一个网络中,因此容器可以相互看到,而它们之间没有链接。

旁注

请记住,使用此配置端口 9000 将可在主机上公开访问,并映射到用户注册容器的 8080 端口。

【讨论】:

每个微服务的容器中是否有映射到8080的端口?我仍在尝试了解使用容器背后的所有网络,并了解如何映射端口。 docker 容器的面向公众的端口是 9000(主机端口),但在容器内部,服务在 8080 下运行,对吗? @rosenthal 你说得对。 Spring Boot 应用程序的端口默认为 8080,并且您在容器中运行它 我喜欢这个但是docker环境变量如何映射到java @SabareeshKkanan 它是由 Spring Boot 完成的:docs.spring.io/spring-boot/docs/current/reference/html/…(获取环境变量)【参考方案3】:

更新:

看起来对于新版本的 spring-boot(在我的情况下为 2.4.5)这个解决方案不起作用。 EUREKA_CLIENT_SERVICEURL_DEFAULTZONE 属性不会自动拾取。 Camelcasing 存在一个永久性问题,这会造成不一致。 EUREKA_CLIENT_SERVICEURL_DEFAULTZONE 未转换为 eureka.client.serviceUrl.defaultZone。它已转换为eureka.client.serviceurl.defaultzone。请查看此Github Issue for spring-cloud-netflix 了解更多详情。他们现在不能更改参数,因为这会破坏向后兼容性。这是最终对我有用的代码

   discovery:
     container_name: discovery
     build: .
     ports:
       - "8761:8761"

   user-registration:
     container_name: user-registration
     build: .
     ports:
       - "8080:8080"
     environment:
       SPRING_APPLICATION_JSON: '"eureka":"client":"serviceUrl":"defaultZone":"http://discovery:8761/eureka"'

只有通过环境变量设置此属性,您必须使用用户SPRING_APPLICATION_JSON。这很难看,但它有效。

【讨论】:

谢谢它救了我的命。我使用了以下属性: SPRING_APPLICATION_JSON: '"eureka":"client":"serviceUrl":"defaultZone":"YOUR_EUREKA_SERVICE_NAME:8761/eureka"'【参考方案4】:

我有同样的问题,我尝试了一切,但没有奏效。 经过 2 周的反复试验,然后我尝试从存储库中删除所有依赖项,然后我确保我在所有这些中都具有相同的 netflix 客户端、服务器和 Zuul 依赖项。

然后我使用了上面的解决方案,之后它对我有用。

还请注意,您需要使用 docker compose 启动服务,然后它才能工作,否则如果您独立启动它们,那么它们都将在其容器中启动,并且不会在服务注册表中注册。希望这会有所帮助

【讨论】:

【参考方案5】:

作为@Tanbir Sagar 解决方案的替代方案,这是对我有用的解决方案:

discovery:
  container_name: discovery
  build: .
  ports:
    - "8761:8761"

user-registration:
  container_name: user-registration
  build: .
  ports:
    - "8080:8080"
  environment:
    eureka.client.serviceUrl.defaultZone: http://discovery:8761/eureka

使用 Spring Boot 2.5.6 和 Docker Compose 3 测试。

【讨论】:

以上是关于带有 Spring Boot 的 Docker 和 Eureka 无法注册客户端的主要内容,如果未能解决你的问题,请参考以下文章

带有 Spring Boot 应用程序的 docker secret 在 docker swarm 模式 /run/secrets 下不起作用

如何使用 Docker 配置带有 redis 的 Spring Boot Web 应用程序

带有 Docker 容器化 Spring Boot 应用程序的 HTTPS 不起作用

带有 Docker 的 Spring Boot 2.3。当我在同一个存储库中有多个 Spring Boot 应用程序时,我想为特定应用程序构建映像

Spring Boot + MySQL docker 容器

带有 Axon 的 Spring Boot JPA