Docker and Eureka with Spring Boot failing to register clients
Solution 1
If useful for you, that's in some way how I configure it for my production environment (docker-compose file version 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'
From the docs that's what expose
does:
Expose ports without publishing them to the host machine - they’ll only be accessible to linked services. Only the internal port can be specified.
As you've got everything in the same network, containers can see each other with no links between them.
SIDE NOTE
Keep in mind that with this configuration port 9000 will be publicly accessible at the host machine, and mapped to the 8080 port of the user-registration container.
Solution 2
Set an environment property to override the eureka.client.serviceUrl.defaultZone
to match the service name in your docker compose file.
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
This will override the property from the packaged application.properties
.
NOTE: As mentioned in the comments you don't need the links
section in the compose file. I removed is as such. See https://docs.docker.com/compose/networking/ for info on that.
Solution 3
UPDATE:
It looks like for the newer versions of spring-boot (in my case 2.4.5
) this solution does not work. The EUREKA_CLIENT_SERVICEURL_DEFAULTZONE
property is not picked up automatically. There is a permanent issue with camelcasing which creates an inconsistency. EUREKA_CLIENT_SERVICEURL_DEFAULTZONE
is not converted to eureka.client.serviceUrl.defaultZone
. It's converted to eureka.client.serviceurl.defaultzone
. Please look at this Github Issue for spring-cloud-netflix for more details. They cannot change the parameter now as this will break backward compatibility. Here is the code that finally worked for me
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"}}}}'
For only this property to set through environment variable you have to user SPRING_APPLICATION_JSON
. It's ugly but it works.
rosenthal
Updated on June 28, 2022Comments
-
rosenthal almost 2 years
I have a very simple demo of using Spring Boot + Docker Compose + Eureka.
My server runs on port 8671 with the following application properties:
server: port: 8761 eureka: instance: prefer-ip-address: true client: registerWithEureka: false fetchRegistry: false server: waitTimeInMsWhenSyncEmpty: 0
My Eureka client runs on port 9000 with the following application properties:
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
When I start up my docker.compose file in the parent maven project, this is the contents of my docker-compose file:
eureka-server: image: rosenthal/eureka-server ports: - "8761:8761" user-registration: image: rosenthal/user-registration ports: - "9000:9000" links: - eureka-server
When I run my application by first starting the eureka server, following by the client via
mvn spring-boot:run
The server successfully registers my client (I call it user-registration).
When I run my application through docker-compose, the client fails to register with the following output:
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)
My first assumption was that running docker-compose ran into a race condition on waiting for the server to start, but my eureka client seems to have a heartbeat trying to call home to the server it's configured with. This means it's just not able to find the Eureka server I have registered (and is running, I can navigate to it on localhost:8671).
What am I missing here? Everything runs fine running locally with spring-boot starting up with it's own embedded tomcat containers. As soon as I start to do it with docker-compose, it doesn't want to work.
EDIT
I realized my problem, I believe. So docker doesn't run on localhost, it runs on the public IP it is assigned when I start up docker. Navigating to this ip + port shows my service running for Eureka Server. The client still doesn't register.
SO, I made changes to the application.yml file for my eureka client to:
serviceUrl: defaultZone: http://192.168.59.103:8761/eureka/
That IP is the one my docker daemon is running under. Now, it misses the first registration when I do docker-compose, but the second heartbeat picks up my client.
How can I ensure the client waits until the server is FULLY up? I used the proper docker "links" field in my docket compose file, but it didn't work as I hoped. Additionally, how can I see the defaultZone file to be my DOCKER_HOST IP?
Final result
The resulting docker-compose file that got everything working for me was:
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
-
Aritz almost 7 yearsAs the docker-compose instruction creates a default network for the whole stuff, using links should not be required. They are deprecated too.
-
M. Deinum almost 7 yearsGood point, but that wasn't the question :). Will add a note to the answer for that. (I just used the configuration from the question and added the environment stuff).
-
Aritz almost 7 yearsYeah, answer is fine, just a side note ;-)
-
rosenthal almost 7 yearsCouple of feedback notes from your response: The syntax is a bit off in the environment as you stated. No '-' or quotation marks are required for the new default zone. Additionally, I tried this by removing the "links" property as you suggest (and note as legacy?) but it didn't work until I added the links tag back.
-
rosenthal almost 7 yearsDo you have the port mapped to 8080 in the container for each microservice? I'm still trying to understand all the networking behind using containers, and knowing how to map the ports. The public facing port for the docker container is 9000 (host machine port), but internally the service is running under 8080 within the container, correct?
-
Aritz almost 7 years@rosenthal youre right. The port for spring boot aplication is 8080 by default and youre running it within a container
-
Sabareesh Kkanan over 5 yearsI like this but how does docker environment variable mapped to java
-
Aritz over 5 years@SabareeshKkanan it is done by Spring Boot: docs.spring.io/spring-boot/docs/current/reference/html/… (taking environment variables)
-
avijitshaw over 2 yearsThanks It saved my life . I used below property : SPRING_APPLICATION_JSON: '{"eureka":{"client":{"serviceUrl":{"defaultZone":"YOUR_EUREKA_SERVICE_NAME:8761/eureka"}}}}'