J'ai une configuration "découverte d'abord" avec Eureka, Config Server et mon client.
Le problème est que ces 3 services démarrent dans l'ordre, mais le client-serveur semble s'enregistrer trop tôt et ne peut jamais trouver config-server. J'ai essayé une bibliothèque tierce qui permet d'attendre jusqu'à ce que config-server: 8888 soit disponible, mais cela ne semble pas toujours fonctionner non plus. C'est semblable à une condition de course.
La solution de contournement est que si je docker restart
le client-serveur une fois que tout est en place, il s'enregistre et trouve très bien config-server.
Première exécution de 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
Quand je docker restart
le client:
Fetching config from server at : http://a80b001d04a7:8888/
Located environment: name=client-server, profiles=[default], label=null, version=053c8e1b14dc0281d5af0349c9b2cf012c1a346f, state=null
Je ne sais pas si mes propriétés Java_OPTS ne sont pas définies assez rapidement à partir de mon docker-compose.yml, ou s'il y a une condition de concurrence réseau, ou quoi. Je fais des allers-retours depuis trop longtemps.
Ma configuration est ci-dessous:
Voici mon 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
Voici le eureka-server 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}
Voici le bootstrap.yml du serveur de configuration:
server:
port: 8888
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka}
spring:
application:
name: config-server
Voici le bootstrap.yml client-serveur:
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}
Éditer:
En utilisant la bibliothèque d'attente docker-compose ( https://github.com/ufoscout/docker-compose-wait ), je peux faire attendre le client-serveur pour que eureka et config soient disponibles, puis attendre 90 secondes (la documentation d'Eureka suggère que l'enregistrement pourrait prendre jusqu'à 90 secondes), et cela semble fonctionner de manière cohérente.
Est-ce une solution acceptable? On dirait un peu un hack.
Étant puriste, la réponse à votre question est [~ # ~] non [~ # ~] , ce n'est pas une solution acceptable, car comme il est indiqué ici , Docker a supprimé healthcheck
de la v3 pour une raison quelconque:
Docker a pris la décision consciente de ne pas prendre en charge les fonctionnalités qui attendent que les conteneurs soient dans un état "prêt". Ils soutiennent que les applications dépendantes d'autres systèmes devraient être résistantes aux pannes.
Dans le même lien, il est expliqué pourquoi:
Le problème d'attendre qu'une base de données (par exemple) soit prête n'est vraiment qu'un sous-ensemble d'un problème beaucoup plus important de systèmes distribués. En production, votre base de données peut devenir indisponible ou déplacer des hôtes à tout moment. Votre application doit être résiliente à ces types d'échecs.
Pour gérer cela, votre application doit tenter de rétablir une connexion à la base de données après un échec. Si l'application réessaye la connexion, elle devrait éventuellement pouvoir se connecter à la base de données.
Fondamentalement, il existe trois options:
healhcheck
. Voir un exemple iciLa solution recommandée et acceptable est 3). Vous pouvez utiliser Spring Retry comme il est mentionné ici . Trouvez ci-dessous le bootstrap.yml
configuration:
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}
BTW J'ai trouvé une erreur dans votre configuration de ressort. C'est fail-fast
et pas fast-fail
.
N'oubliez pas d'inclure les dépendances suivantes (ou similaires si vous utilisez gradle):
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
Vous pouvez trouver une très bonne configuration (et explication) ici prenant également en compte la résilience lors du processus d'enregistrement sur le serveur Eureka.
Lorsque nous avons un environnement de microservices, nous devons penser à la résilience de notre environnement lorsque les services de plate-forme tels que le service de configuration, le service de découverte ne sont pas disponibles pendant une courte période.
Mais je ne suis pas du tout puriste et je n'aurais pas supprimé certaines fonctionnalités que les gens utilisent (c'est une question de liberté). Ainsi, une solution alternative est:
Si cela fonctionne pour vous, alors allez-y
Parce que je ne comprends pas vraiment pourquoi Docker a supprimé la fantastique commande healthcheck
de la v3.
La meilleure solution est probablement, comme l'a dit Carlos Cavero , de rendre votre application résistante aux pannes du serveur de configuration. Mais vous pouvez également résoudre le problème en utilisant wait-for
script de Eficode sur Github.
Copiez le script dans votre conteneur et dans votre docker-compose.yml
utilisation:
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
Les variables d'environnement pour CONFIGSERVER_SERVICE_NAME
et CONFIGSERVER_PORT
peut être défini dans votre fichier d'environnement Docker Compose .
Si vous devez attendre plusieurs services, vous pouvez fusionner cette demande d'extraction et répertorier tous les services nécessaires dans les paramètres de ligne de commande tels que:
command: wait-for $SERVICE1_NAME $SERVICE1_PORT $SERVICE2_NAME $SERVICE2_PORT -- Java $JVM_OPTIONS -jar client.war $SPRING_OPTIONS
La dépendance aux services est toujours délicate lors de l'utilisation de docker-compose.
Votre solution est acceptable car "il n'y a pas d'autre moyen". Pour éviter les bibliothèques tierces, voici ce que je fais dans le même scénario:
Dans le Dockerfile j'ajoute netcat-openbsd
, un fichier bash que j'appelle entrypoint
et le pot d'application, puis j'exécute le point d'entrée.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
Le fichier de point d'entrée contient les instructions suivantes:
#!/bin/sh
while ! nc -z config 8888 ; do
echo "Waiting for upcoming Config Server"
sleep 2
done
Java -jar /opt/lib/app.jar
Cela retardera le démarrage de l'application jusqu'à ce que votre serveur de configuration soit opérationnel, sans intervalle spécifique.