Spring Cloud Netflix and Docker Compose - cannot register services with eureka - spring-boot

I'm trying to use docker-compose to run 2 simple services locally (Ubuntu): a eureka server, and config server (which is also a eureka client). Both of these have simple dockerfiles that run java -jar, expose their ports, and individually work fine. I also tried to add eureka.client.service-url.defaultZone=http://company-service-discovery:8761/eureka to see if it would register itself, and it worked.
My config server cannot successfully register to the eureka server, and I've googled it and nothing I've seen helped me solve this.
According to docker-compose documentation at https://docs.docker.com/compose/networking/ :
By default Compose sets up a single network for your app. Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name.
With the following example web should be able to use postgres://db:5432 to communicate with the database.
version: "3"
services:
web:
build: .
ports:
- "8000:8000"
db:
image: postgres
ports:
- "8001:5432"
I have used the same method to configure my services, but my config server gets connection refused when trying to register:
docker-compose.yml
version: '3.3'
services:
company-service-discovery:
build: company-service-discovery/
ports:
- "8761:8761"
company-config-server:
build: company-config-server/
ports:
- "8888:8888"
links:
- company-service-discovery
config server bootstrap.yml
server:
port: 8888
management:
security:
enabled: false
spring:
application:
name: company-config-server
cloud:
config:
server:
native:
search-locations: classpath:/shared
profiles:
include: native
eureka:
client:
service-url:
defaultZone: http://company-service-discovery:8761/eureka
eureka server bootstrap.yml
spring:
application:
name: company-service-discovery
server:
port: 8761
management:
security:
enabled: false
exception
2017-07-26 14:25:05.738 WARN 1 --- [nfoReplicator-0] c.n.d.s.t.d.RetryableEurekaHttpClient : Request execution failed with message: java.net.ConnectException: Connection refused (Connection refused)
2017-07-26 14:25:05.739 WARN 1 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_COMPANY-CONFIG-SERVER/365d20275ab0:company-config-server:8888 - registration failed Cannot execute request on any known server
question
Is there anything wrong with my configuration ? How can I make it work ?
Let me know if there's any info missing, I'll gladly give any info I can.

Add the defaultZone configuration to the Eureka server's properties too (and change the service-url to serviceUrl in your config server bootstrap.yml).
eureka server bootstrap.yml
spring:
application:
name: company-service-discovery
eureka:
client:
serviceUrl:
defaultZone: http://company-service-discovery:8761/eureka
server:
port: 8761
management:
security:
enabled: false

Thank you sooo much. this post resolved my problem - connecting eureka client to eureka server with docker. After 2 days of searching it work. I am in tears.
So basically -
you should use following in eureka client's application.properties/.yml
eureka.client.service-url.defaultZone=http://eureka-server:8761/eureka
and in docker-compose.yml your eureka service name should match with - the url host name, in my case it is - eureka-server

Related

Deployment issue on Heroku : zuul + eureka + microservices

Thanks for taking the time to read my post.
My issue is as such. I have 2 microservices (auth-service and image-service) deployed on Heroku, as well as a server Eureka and a gateway Zuul.
In local, all of this works.
However, when deployed, the same calls give me 404 errors : for example :
zuul is deployed on port 8762 in local,
so http://localhost:8762/images/user/create works, it points to http://localhost:8200/user/create of the image-service.
Deployed, https://croquis-time-image.herokuapp.com/user/create works, but https://croquis-time-zuul.herokuapp.com/images/user/create throws a 404 error.
On eureka however, all my services are registered, so the trouble doesn't come from there. I thought perhaps zuul didn't manage to get all the services from Eureka, but I'm not sure how to check.
More information :
application.yml of zuul deployed :
server:
port: 8762
eureka:
client:
serviceUrl:
defaultZone: https://croquis-time-eureka.herokuapp.com/eureka/
instance:
hostName: croquis-time-eureka.herokuapp.com
zuul:
#Service will be mapped under the /api URI
prefix: /api
# Uncomment to disable auto-registering all services read from Eureka
# ignoredServices: '*'
routes:
image-service:
path: /images/**
serviceId: image-service
auth-service:
path: /auth/**
serviceId: auth-service
stripPrefix: false
sensitiveHeaders: Cookie,Set-Cookie
spring:
application:
name: zuul-server
security:
jwt:
uri: /auth/**
prefix: Bearer
expiration: 86400
header: Authorization
secret: *****************
The application.yml of eureka deployed
server:
port: 8761
eureka:
instance:
hostname: croquis-time-eureka.herokuapp.com
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: https://croquis-time-eureka.herokuapp.com/eureka/
spring:
application:
name: eureka-server
The image service I'm trying to call :
server:
port: 8200
eureka:
client:
serviceUrl:
defaultZone: https://croquis-time-eureka.herokuapp.com/eureka/
instance:
hostName: croquis-time-eureka.herokuapp.com
spring:
application:
name: image-service
jpa:
showSql: true
hibernate:
ddlAuto: update
datasource:
url: jdbc:mysql://***********:3306/croquis-time
username: ******************
password: ****************
I have tried removing and adding the "ignored-services" from zuul to see if that was the problem, but it didn't change anything.
I'm very willing to edit my question with more information if you require more, I'm not sure where is the origin of the problem, so I'm a bit at a loss as to what to add.
Thanks !
you have an issue with the configuration of .yml in zuul server
Your .yml of zuul server must be:
instance:
hostName: croquis-time-zuul.herokuapp.com
And your .yml of image must be:
instance:
hostName: croquis-time-image.herokuapp.com
because instance.hostname is the host of your application acting as the eureka client. if you need more information please visit: https://blog.heroku.com/managing_your_microservices_on_heroku_with_netflix_s_eureka

Api Gateway adding "localhost" to address on docker-compose

I'm trying to deploy SpringBoot microservices using docker-compose but I'm having a problem with API Gateway.
If I run the project locally it works ok, even if I deploy project using docker-compose but API Gateway locally, it works ok, so problem has to be "dockerizing" the API Gateway service.
Doing docker logs <container> it shows:
io.netty.channel.AbstractChannel$AnnotatedConnectException: finishConnect(..) failed: Connection refused: localhost/127.0.0.1:8083
Is obvious there is a problem on host localhost/127.0.0.1. Why Gateway is trying to point a "repeated" address?.
docker-compose.yml looks like this:
version: '3.8'
services:
# more services
api-gateway:
build: ./path_to_dockerfile
depends_on:
- eureka-server
environment:
- eureka.client.serviceUrl.defaultZone=http://eureka-server:8761/eureka/
restart: always
container_name: gateway
ports:
- '9000:9000'
Dockerfile is as simple as this
FROM openjdk:8-jdk-alpine
ADD target/apigateway-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
And application.yml:
server:
port: 9000
spring:
application:
name: Api-Gateway-Service
cloud:
gateway:
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE
globalcors:
# cors config
routes:
- id: <name>-microservice
uri: http://localhost:8083
predicates:
- Path=/<path>/**
- Method=GET,POST,DELETE,PUT,OPTIONS
# more routes on different ports
eureka:
# eureka config
So, why is adding "localhost" or "127.0.0.1" and calling twice?
Thanks in advance.
I don't think Connection refused: localhost/127.0.0.1:8083 means that it was trying to add or call localhost twice. It is just the way it shows the error.
In your application.yml, try changing uri to the name you used for your microservice inside docker-compose file.
routes:
- id: <name>-microservice
uri: <YOUR_SERVICE_NAME>
I guess the problem is that docker doesn't support network communication between containers by default. You can connect to the 8083 port from your host but not another container. If so, you should create a network and contact the container and network.

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

I have a 'discovery first' setup with Eureka, Config Server, and my client.
The issue is that these 3 services start in order, but the client-server seems to register too early, and can never find config-server. I've tried a third-party library that allows a wait until config-server:8888 is available, but that doesn't always seem to work either. It's similar to a race condition.
The workaround is that if I docker restart the client-server after everything is up, it registers and finds config-server just fine.
First run of 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
When I docker restart the client:
Fetching config from server at : http://a80b001d04a7:8888/
Located environment: name=client-server, profiles=[default], label=null, version=053c8e1b14dc0281d5af0349c9b2cf012c1a346f, state=null
Not sure if my JAVA_OPTS properties aren't being set fast enough from my docker-compose.yml, or there is some networking race condition, or what. I've been going back and forth on this for too long.
My configuration is below:
Here's my 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
Here's the 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}
Here's the config-server bootstrap.yml:
server:
port: 8888
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka}
spring:
application:
name: config-server
Here's the client-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}
Edit:
Using the docker-compose wait library (https://github.com/ufoscout/docker-compose-wait), I can have the client-server wait for eureka and config to be available, then wait 90 seconds (Eureka documentation suggests that registration could take up to 90 seconds), and it seems to work consistently.
Is this an acceptable solution? Feels like a bit of a hack.
Being purist the answer to your question is NO, it is not an acceptable solution, because as it is stated here, Docker removed healthcheck from v3 on for some reason:
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.
In the same link, it is described why:
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.
Basically then, there are three options:
Use v2.1 with healhcheck. See an example here
Use v3 and a tool like wait-for-it or
dockerize as #ortomala-lokni already perfectly explained
Make your application resilient to config-server failure and able config-client to retry the connection on startup
The recommended and acceptable solution is 3). You can use Spring Retry as it is mentioned here. Find below the 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 I found an error in your spring configuration. It is fail-fast and not fast-fail.
Remember to include the following dependencies (or similar if you are using gradle):
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
You can find a very well configuration (and explanation) here taking also into account resiliency during the registering process in the 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.
But I am not a purist at all and I would not have removed some functionality people is using (it is a question of freedom). So, an alternative solution is:
If it is working for you, then go ahead
Because I do not really understand why Docker suppressed the fantastic healthcheck command from v3.
Service dependency are always tricky when using docker-compose.
Your solution is acceptable because "there is no other way".
To avoid third-part libs, this is what I do in the same scenario:
In the Dockerfile I add netcat-openbsd, a bash file I call entrypoint and the application jar and then I run the 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
The entrypoint file has the following instruction:
#!/bin/sh
while ! nc -z config 8888 ; do
echo "Waiting for upcoming Config Server"
sleep 2
done
java -jar /opt/lib/app.jar
It will delay the application start-up until your config server is up, without a specific interval.
The best solution is probably, as Carlos Cavero said, to make your application resilient to config-server failure. But you can also solve the problem by using the wait-for script from Eficode on Github.
Copy the script into your container and in your docker-compose.yml use:
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
The environment variables for CONFIGSERVER_SERVICE_NAME and CONFIGSERVER_PORT can be defined in your Docker Compose environment file.
If you need to wait for multiple services, you can merge this pull request and list all needed services in the command line parameters such as:
command: wait-for $SERVICE1_NAME $SERVICE1_PORT $SERVICE2_NAME $SERVICE2_PORT -- java $JVM_OPTIONS -jar client.war $SPRING_OPTIONS
Just a friendly tip: You should not bind Config to Eureka but the other way around -> Eureka should be Config client.

Error "Load balancer does not have available server for client" when using zuul and eureka

I have a couple of microservices on Heroku - eureka-server, Zuul server and some app services.
While I am trying to reach any of my services, for example, "service1" via Zuul gateway, Zuul is unable to forward the request to the respective service (when I'm trying to run them locally everything works fine).
I've found the following errors in Zuul logs:
com.netflix.zuul.exception.ZuulException: Forwarding error
Caused by: com.netflix.client.ClientException: Load balancer does not have the available server for the client: service1
Below are configurations of my services:
1) "zuul server" application.yml
server:
port: ${PORT:8000}
zuul:
prefix: /api
ignoredServices: '*'
routes:
service1:
path: /path_for_service1/**
serviceId: service1
strip-prefix: false
...
management:
endpoints:
web:
exposure:
include: "*"
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_URL:http://localhost:5000}/eureka/
2) "eureka server" application.yml
server:
port: ${PORT:5000}
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
3.1) "service1" application.yml
server.port=${PORT:8081}
eureka.client.service-url.defaultZone=${EUREKA_URL:http://localhost:5000}/eureka/
3.2) "service1" bootstrap.yml
spring:
application:
name: service1
All microservices are visible in eureka dashboard.
If I change zuul routes to hard-coded url it works well but it is not what I'm looking for.
zuul:
prefix: /api
ignoredServices: '*'
routes:
service1:
path: /path_for_service1/**
url: http://url_of_service_1
strip-prefix: false
Could you please help me with this issue?
Finally, I've found root cause :)
All services are registered in eureka using heroku hostname by default (like "085930c7-b893-4b34-88a7-6e37fbe7fa0f") which is not accessible outside.
But services are accessible by domain names.
So I just added domain name settings to application.properties of each service (https://blog.heroku.com/managing_your_microservices_on_heroku_with_netflix_s_eureka)
eureka:
instance:
non-secure-port: 80
hostname: ${DOMAIN_NAME}
and now it works.

How to config spring cloud oauth2 in docker container

I met some problems with micro-spring-docker , i think maybe the sso token-url is not correct.
The demo https://github.com/keryhu/micro-oauth2-docker
In local computer , sso service and auth-service works fine .
But not in docker container ,
the problem is that redirecting to auth-server Timeout .
SSO(pc-gateway service) application.yml:
security:
user:
password: none
oauth2:
client:
accessTokenUri: http://${AUTHSERVER_PORT_9999_TCP_ADDR:localhost}:9999/uaa/oauth/token
userAuthorizationUri: http://${AUTHSERVER_PORT_9999_TCP_ADDR:localhost}:9999/uaa/oauth/authorize
docker-compose.yml
eureka:
image: eureka:0.0.1-SNAPSHOT
container_name: eureka
hostname: eureka
ports:
- "8761:8761"
configserver:
image: config-server:0.0.1-SNAPSHOT
container_name: configserver
hostname: configserver
links:
- eureka
ports:
- "8888:8888"
authserver:
image: auth-server:0.0.1-SNAPSHOT
container_name: authserver
hostname: authserver
links:
- eureka
- configserver
ports:
- "9999:9999"
pcgateway:
image: pc-gateway:0.0.1-SNAPSHOT
container_name: pcgateway
hostname: pcgateway
links:
- eureka
- configserver
- authserver
ports:
- "8080:8080"
After starting in docker container :
http://192.168.99.100:8761/ showing :
Instances currently registered with Eureka
Application AMIs Availability Zones Status
AUTHSERVER n/a(1) (1) UP (1) - authserver:authserver:9999
CONFIGSERVER n/a(1) (1) UP (1) - configserver:configserver:8888
PCGATEWAY n/a(1) (1) UP (1) - pcgateway:pcgateway:8080
But when open the auth page: http://192.168.99.100:8080
It should be redirected to auth-server login page , but it opened Timeout , the Address Bar is:
http://172.17.0.4:9999/uaa/oauth/authorize?client_id=clientapp&redirect_uri=http://192.168.99.100:8080/login&response_type=code&state=cdXhfg
I don't know why , maybe the above sso tokenurl is not correct . How to resolve ?
The 172.17.0.4 IP-address is the IP-address of the authserver container on the internal (container-container) network, which is not reachable from outside the docker host (Virtual Machine).
This may be tricky, because (in this case) you need to provide the IP-address of the Virtual Machine that docker runs on, which may change, and definitely will be different in production.
If you change ${AUTHSERVER_PORT_9999_TCP_ADDR:localhost} to 192.168.99.100, it should work.
I suggest to make the IP-address (or domain) configurable using an environment-variable that you provide in the docker-compose.yml, so something like:
${DOMAIN_NAME:192.168.99.100}
Which defaults to the "standard" IP-address of the Virtual Machine. In production you can then pass the actual domain-name, or IP-address of the server your project runs on.
Note that the "link" environment variables are marked deprecated, and only will
be used on the default (bridge) network. The new linking feature won't create
these variables, but you can simply link to other containers by name. See
https://docs.docker.com/engine/userguide/networking/work-with-networks/#linking-containers-in-user-defined-networks

Resources