Spring 配置服务器无法通过 docker-compose 访问,直到客户端重新启动

Spring Config server not reachable with docker-compose until client is restarted

我有 'discovery first' Eureka、Config Server 和我的客户端设置。

问题是这3个服务是按顺序启动的,但是client-server好像注册的太早了,一直找不到config-server。我尝试了一个第三方库,它允许等待 config-server:8888 可用,但这似乎也不总是有效。它类似于竞争条件。

解决方法是,如果我 docker restart 客户端服务器在一切正常后,它会注册并找到配置服务器。

docker-compose 的第一个 运行:

Fetching config from server at : http://localhost:8888
Connect Timeout Exception on Url - http://localhost:8888. Will be trying the next url if available

当我docker restart客户:

Fetching config from server at : http://a80b001d04a7:8888/
Located environment: name=client-server, profiles=[default], label=null, version=053c8e1b14dc0281d5af0349c9b2cf012c1a346f, state=null

不确定我的 JAVA_OPTS 属性是否没有根据我的 docker-compose.yml 设置得足够快,或者存在一些网络竞争条件,或者什么。我在这个问题上来回折腾了太久了。

我的配置如下:

这是我的 docker-compose.yml:

version: '3'
services:
  eureka:
    image: eureka-server:latest
    environment:
    - "JAVA_OPTS=-DEUREKA_SERVER=http://eureka:8761/eureka"
    ports:
      - 8761:8761
  config:
    image: config-server:latest
    environment:
      - "JAVA_OPTS=-DEUREKA_SERVER=http://eureka:8761/eureka"
    depends_on:
      - eureka
    ports:
      - 8888:8888
  client:
    image: client-server:latest
    environment:
      JAVA_OPTS: -DEUREKA_SERVER=http://eureka:8761/eureka
    depends_on:
      - config
    ports:
      - 9000:9000

这是尤里卡服务器 application.yml:

server:
  port: 8761

spring:
  application:
    name: eureka-server

eureka:
  client:
    registerWithEureka: false
    fetchRegistry: false
    service-url:
      defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka}

这是配置服务器 bootstrap.yml:

server:
  port: 8888

eureka:
  client:
    serviceUrl:
      defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka}

spring:
  application:
    name: config-server

这是客户端-服务器 bootstrap.yml:

spring:
  application:
    name: client-server
  cloud:
    config:
      discovery:
        enabled: true
        serviceId: config-server
      fast-fail: true
    retry:
      max-attempts: 10000
      max-interval: 1000

eureka:
  instance:
    hostname: client-server
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka}

编辑:

使用 docker-compose 等待库 (https://github.com/ufoscout/docker-compose-wait),我可以让客户端-服务器等待 eureka 和配置可用,然后等待 90 秒(Eureka 文档建议注册最多可能需要 90 秒),而且它似乎始终如一地工作。

这是可以接受的解决方案吗?感觉有点乱。

最好的解决方案可能是 , to make your application resilient to config-server failure. But you can also solve the problem by using the wait-for script from Eficode Github。

将脚本复制到您的容器中,并在您的 docker-compose.yml 中使用:

client:
    image: client-server:latest
    environment:
      JAVA_OPTS: -DEUREKA_SERVER=http://eureka:8761/eureka
    depends_on:
      - config
    ports:
      - 9000:9000
    command: wait-for $CONFIGSERVER_SERVICE_NAME:$CONFIGSERVER_PORT -- java $JVM_OPTIONS -jar client.war $SPRING_OPTIONS

CONFIGSERVER_SERVICE_NAMECONFIGSERVER_PORT 的环境变量可以在您的 Docker Compose environment file 中定义。

如果需要等待多个服务,可以合并this pull request,在命令行参数中列出所有需要的服务如:

command: wait-for $SERVICE1_NAME $SERVICE1_PORT $SERVICE2_NAME $SERVICE2_PORT -- java $JVM_OPTIONS -jar client.war $SPRING_OPTIONS

使用 docker-compose 时,服务依赖总是很棘手。

您的解决方案可以接受,因为 "there is no other way"。 为了避免第三方库,这就是我在同一场景中所做的:

我在 Dockerfile 中添加了 netcat-openbsd,我调用了一个 bash 文件 entrypoint 和应用程序 jar,然后我 运行 entrypoint.sh。

FROM openjdk:8-jdk-alpine
RUN apk --no-cache add netcat-openbsd
COPY entrypoint.sh /opt/bin/
COPY app.jar /opt/lib/
RUN chmod 755 /opt/esusab-bi/bin/app/entrypoint.sh

入口点文件有以下指令:

#!/bin/sh

while ! nc -z config 8888 ; do
    echo "Waiting for upcoming Config Server"
    sleep 2
done

java -jar /opt/lib/app.jar

它会延迟应用程序启动,直到您的配置服务器启动,没有特定的时间间隔。

作为纯粹主义者,您问题的答案是,这不是一个可以接受的解决方案,因为正如here所述,Docker已删除healthcheck 由于某种原因从 v3 开始:

Docker have made a conscious decision not to support features that wait for containers to be in a "ready" state. They argue that applications depending on other systems should be resilient to failure.

在同一个link中,说明了原因:

The problem of waiting for a database (for example) to be ready is really just a subset of a much larger problem of distributed systems. In production, your database could become unavailable or move hosts at any time. Your application needs to be resilient to these types of failures.

To handle this, your application should attempt to re-establish a connection to the database after a failure. If the application retries the connection, it should eventually be able to connect to the database.

那么基本上就三种选择:

  1. 将 v2.1 与 healhcheck 一起使用。看一个例子 here
  2. 使用 v3 和类似 wait-for-it 的工具或 dockerize 正如@ortomala-lokni 已经完美解释的那样
  3. 使您的应用程序能够适应配置服务器故障并能够配置客户端在启动时重试连接

推荐且可接受的解决方案是 3)。您可以使用 Spring Retry as it is mentioned here。在下面找到 bootstrap.yml 配置:

spring:
  application:
    name: config-client
  profiles:
     active: dev
  cloud:
    config:
     discovery:
       enabled: true
       service-id: config-server
     fail-fast: true
     retry:
       initial-interval: 1500
       multiplier: 1.5
       max-attempts: 10000
       max-interval: 1000

eureka:
  instance:
    hostname: config-client
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka}

顺便说一句,我在您的 spring 配置中发现了一个错误。它是 fail-fast 而不是 fast-fail.

请记住包含以下依赖项(如果您使用的是 gradle,则包含类似项):

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

你可以找到一个非常好的配置(和解释)here 还考虑了在 Eureka Server 中注册过程中的弹性。

When having a microservices environment we must think of the resiliency of our environment when platform services like config-service, discovery-service are not available for a short period of time.

但我根本不是纯粹主义者,我不会删除人们正在使用的某些功能(这是自由的问题)。因此,另一种解决方案是:

If it is working for you, then go ahead

因为我真的不明白为什么 Docker 抑制了来自 v3.healthcheck 的神奇命令。

友情提示:您不应该将 Config 绑定到 Eureka,反之亦然 -> Eureka 应该是 Config 客户端。