Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot find a way to configure Eureka client with Docker swarm mode #1820

Closed
cecchisandrone opened this issue Mar 30, 2017 · 25 comments
Closed
Labels

Comments

@cecchisandrone
Copy link

When using Eureka client with Docker swarm mode, I didn't find a way to correctly configure the client to notify the correct hostname / ip. With Docker swarm mode you have at least two networks (if the container exposes a port to the public network), the ingress and the overlay network used for internal cluster communication (the correct one to use for Eureka). The problem is that, in each container, you have two interfaces eth0 and eth1; these are assigned randomically to the ingress or overlay ip thus is not possible to benefit from spring.cloud.inetutils.ignoredInterfaces property. So what happens is that randomically Eureka clients advertise the correct host configuration. Did you have any similar case to this or is there a specific Eureka configuration that can help me with that?

@spencergibb
Copy link
Member

You have to have some way to know what address to advertise. I've not used swarm before, so I can't help there.

@cecchisandrone
Copy link
Author

I have found two solutions to this problem:

  1. Specifying a custom network value in Docker swarm and using spring.cloud.inetutils.preferredNetworks property
  2. Using eureka.instance.hostName: service-name and eureka.instance.preferIpAddress: false, where service-name is the same service-name specified in the Docker compose file

What do you think about them?

@spencergibb
Copy link
Member

I think either of those things sound reasonable if you've found them to work.

@diegochavezcarro
Copy link

Excelent! using eureka.instance.hostName: service-name and eureka.instance.preferIpAddress: false worked for me!

diegochavezcarro added a commit to diegochavezcarro/spmia4 that referenced this issue Jul 5, 2017
diegochavezcarro added a commit to diegochavezcarro/spmia4 that referenced this issue Jul 5, 2017
@diegochavezcarro
Copy link

The problem with option 2 (eureka.instance.hostName: service-name and eureka.instance.preferIpAddress: false ) is that I think load balancing is being made in docker swarm. Look the following code:
discoveryClient.getServices().forEach(serviceName -> {
discoveryClient.getInstances(serviceName).forEach(instance->{
services.add( String.format("%s:%s",serviceName,instance.getUri()));
});
});
These are the results:
[
"organizationservice:http://organizationservice:8085",
"organizationservice:http://organizationservice:8085",
"organizationservice:http://organizationservice:8085",
"organizationservice:http://organizationservice:8085",
"licensingservice:http://licensingservice:8080"
]
So load balancing is not on client side.
But using option 1 instead,such as:
spring:
cloud
inetutils:
preferredNetworks:
- 192.168
- 10.0
I could see is working appropriately,these are the results:
[
"licensingservice:http://10.0.0.7:8080",
"organizationservice:http://10.0.0.11:8085",
"organizationservice:http://10.0.0.12:8085",
"organizationservice:http://10.0.0.9:8085",
"organizationservice:http://10.0.0.10:8085"
]

diegochavezcarro added a commit to diegochavezcarro/spmia4 that referenced this issue Jul 6, 2017
diegochavezcarro added a commit to diegochavezcarro/spmia4 that referenced this issue Jul 6, 2017
@ryanjbaxter
Copy link
Contributor

Does this issue need to be open anymore? Sounds like there is a solution using option 2 above

@diegochavezcarro
Copy link

In think option 1 is more adequate. With option 2 load balancing is being made in docker swarm and not in the client, so we would not have client load balancing which is one of the key advantages of using Eureka plus Ribbon.

@spencergibb
Copy link
Member

Either way, there is a configuration that works. I don't see the need for us to change anything.

@binakot
Copy link

binakot commented Nov 22, 2017

Hello everyone ✋ . Same problem with my spring-cloud (Dalston.SR3) system in docker swarm.

Option 2 (preferIpAddress: false & service-name) really breaks the client-side load balancing (eureka + ribbon). That's why I am trying to use option 1 (preferIpAddress: true & preferredNetworks).

When we run a service in docker swarm, service without public ports registers in only overlay network (by default it's 10.0.0.0/24, but we can create own docker network create with another mask, gateway, etc). When we register a service with public ports, docker swarm add this service to overlay (10.0.0.0/24 or your own) and ingress (10.255.0.0/16) networks. Overlay lets services to communicate and ingress lets to swarm's built-in load balancing between service replicas.

This is my configuration v1:

spring:
  cloud:
    inetutils:
      preferred-networks:
        - 10.0

eureka:
  instance:
    preferIpAddress: true

With v1 configuration Eureka show this:

eureka_apps_v1

Look at instance-id in last column. By default instance-id create by pattern:

${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}}

This means that spring.cloud.client.hostname is getting from ingress network while overlay is not ready.

"springCloudClientHostInfo": {
    "spring.cloud.client.hostname": "10.255.0.15",
    "spring.cloud.client.ipAddress": "10.255.0.15"
}

Eureka uses instance-id to distinct between different instances of the same application. But if we check http://localhost:8761/eureka/apps, everything is correct except instance-id:

<application>
  <name>AUTH</name>
  <instance>
    <instanceId>10.255.0.11:auth:9999</instanceId>
    <hostName>10.0.0.15</hostName>
    <app>AUTH</app>
    <ipAddr>10.0.0.15</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

<application>
  <name>TEMPLATE-SERVICE-1</name>
  <instance>
    <instanceId>10.0.0.8:template-service-1:8080</instanceId>
    <hostName>10.0.0.9</hostName>
    <app>TEMPLATE-SERVICE-1</app>
    <ipAddr>10.0.0.9</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

I just change instance-id pattern and create my configuration v2:

spring:
  cloud:
    inetutils:
      preferred-networks:
        - 10.0

eureka:
  instance:
    preferIpAddress: true
    instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}

And Eureka shows:

eureka_apps_v2

<application>
  <name>AUTH</name>
  <instance>
    <instanceId>auth:f48997c36edbc1f1acdb5fb0ff8dc533</instanceId>
    <hostName>10.0.0.9</hostName>
    <app>AUTH</app>
    <ipAddr>10.0.0.9</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

<application>
  <name>TEMPLATE-SERVICE-1</name>
  <instance>
    <instanceId>template-service-1:7b6e38be3c96a44bafced1633b9948f5</instanceId>
    <hostName>10.0.0.15</hostName>
    <app>TEMPLATE-SERVICE-1</app>
    <ipAddr>10.0.0.15</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

Okay. For now the most important feature is scaling. Lets try to run stack in docker swarm with 2 template-service-1 and 3 template-service-2:

eureka_apps_v2-2

<application>
  <name>TEMPLATE-SERVICE-1</name>
  <instance>
    <instanceId>template-service-1:3faecf171f061b2068471b3d866f4805</instanceId>
    <hostName>10.0.0.8</hostName>
    <app>TEMPLATE-SERVICE-1</app>
    <ipAddr>10.0.0.8</ipAddr>
    <status>UP</status>
    <...>
  </instance>
  <instance>
    <instanceId>template-service-1:7e29744497a4045b037c2b03f3801cfa</instanceId>
    <hostName>10.0.0.8</hostName>
    <app>TEMPLATE-SERVICE-1</app>
    <ipAddr>10.0.0.8</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

<application>
  <name>TEMPLATE-SERVICE-2</name>
  <instance>
    <instanceId>template-service-2:f10c8dab85c45e6492af905f8a6a845c</instanceId>
    <hostName>10.0.0.21</hostName>
    <app>TEMPLATE-SERVICE-2</app>
    <ipAddr>10.0.0.21</ipAddr>
    <status>UP</status>
    <...>
  </instance>
  <instance>
    <instanceId>template-service-2:ce5de6eebfb22c092d4b0a6ca85e2829</instanceId>
    <hostName>10.0.0.21</hostName>
    <app>TEMPLATE-SERVICE-2</app>
    <ipAddr>10.0.0.21</ipAddr>
    <status>UP</status>
    <...>
  </instance>
  <instance>
    <instanceId>template-service-2:87a86dafd79723fc023600073cc93614</instanceId>
    <hostName>10.0.0.21</hostName>
    <app>TEMPLATE-SERVICE-2</app>
    <ipAddr>10.0.0.21</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

Look at IP addresses of all replicas of both services - they are equal 🤕

And the last test to check run-time scaling docker service update --replicas 3 template-service-1 & docker service update --replicas 1 template-service-2:

eureka_apps_v2-3

<application>
  <name>TEMPLATE-SERVICE-1</name>
  <instance>
    <instanceId>template-service-1:6a3d376a43051ee2a1c931b3ed61fb60</instanceId>
    <hostName>10.0.0.8</hostName>
    <app>TEMPLATE-SERVICE-1</app>
    <ipAddr>10.0.0.8</ipAddr>
    <status>UP</status>
    <...>
  </instance>
  <instance>
    <instanceId>template-service-1:3faecf171f061b2068471b3d866f4805</instanceId>
    <hostName>10.0.0.8</hostName>
    <app>TEMPLATE-SERVICE-1</app>
    <ipAddr>10.0.0.8</ipAddr>
    <status>UP</status>
    <...>
  </instance>
  <instance>
    <instanceId>template-service-1:7e29744497a4045b037c2b03f3801cfa</instanceId>
    <hostName>10.0.0.8</hostName>
    <app>TEMPLATE-SERVICE-1</app>
    <ipAddr>10.0.0.8</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

<application>
  <name>TEMPLATE-SERVICE-2</name>
  <instance>
    <instanceId>template-service-2:87a86dafd79723fc023600073cc93614</instanceId>
    <hostName>10.0.0.21</hostName>
    <app>TEMPLATE-SERVICE-2</app>
    <ipAddr>10.0.0.21</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

Eureka understands that service's replicas count are changing, but...

One more time. Look at hostName and ipAddr of every instances of both services. They are equal 😣. Eureka has 3 instances of my template-service-1 with same ip 10.0.0.8 which was used on the 1th registration. Let's check out our network docker network inspect stack_default:

[
    {
        "Name": "stack_default",
        "Id": "fusf35hkd98se12cj83w60idn",
        "Created": "2017-11-22T08:14:29.363971Z",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.0.0.0/24",
                    "Gateway": "10.0.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "0f42cbc6278af4a2d7b5dcf886f3946183f8f28569f13a6f7f081b469964d34f": {
                "Name": "stack_template-service-2.2.1mk1nm3k7lkxulq3hu82ytcf6",
                "EndpointID": "a0d09bf5458eaf281867c039897056cc5b290963d6d8f4e7944780b4bc019411",
                "MacAddress": "02:42:0a:00:00:17",
                "IPv4Address": "10.0.0.23/24",
                "IPv6Address": ""
            },   
            "519586a0855dc64d041e76e3e8d647177f689b72552ee4db86fd6c5caa60e058": {
                "Name": "stack_template-service-1.3.r6p74h0lv0825pyt4j9r9rshn",
                "EndpointID": "f2da49faa39dead6a98c5b18388aed58980cc281a3dc74ca046feda765fd8af8",
                "MacAddress": "02:42:0a:00:00:1b",
                "IPv4Address": "10.0.0.27/24",
                "IPv6Address": ""
            },
            "596154a80c95f21152130099b3738259e2d87f6c66580c78e2422a339cb33135": {
                "Name": "stack_config.1.nc2kmq8no8nha00u37dgxf84k",
                "EndpointID": "34f00a19458a098d0536ecf3ec73c972294befbcda89d441e9930420e745532b",
                "MacAddress": "02:42:0a:00:00:0e",
                "IPv4Address": "10.0.0.14/24",
                "IPv6Address": ""
            },
            "6e05edd58d8b300bad4407efa3462a30e0bc678c15cb93041a516ec88419c4ac": {
                "Name": "stack_rabbitmq.1.n3nio3q50zfmhxawzac0wlpkm",
                "EndpointID": "46b7f2c24927fed77bb66625a338caf8af9581dd1161802eab80e204fb22e3c2",
                "MacAddress": "02:42:0a:00:00:10",
                "IPv4Address": "10.0.0.16/24",
                "IPv6Address": ""
            },            
            "ab62abccad3c50745d1ddfd5b44e5d320c7915e0e880caf6df94dc699138e814": {
                "Name": "stack_template-service-1.1.pe9kmnzhord5e7cqcf6asstvw",
                "EndpointID": "70bb8a08b6d1a2da1fb228e52ec1acbddb7b371b41a73c55af74115e36642201",
                "MacAddress": "02:42:0a:00:00:09",
                "IPv4Address": "10.0.0.9/24",
                "IPv6Address": ""
            },
            "da06a28ab31cefb030cd68729589f45ae82fdfbd5545b431f2cf97a8f0af7e30": {
                "Name": "stack_registry.1.if6gdn48et01zpgyssa7nfq8e",
                "EndpointID": "9dfd032f61bfe8d096e8f7413e690cd02e06d47fc88cf2a9e4477be18a96aee9",
                "MacAddress": "02:42:0a:00:00:12",
                "IPv4Address": "10.0.0.18/24",
                "IPv6Address": ""
            },
            "e788ac5083cdb8e6f33264e5cb2de58e5c9f73810286feb593a2f51ae993ea11": {
                "Name": "stack_template-service-1.2.qnlor4tcx6kt9fypadxtt7lgp",
                "EndpointID": "7591b226918a51d1f4dbe6c8b66edb14f104ca658d6c20f298e8f0bfb79b8eab",
                "MacAddress": "02:42:0a:00:00:0a",
                "IPv4Address": "10.0.0.10/24",
                "IPv6Address": ""
            },
            "ea22b9903c95c62adbf6dd916890e858fce79b7b1cc5241c78d78051aad8cc22": {
                "Name": "stack_gateway.1.y42ihbkq93f5t5spwj321amkz",
                "EndpointID": "62fb9e93c23b3c45e3fc2ee4b83410bca9e70b3ca5c27f4d39a7cfac64f522a4",
                "MacAddress": "02:42:0a:00:00:1a",
                "IPv4Address": "10.0.0.26/24",
                "IPv6Address": ""
            },
            "f694dc0cf21f774490413293a328cbf400f26f6cd6739804d71aa08c65b7a0ca": {
                "Name": "stack_auth.1.xp8tb6h1zam0luh53ucnxsus0",
                "EndpointID": "06021ee2004052bd40a5969c551f217e3db99603a0b09479e34b12d4f39a82a9",
                "MacAddress": "02:42:0a:00:00:07",
                "IPv4Address": "10.0.0.7/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4097"
        },
        "Labels": {
            "com.docker.stack.namespace": "stack"
        },
        "Peers": [
            {
                "Name": "moby-33d8cf6a608e",
                "IP": "192.168.65.2"
            }
        ]
    }
]

There are 3 replicas of template-service-1:

  • "Name": "stack_template-service-1.1.pe9kmnzhord5e7cqcf6asstvw", "IPv4Address": "10.0.0.9/24",
  • "Name": "stack_template-service-1.2.qnlor4tcx6kt9fypadxtt7lgp", "IPv4Address": "10.0.0.10/24"
  • "Name": "stack_template-service-1.3.r6p74h0lv0825pyt4j9r9rshn", "IPv4Address": "10.0.0.27/24"

Why Eureka registered service's replicas with same ip 10.0.0.12? 🤕 This IP address is not using anymore in overlay network, it was using for the 1th replica before I changed scale value. This means that Eureka remember 1th IP address of the 1th replica of any service and continue to use it for every other repicas.

What do you think, guys? How can I fix that?


UPDATE1:

I just run the stack in docker swarm. I have gateway service with single replica. When it's running first, it crashed because config server is not available yet. Then container restart again. It is okay in docker environment (restart=always and etc, u know 😄 ).

The most interesting thing is result IP address of gateway. First container got IP 10.0.0.10, but was terminated. Second container got IP 10.0.0.11 and was successfully run.

Now lets look at Eureka and network state:

<application>
  <name>GATEWAY</name>
  <instance>
    <instanceId>gateway:f1a615945d3b68bccb635f0f0166193a</instanceId>
    <hostName>10.0.0.10</hostName>
    <app>GATEWAY</app>
    <ipAddr>10.0.0.10</ipAddr>
    <status>UP</status>    
    <...>
  </instance>
</application>

But this is from docker network inspect stack_default

"03f9423b940d2fd3392212d7f99914ef710db447da7bca15a8e181a91b4665b1": {
	"Name": "stack_gateway.1.pq2s9c7p7zd6hukfr7jax80gg",
	"EndpointID": "f51b467b3da326735fba11bb603caa2a788ff0a7077057469dc0fd7c9fd1e905",
	"MacAddress": "02:42:0a:00:00:0b",
	"IPv4Address": "10.0.0.11/24",
	"IPv6Address": ""
}

And from container itself:

$ ifconfig

eth0      Link encap:Ethernet  HWaddr 02:42:0A:FF:00:0A  
          inet addr:10.255.0.10  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:2 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:116 (116.0 B)  TX bytes:0 (0.0 B)

eth1      Link encap:Ethernet  HWaddr 02:42:AC:15:00:0B  
          inet addr:172.21.0.11  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:90 errors:0 dropped:0 overruns:0 frame:0
          TX packets:10 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:4066 (3.9 KiB)  TX bytes:582 (582.0 B)

eth2      Link encap:Ethernet  HWaddr 02:42:0A:00:00:0B  
          inet addr:10.0.0.11  Bcast:0.0.0.0  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:560 errors:0 dropped:0 overruns:0 frame:0
          TX packets:749 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:78216 (76.3 KiB)  TX bytes:89291 (87.1 KiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:134 errors:0 dropped:0 overruns:0 frame:0
          TX packets:134 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1 
          RX bytes:9088 (8.8 KiB)  TX bytes:9088 (8.8 KiB)

Eureka just registered gateway with IP of the 1th failed container. Is this DNS cache or what?

If I ping gateway from itself, IP is correct:

$ hostname
gateway
$ ping gateway
PING gateway (10.0.0.11): 56 data bytes
64 bytes from 10.0.0.11: seq=0 ttl=64 time=0.053 ms

But If I ping from another container, IP is wrong (cached with 1th failed container):

$ hostname
auth
$ ping gateway
PING gateway (10.0.0.10): 56 data bytes
64 bytes from 10.0.0.10: seq=0 ttl=64 time=0.042 ms

How can I solve this problem, if I understand it correctly? Can I force the Eureka clients to send their IP addresses itself without Eureka server's DNS resolving from already invalid cache? Cheers ☕️


UPDATE2:

I found the way to specify hostname and ip for eureka clients:

eureka:
  instance:
    instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
    prefer-ip-address: true
    hostname: localhost
    ip-address: 127.0.0.1

From /eureka/apps:

<application>
  <name>CONFIG</name>
  <instance>
    <instanceId>config:3a734041536c13e20dff7b3e0c21f9ae</instanceId>
    <hostName>127.0.0.1</hostName>
    <app>CONFIG</app>
    <ipAddr>127.0.0.1</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

Now I need to find the way to set these parameters with actual IP address of the container in my overlay network.

The code below doesn't help, because InetAddress.getLocalHost() get the externally advertised FQDN for the host (it's cached in DNS too).

    @Bean
    @Autowired
    public EurekaInstanceConfigBean eurekaInstanceConfig(final InetUtils inetUtils) throws UnknownHostException { 
        final EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils);
        config.setHostname(InetAddress.getLocalHost().getHostName());
        config.setIpAddress(InetAddress.getLocalHost().getHostAddress());
        return config;
    }

Lets check all network interfaces:

Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
        for (NetworkInterface netInt : Collections.list(networkInterfaces))
            for (InetAddress inetAddress : Collections.list(netInt.getInetAddresses()))
                System.out.printf("InetAddress: %s %s\n", inetAddress.getHostName(), inetAddress.getHostAddress());
InetAddress: 3f5ba074d5f7 10.0.0.3  # ACTUAL
InetAddress: 172.21.0.3 172.21.0.3
InetAddress: 10.255.0.4 10.255.0.4
InetAddress: 10.0.0.2 10.0.0.2  # EUREKA CLIENT USE
InetAddress: 10.255.0.3 10.255.0.3
InetAddress: localhost 127.0.0.1

And lets check actual hostname of service:

$ hostname
3f5ba074d5f7
$ hostname -i
10.0.0.3

But Eureka client is using 4th row with 10.0.0.2 10.0.0.2. What is that? IP address 10.0.0.2 of what? There is no anything with this IP address docker network inspect stack_default:

[
    {
        "Name": "stack_default",
        "Id": "qm3mqslq6jnozp3h9552ymuh7",
        "Created": "2017-11-22T13:19:16.1862187Z",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.0.0.0/24",
                    "Gateway": "10.0.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "3c0f0b69977dbca3bc527c66cc800bec9d047db52eaf62d4ef92c925c59226fb": {
                "Name": "stack_rabbitmq.1.yuohjhatwvleff0y02vr4b3vs",
                "EndpointID": "c99aa7e7ba2bd61ea8d54d937173b89f62f72c20f32999a4753f23094ac5e4b2",
                "MacAddress": "02:42:0a:00:00:07",
                "IPv4Address": "10.0.0.7/24",
                "IPv6Address": ""
            },
            "3f5ba074d5f7cc1da1186ce64bd5f89d2c10b4ac786888dba8bf9e9cebc4a686": {
                "Name": "stack_config.1.oumx45sc8zwzf2emalozhnc1n",
                "EndpointID": "5f179bbafc0b2f46810deae171672fbaeaf645da94d9b01c04676f92f6340d1c",
                "MacAddress": "02:42:0a:00:00:03",
                "IPv4Address": "10.0.0.3/24",
                "IPv6Address": ""
            },
            "437a3db4e2195fec210a42d8ef87027e63e68f018c93c77a817d9d5087ffa618": {
                "Name": "stack_registry.1.j0kbsf6zn6aew8x9dbzervn8d",
                "EndpointID": "00fee061dc430885804cf73bf62ee175985383b918dbbdd33720dc2d25ab0d80",
                "MacAddress": "02:42:0a:00:00:05",
                "IPv4Address": "10.0.0.5/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4097"
        },
        "Labels": {
            "com.docker.stack.namespace": "stack"
        },
        "Peers": [
            {
                "Name": "moby-33d8cf6a608e",
                "IP": "192.168.65.2"
            }
        ]
    }
]

UPDATE3:

I did the dirty trick:

@Bean
@Autowired
public EurekaInstanceConfigBean eurekaInstanceConfig(final InetUtils inetUtils) throws IOException {
	final String hostName = System.getenv("HOSTNAME");
	String hostAddress = null;
	final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
	for (NetworkInterface netInt : Collections.list(networkInterfaces)) {
		for (InetAddress inetAddress : Collections.list(netInt.getInetAddresses())) {
			if (hostName.equals(inetAddress.getHostName())) {
				hostAddress = inetAddress.getHostAddress();
			}
		}
	}

	final EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils);
	config.setHostname(hostName);
	config.setIpAddress(hostAddress);
	return config;
}

System.getenv("HOSTNAME") returns exactly what I need: 3f5ba074d5f7. And I just find the real IP address of this container. Lets look at Eureka:

<application>
  <name>CONFIG</name>
  <instance>
    <instanceId>config:ba555440d63dcb11add385710c429f3a</instanceId>
    <hostName>10.0.0.3</hostName>
    <app>CONFIG</app>
    <ipAddr>10.0.0.3</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

Looks like working thing 🔨 Next step is check the swarm scaling and client-side load balancing...

@binakot
Copy link

binakot commented Nov 23, 2017

UPDATE4:

Day 2️⃣. I added code below in all my spring-boot apps in a cluster.

@Configuration
public class EurekaClientConfig {

    @Bean
    @Autowired
    @Profile("docker")
    public EurekaInstanceConfigBean eurekaInstanceConfig(final InetUtils inetUtils) throws IOException {
        final String hostName = System.getenv("HOSTNAME");
        String hostAddress = null;

        final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
        for (NetworkInterface netInt : Collections.list(networkInterfaces)) {
            for (InetAddress inetAddress : Collections.list(netInt.getInetAddresses())) {
                if (hostName.equals(inetAddress.getHostName())) {
                    hostAddress = inetAddress.getHostAddress();
                }

                System.out.printf("%s: %s / %s\n", netInt.getName(),  inetAddress.getHostName(), inetAddress.getHostAddress());
            }
        }

        if (hostAddress == null) {
            throw new UnknownHostException("Cannot find ip address for hostname: " + hostName);
        }

        final EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils);
        config.setHostname(hostName);
        config.setIpAddress(hostAddress);
        return config;
    }
}

Lets check config and registry services.

Config:

eth2: 979c83c891fa / 10.0.0.3
eth1: 172.21.0.3 / 172.21.0.3
eth0: 10.255.0.4 / 10.255.0.4
lo: 10.0.0.2 / 10.0.0.2
lo: 10.255.0.3 / 10.255.0.3
lo: localhost / 127.0.0.1
<application>
  <name>CONFIG</name>
  <instance>
    <instanceId>config:b9701dfcc9f70001937fc53e9c3dbebb</instanceId>
    <hostName>10.0.0.3</hostName>
    <app>CONFIG</app>
    <ipAddr>10.0.0.3</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

Registry:

eth2: b2c8b40d16de / 10.0.0.5
eth1: 172.21.0.4 / 172.21.0.4
eth0: 10.255.0.6 / 10.255.0.6
lo: 10.0.0.4 / 10.0.0.4
lo: 10.255.0.5 / 10.255.0.5
lo: localhost / 127.0.0.1
<application>
  <name>REGISTRY</name>
  <instance>
    <instanceId>registry:07942e8b1a5cb63062d0caa7da9529f9</instanceId>
    <hostName>10.0.0.5</hostName>
    <app>REGISTRY</app>
    <ipAddr>10.0.0.5</ipAddr>
    <status>UP</status>
    <...>
  </instance>
</application>

Overlay network

[
    {
        "Name": "stack_default",
        "Id": "mnln7pi1y53chqcqfl5njmap3",
        "Created": "2017-11-23T05:37:47.3809552Z",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.0.0.0/24",
                    "Gateway": "10.0.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "5210e982c5dac5f60295fe8b838c0125ef57af59ef674109aaf311976f48ea90": {
                "Name": "stack_rabbitmq.1.9ai08boa6ts8v6aanwd3fpjbv",
                "EndpointID": "32d9066c6daaee194ebd771ea959b8783d012744fd564ca791776aa43faf2d96",
                "MacAddress": "02:42:0a:00:00:07",
                "IPv4Address": "10.0.0.7/24",
                "IPv6Address": ""
            },
            "979c83c891fa39b4d1ed3c2c95de9209eba15217afde8a5f115ab68c609381fa": {
                "Name": "stack_config.1.84it2lq7j67vmxeibtvp0q7eq",
                "EndpointID": "aee5c133b60f4b21c92f4b6196ef1a29ec4803414348b2363b41e240fbf788d8",
                "MacAddress": "02:42:0a:00:00:03",
                "IPv4Address": "10.0.0.3/24",
                "IPv6Address": ""
            },
            "b2c8b40d16de987d87d6ec6cd44acb036246249c1316daf0511b7a07ad41236d": {
                "Name": "stack_registry.1.x7j9gzh4gu1id44j60d0ilx8e",
                "EndpointID": "0126dd6894d961bfe4a6f7788dc1f5a6d9b20bc53021798a5e4c41126f4053b1",
                "MacAddress": "02:42:0a:00:00:05",
                "IPv4Address": "10.0.0.5/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4097"
        },
        "Labels": {
            "com.docker.stack.namespace": "stack"
        },
        "Peers": [
            {
                "Name": "moby-e6a48a47d001",
                "IP": "192.168.65.2"
            }
        ]
    }
]

Remember, I asked about 10.0.0.2 / 10.0.0.2, 10.0.0.4 / 10.0.0.4 and so on which Eureka uses by default. Network interfaces show this one as Loopback. Actually every service in Docker Swarm has 3 loobpack and 3 eth interfaces. When we set to prefer 10.0 addresses in yaml configuration, we force to use this loopback, because the real eth hostname in hex format (979c83c891fa, b2c8b40d16de, etc).


Okay, we got correct IP addresses on Eureka server from their clients. But we lost other configurations: urls, ports and so on. That's why requests to gateway and other communications through Eureka stop working...

We need something like this:

<application>
  <name>CONFIG</name>
  <instance>
    <instanceId>config:effc6bb2ad867575d2c83a451bd1c04b</instanceId>
    <hostName>10.0.0.16</hostName>
    <app>CONFIG</app>
    <ipAddr>10.0.0.16</ipAddr>
    <status>UP</status>
    <overriddenstatus>UNKNOWN</overriddenstatus>
    <port enabled="true">8888</port>
    <securePort enabled="false">443</securePort>
    <countryId>1</countryId>
    <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
      <name>MyOwn</name>
    </dataCenterInfo>
    <leaseInfo>
      <renewalIntervalInSecs>30</renewalIntervalInSecs>
      <durationInSecs>90</durationInSecs>
      <registrationTimestamp>1511418416077</registrationTimestamp>
      <lastRenewalTimestamp>1511418416077</lastRenewalTimestamp>
      <evictionTimestamp>0</evictionTimestamp>
      <serviceUpTimestamp>1511418414924</serviceUpTimestamp>
    </leaseInfo>
    <metadata class="java.util.Collections$EmptyMap"/>
    <homePageUrl>http://10.0.0.16:8888/</homePageUrl>
    <statusPageUrl>http://10.0.0.16:8888/info</statusPageUrl>
    <healthCheckUrl>http://10.0.0.16:8888/health</healthCheckUrl>
    <vipAddress>config</vipAddress>
    <secureVipAddress>config</secureVipAddress>
    <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
    <lastUpdatedTimestamp>1511418416077</lastUpdatedTimestamp>
    <lastDirtyTimestamp>1511418265181</lastDirtyTimestamp>
    <actionType>ADDED</actionType>
  </instance>
</application>

But with new custom EurekaClientConfig configuration we have:

<application>
  <name>CONFIG</name>
  <instance>
    <instanceId>config:1d4a762c49306da5e8038bfa74dd72b9</instanceId>
    <hostName>10.0.0.7</hostName>
    <app>CONFIG</app>
    <ipAddr>10.0.0.7</ipAddr>
    <status>UP</status>
    <overriddenstatus>UNKNOWN</overriddenstatus>
    <port enabled="true">80</port>
    <securePort enabled="false">443</securePort>
    <countryId>1</countryId>
    <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
      <name>MyOwn</name>
    </dataCenterInfo>
    <leaseInfo>
      <renewalIntervalInSecs>30</renewalIntervalInSecs>
      <durationInSecs>90</durationInSecs>
      <registrationTimestamp>1511418992485</registrationTimestamp>
      <lastRenewalTimestamp>1511419172515</lastRenewalTimestamp>
      <evictionTimestamp>0</evictionTimestamp>
      <serviceUpTimestamp>1511418991404</serviceUpTimestamp>
    </leaseInfo>
    <metadata class="java.util.Collections$EmptyMap"/>
    <homePageUrl>http://10.0.0.7:80/</homePageUrl>
    <statusPageUrl>http://10.0.0.7:80/info</statusPageUrl>
    <healthCheckUrl>http://10.0.0.7:80/health</healthCheckUrl>
    <vipAddress>config</vipAddress>
    <secureVipAddress>config</secureVipAddress>
    <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
    <lastUpdatedTimestamp>1511418992485</lastUpdatedTimestamp>
    <lastDirtyTimestamp>1511418869926</lastDirtyTimestamp>
    <actionType>ADDED</actionType>
  </instance>
</application>

UPDATE5:

I override bean from autoconfiguration org.springframework.cloud.netflix.eureka. EurekaClientAutoConfiguration#eurekaInstanceConfigBean() like this:

@Configuration
@EnableConfigurationProperties
@Profile("docker")
public class EurekaClientConfig {

    private ConfigurableEnvironment env;
    private RelaxedPropertyResolver propertyResolver;

    public EurekaClientConfig(final ConfigurableEnvironment env) {
        this.env = env;
        this.propertyResolver = new RelaxedPropertyResolver(env);
    }

    @Bean
    @Primary
    public EurekaInstanceConfigBean eurekaInstanceConfigBean(final InetUtils inetUtils) throws IOException {
        final String hostName = System.getenv("HOSTNAME");
        String hostAddress = null;

        final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
        for (NetworkInterface netInt : Collections.list(networkInterfaces)) {
            for (InetAddress inetAddress : Collections.list(netInt.getInetAddresses())) {
                if (hostName.equals(inetAddress.getHostName())) {
                    hostAddress = inetAddress.getHostAddress();
                }

                System.out.printf("Inet %s: %s / %s\n", netInt.getName(),  inetAddress.getHostName(), inetAddress.getHostAddress());
            }
        }
        if (hostAddress == null) {
            throw new UnknownHostException("Cannot find ip address for hostname: " + hostName);
        }

        final int nonSecurePort = Integer.valueOf(propertyResolver.getProperty("server.port", propertyResolver.getProperty("port", "8080")));

        final EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
        instance.setHostname(hostName);
        instance.setIpAddress(hostAddress);
        instance.setNonSecurePort(nonSecurePort);
        System.out.println(instance);
        return instance;
    }
}

And now I got next Eureka client registration:

<application>
  <name>CONFIG</name>
  <instance>
    <instanceId>config:a1b85f942f8d075984ff6ca490b476b1</instanceId>
    <hostName>10.0.0.5</hostName>
    <app>CONFIG</app>
    <ipAddr>10.0.0.5</ipAddr>
    <status>UP</status>
    <overriddenstatus>UNKNOWN</overriddenstatus>
    <port enabled="true">8888</port>
    <securePort enabled="false">443</securePort>
    <countryId>1</countryId>
    <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
      <name>MyOwn</name>
    </dataCenterInfo>
    <leaseInfo>
      <renewalIntervalInSecs>30</renewalIntervalInSecs>
      <durationInSecs>90</durationInSecs>
      <registrationTimestamp>1511430156036</registrationTimestamp>
      <lastRenewalTimestamp>1511430252197</lastRenewalTimestamp>
      <evictionTimestamp>0</evictionTimestamp>
      <serviceUpTimestamp>1511430131155</serviceUpTimestamp>
    </leaseInfo>
    <metadata class="java.util.Collections$EmptyMap"/>
    <homePageUrl>http://10.0.0.5:8888/</homePageUrl>
    <statusPageUrl>http://10.0.0.5:8888/info</statusPageUrl>
    <healthCheckUrl>http://10.0.0.5:8888/health</healthCheckUrl>
    <vipAddress>config</vipAddress>
    <secureVipAddress>config</secureVipAddress>
    <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
    <lastUpdatedTimestamp>1511430156036</lastUpdatedTimestamp>
    <lastDirtyTimestamp>1511430100452</lastDirtyTimestamp>
    <actionType>ADDED</actionType>
  </instance>
</application>

And from container:

$ hostname
7aa5c788c463
$ hostname -i
10.0.0.5

It is exactly what I want ☕️
I need to test docker swarm scaling with replicas and my problem looks solved.


UPDATE6:

Finish 🥇
I got this works. Docker swarm service replications and Eureka are friends now.

eureka-final

<application>
  <name>TEMPLATE-SERVICE-1</name>
  <instance>
    <instanceId>template-service-1:431109dab3de8f77c471dc2719ba9c28</instanceId>
    <hostName>10.0.0.3</hostName>
    <app>TEMPLATE-SERVICE-1</app>
    <ipAddr>10.0.0.3</ipAddr>
    <status>UP</status>
    <overriddenstatus>UNKNOWN</overriddenstatus>
    <port enabled="true">8080</port>
    <securePort enabled="false">443</securePort>
    <countryId>1</countryId>
    <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
      <name>MyOwn</name>
    </dataCenterInfo>
    <leaseInfo>
      <renewalIntervalInSecs>30</renewalIntervalInSecs>
      <durationInSecs>90</durationInSecs>
      <registrationTimestamp>1511433565078</registrationTimestamp>
      <lastRenewalTimestamp>1511434508066</lastRenewalTimestamp>
      <evictionTimestamp>0</evictionTimestamp>
      <serviceUpTimestamp>1511433517989</serviceUpTimestamp>
    </leaseInfo>
    <metadata class="java.util.Collections$EmptyMap"/>
    <homePageUrl>http://10.0.0.3:8080/</homePageUrl>
    <statusPageUrl>http://10.0.0.3:8080/info</statusPageUrl>
    <healthCheckUrl>http://10.0.0.3:8080/health</healthCheckUrl>
    <vipAddress>template-service-1</vipAddress>
    <secureVipAddress>template-service-1</secureVipAddress>
    <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
    <lastUpdatedTimestamp>1511433565078</lastUpdatedTimestamp>
    <lastDirtyTimestamp>1511433517697</lastDirtyTimestamp>
    <actionType>ADDED</actionType>
  </instance>
  <instance>
    <instanceId>template-service-1:2bef4192a3106139a306e9ad07fdeac2</instanceId>
    <hostName>10.0.0.18</hostName>
    <app>TEMPLATE-SERVICE-1</app>
    <ipAddr>10.0.0.18</ipAddr>
    <status>UP</status>
    <overriddenstatus>UNKNOWN</overriddenstatus>
    <port enabled="true">8080</port>
    <securePort enabled="false">443</securePort>
    <countryId>1</countryId>
    <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
      <name>MyOwn</name>
    </dataCenterInfo>
    <leaseInfo>
      <renewalIntervalInSecs>30</renewalIntervalInSecs>
      <durationInSecs>90</durationInSecs>
      <registrationTimestamp>1511434087896</registrationTimestamp>
      <lastRenewalTimestamp>1511434508066</lastRenewalTimestamp>
      <evictionTimestamp>0</evictionTimestamp>
      <serviceUpTimestamp>1511434087833</serviceUpTimestamp>
    </leaseInfo>
    <metadata class="java.util.Collections$EmptyMap"/>
    <homePageUrl>http://10.0.0.18:8080/</homePageUrl>
    <statusPageUrl>http://10.0.0.18:8080/info</statusPageUrl>
    <healthCheckUrl>http://10.0.0.18:8080/health</healthCheckUrl>
    <vipAddress>template-service-1</vipAddress>
    <secureVipAddress>template-service-1</secureVipAddress>
    <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
    <lastUpdatedTimestamp>1511434087896</lastUpdatedTimestamp>
    <lastDirtyTimestamp>1511434087766</lastDirtyTimestamp>
    <actionType>ADDED</actionType>
  </instance>
</application>

<application>
  <name>TEMPLATE-SERVICE-2</name>
  <instance>
    <instanceId>template-service-2:4d066c17e11121460efb29fc82facd34</instanceId>
    <hostName>10.0.0.5</hostName>
    <app>TEMPLATE-SERVICE-2</app>
    <ipAddr>10.0.0.5</ipAddr>
    <status>UP</status>
    <overriddenstatus>UNKNOWN</overriddenstatus>
    <port enabled="true">8080</port>
    <securePort enabled="false">443</securePort>
    <countryId>1</countryId>
    <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
      <name>MyOwn</name>
    </dataCenterInfo>
    <leaseInfo>
      <renewalIntervalInSecs>30</renewalIntervalInSecs>
      <durationInSecs>90</durationInSecs>
      <registrationTimestamp>1511433565079</registrationTimestamp>
      <lastRenewalTimestamp>1511434506703</lastRenewalTimestamp>
      <evictionTimestamp>0</evictionTimestamp>
      <serviceUpTimestamp>1511433517053</serviceUpTimestamp>
    </leaseInfo>
    <metadata class="java.util.Collections$EmptyMap"/>
    <homePageUrl>http://10.0.0.5:8080/</homePageUrl>
    <statusPageUrl>http://10.0.0.5:8080/info</statusPageUrl>
    <healthCheckUrl>http://10.0.0.5:8080/health</healthCheckUrl>
    <vipAddress>template-service-2</vipAddress>
    <secureVipAddress>template-service-2</secureVipAddress>
    <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
    <lastUpdatedTimestamp>1511433565079</lastUpdatedTimestamp>
    <lastDirtyTimestamp>1511433516707</lastDirtyTimestamp>
    <actionType>ADDED</actionType>
  </instance>
  <instance>
    <instanceId>template-service-2:8f4e5399cb6c20d0384c1ac4327bf8da</instanceId>
    <hostName>10.0.0.20</hostName>
    <app>TEMPLATE-SERVICE-2</app>
    <ipAddr>10.0.0.20</ipAddr>
    <status>UP</status>
    <overriddenstatus>UNKNOWN</overriddenstatus>
    <port enabled="true">8080</port>
    <securePort enabled="false">443</securePort>
    <countryId>1</countryId>
    <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
      <name>MyOwn</name>
    </dataCenterInfo>
    <leaseInfo>
      <renewalIntervalInSecs>30</renewalIntervalInSecs>
      <durationInSecs>90</durationInSecs>
      <registrationTimestamp>1511434220315</registrationTimestamp>
      <lastRenewalTimestamp>1511434519656</lastRenewalTimestamp>
      <evictionTimestamp>0</evictionTimestamp>
      <serviceUpTimestamp>1511434219932</serviceUpTimestamp>
    </leaseInfo>
    <metadata class="java.util.Collections$EmptyMap"/>
    <homePageUrl>http://10.0.0.20:8080/</homePageUrl>
    <statusPageUrl>http://10.0.0.20:8080/info</statusPageUrl>
    <healthCheckUrl>http://10.0.0.20:8080/health</healthCheckUrl>
    <vipAddress>template-service-2</vipAddress>
    <secureVipAddress>template-service-2</secureVipAddress>
    <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
    <lastUpdatedTimestamp>1511434220315</lastUpdatedTimestamp>
    <lastDirtyTimestamp>1511434219577</lastDirtyTimestamp>
    <actionType>ADDED</actionType>
  </instance>
  <instance>
    <instanceId>template-service-2:96290f047823d00e46175f55b90c9cb2</instanceId>
    <hostName>10.0.0.19</hostName>
    <app>TEMPLATE-SERVICE-2</app>
    <ipAddr>10.0.0.19</ipAddr>
    <status>UP</status>
    <overriddenstatus>UNKNOWN</overriddenstatus>
    <port enabled="true">8080</port>
    <securePort enabled="false">443</securePort>
    <countryId>1</countryId>
    <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
      <name>MyOwn</name>
    </dataCenterInfo>
    <leaseInfo>
      <renewalIntervalInSecs>30</renewalIntervalInSecs>
      <durationInSecs>90</durationInSecs>
      <registrationTimestamp>1511434221171</registrationTimestamp>
      <lastRenewalTimestamp>1511434520412</lastRenewalTimestamp>
      <evictionTimestamp>0</evictionTimestamp>
      <serviceUpTimestamp>1511434220659</serviceUpTimestamp>
    </leaseInfo>
    <metadata class="java.util.Collections$EmptyMap"/>
    <homePageUrl>http://10.0.0.19:8080/</homePageUrl>
    <statusPageUrl>http://10.0.0.19:8080/info</statusPageUrl>
    <healthCheckUrl>http://10.0.0.19:8080/health</healthCheckUrl>
    <vipAddress>template-service-2</vipAddress>
    <secureVipAddress>template-service-2</secureVipAddress>
    <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
    <lastUpdatedTimestamp>1511434221171</lastUpdatedTimestamp>
    <lastDirtyTimestamp>1511434220463</lastDirtyTimestamp>
    <actionType>ADDED</actionType>
  </instance>
</application>
[
    {
        "Name": "stack_default",
        "Id": "vyei6hxull9le7ajma5n6r8gf",
        "Created": "2017-11-23T10:34:18.6336376Z",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.0.0.0/24",
                    "Gateway": "10.0.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "189f0ae2a2924ac45d844c957d9668030381f51a522c4d782c9c6806fc1d968b": {
                "Name": "stack_rabbitmq.1.lrpow8l5lxy051jsl1nhcxaje",
                "EndpointID": "990376d02a606c1a8bd7a61018321c6a99586245e0b233a40ba60d2877d76b86",
                "MacAddress": "02:42:0a:00:00:07",
                "IPv4Address": "10.0.0.7/24",
                "IPv6Address": ""
            },
            "1fef95280748ec36508c7b6d52a156f1332476211a9d7f5fb22e4d5e1f9e21d8": {
                "Name": "stack_template-service-2.3.2anjsglhw44jpxp47relufvv1",
                "EndpointID": "aa20fdd4d8cb783bb6eda8f191f4039f33464f94a43fec6f1a985514c285a32a",
                "MacAddress": "02:42:0a:00:00:14",
                "IPv4Address": "10.0.0.20/24",
                "IPv6Address": ""
            },
            "2a3af4cee6c8b14143c2408358aaebf0251fe3525fd6fc3a97c755518848c576": {
                "Name": "stack_monitor.1.rczns5q61fmflmwkmm2npl3iz",
                "EndpointID": "e70f108a4fff31a1445387bd549708b2e4a8a30d30e8173a3a5e083a61d4a860",
                "MacAddress": "02:42:0a:00:00:11",
                "IPv4Address": "10.0.0.17/24",
                "IPv6Address": ""
            },
            "6d06a459037fbed038b978353f4bd7838dcf049da35594a325cad6821aac7400": {
                "Name": "stack_registry.1.yecxlmhaezmnt2k76gzuw26jm",
                "EndpointID": "e6ee97f92c281b8a5cc64d800a474522c44daa5869dd5e01b0dce4746d557ccd",
                "MacAddress": "02:42:0a:00:00:0b",
                "IPv4Address": "10.0.0.11/24",
                "IPv6Address": ""
            },
            "a26eaa9094bef35a237d732c48f1771103f4874b62453a2bf98d7ba2da6b7653": {
                "Name": "stack_template-service-1.1.qpqya9b2ld8f5fl6if6evit0j",
                "EndpointID": "bcf7b2e685ee471652056963f9eb0d9e351ba969b14f813705a34c96e22931fc",
                "MacAddress": "02:42:0a:00:00:03",
                "IPv4Address": "10.0.0.3/24",
                "IPv6Address": ""
            },
            "aae4495d4012f9321fd1d69af5da3708b201e9bf95dea387a26d9865984cd0f7": {
                "Name": "stack_template-service-2.1.s6nvdc8cywj766t6h3wxssaam",
                "EndpointID": "48fb0ea7e8ad706a2f2a14c08b6ea1c16dabbb5664e86c449ed447d725c26d89",
                "MacAddress": "02:42:0a:00:00:05",
                "IPv4Address": "10.0.0.5/24",
                "IPv6Address": ""
            },
            "b84a6706b89d0d2ee2784385849ba3b7d094391cb8e6e445464f0b6f6e12e1a0": {
                "Name": "stack_config.1.6q03r8grjvq05ak9g0jgga3zg",
                "EndpointID": "c07ff9a1f5d5c302a0ca65104515994c004c4dbe1761ac12bc29a17ddedb3df9",
                "MacAddress": "02:42:0a:00:00:09",
                "IPv4Address": "10.0.0.9/24",
                "IPv6Address": ""
            },
            "cec008adc2e31257ab73ff495c709973e90ee54175c63947d0cf2b3ceec2734f": {
                "Name": "stack_auth.1.e3rw5edvumgzf3kq5078yvwsn",
                "EndpointID": "e79e03c37672cff86e5647e389bdcb50fc517f42d1d7561938743126e9995160",
                "MacAddress": "02:42:0a:00:00:0d",
                "IPv4Address": "10.0.0.13/24",
                "IPv6Address": ""
            },
            "d28b064353ba9776c0703eda529db61d47c1fb065e0494533fbfea7850e35544": {
                "Name": "stack_template-service-2.2.trxb2kaxg8uqvosf4sp887hcu",
                "EndpointID": "80ec6a0b57bdbec7b43b5e0784347292b9b5096384a0b49adbb47b9786b057cd",
                "MacAddress": "02:42:0a:00:00:13",
                "IPv4Address": "10.0.0.19/24",
                "IPv6Address": ""
            },
            "e3d0db794f525828b530f64900365bac9ff06bbd30cccd135556d0c8521c610b": {
                "Name": "stack_template-service-1.2.vl038gbz29jrralvhkp80ed7j",
                "EndpointID": "44d6fb2306db9ab936efbd7fb2c84e5691cf1ed4e845d178cff28c4db3ef030a",
                "MacAddress": "02:42:0a:00:00:12",
                "IPv4Address": "10.0.0.18/24",
                "IPv6Address": ""
            },
            "eec959f70e20d3c1ee4ee709ba6034d2d69a950628722c7d1435c3392d58ccaa": {
                "Name": "stack_gateway.1.r6expujs6l8q26htayzxc49ut",
                "EndpointID": "de7b895408d84eca063ee7ce032ac372ddbfc1e6d07881fc150e9935184eb9e8",
                "MacAddress": "02:42:0a:00:00:0f",
                "IPv4Address": "10.0.0.15/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4097"
        },
        "Labels": {
            "com.docker.stack.namespace": "stack"
        },
        "Peers": [
            {
                "Name": "moby-e6a48a47d001",
                "IP": "192.168.65.2"
            }
        ]
    }
]

Even Hystrix Dashboard understands replicas count via Feign commucations:

hystrix

At this moment I think I solved my problem with Spring Cloud + Docker Swarm. If not, I let you know 😄

@binakot
Copy link

binakot commented Nov 23, 2017

Also if u don't wanna fight with Eureka, client-side load balancing and other things in Docker Swarm, it can work with other discovery backends ☺️ https://docs.docker.com/swarm/discovery

@meatfly
Copy link

meatfly commented Nov 29, 2017

thank you @binakot
for you soluion. I have one extension, if you want to use eureka in HA and then provide eureka client all overlay ipaddress you can get it from dns request tasks. {{SwarmEurekaServiceName}}. Eureka in HA need eureka.client.defaultZone.serviceUrl of another replica, so this can be done like this:

@Bean
    @Primary
    EurekaClientConfigBean eurekaClientConfigBean() {
        EurekaClientConfigBean client = new EurekaClientConfigBean();
        if ("bootstrap".equals(propertyResolver.getProperty("spring.config.name"))) {
            // We don't register during bootstrap by default, but there will be another
            // chance later.
            client.setRegisterWithEureka(false);
        } else {
            String serviceName = propertyResolver.getProperty("eureka.client.swarmServiceName");
            String eurekaPort = propertyResolver.getProperty("eureka.client.servicePort");
            String myHostAddress = findHostAddress();

            List<String> eurekaIps = getDnsIps("tasks." + serviceName);
            //remove my ip maybe Iam eureka app
            eurekaIps.remove(myHostAddress);

            Map<String, String> eurekaServiceUrls = new HashMap<>();
            for (String eurekaServiceUrl : eurekaIps) {
                String url = "http://" + eurekaServiceUrl + ":" + eurekaPort + "/eureka/";
                eurekaServiceUrls.put("defaultZone", url);
                log.info("eureka service url" + url);
            }
            client.setServiceUrl(eurekaServiceUrls);
        }
        return client;
    }

    private static List<String> getDnsIps(String host) {
        List<String> ips = new ArrayList<>();
        try {
            for (InetAddress inetAddress : InetAddress.getAllByName(host)) {
                String hostAddress = inetAddress.getHostAddress();
                ips.add(hostAddress);
            }
        } catch (UnknownHostException e) {
            throw new RuntimeException("error", e);
        }
        return ips;
    }

    private static String getHostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            e.printStackTrace();
            throw new RuntimeException("error", e);
        }
    }

    private String findHostAddress() {
        String hostAddress = null;
        String hostName = getHostName();

        try {
            final Enumeration<NetworkInterface> networkInterfaces;
            networkInterfaces = NetworkInterface.getNetworkInterfaces();
            for (NetworkInterface netInt : Collections.list(networkInterfaces)) {
                for (InetAddress inetAddress : Collections.list(netInt.getInetAddresses())) {
                    if (hostName.equals(inetAddress.getHostName())) {
                        hostAddress = inetAddress.getHostAddress();
                    }
                    log.info("inet {} : {} / {}", netInt.getName(), inetAddress.getHostName(), inetAddress.getHostAddress());
                }
            }
            if (hostAddress == null) {
                throw new IllegalStateException("Cannot find ip address for hostname: " + hostName);
            }
        } catch (SocketException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return hostAddress;
    }

eureka.client.servicePort and eureka.client.swarmServiceName must be set

@binakot
Copy link

binakot commented Nov 29, 2017

@meatfly Thank you 👍. You are right. At this moment I'm using Eureka with standalone mode in testing environment. The HA Eureka configuration will be my next step for the stage/production environment. I'll try to use your code snippets. When it will work, I write here my solution and experience.

@lostsquirrel
Copy link

lostsquirrel commented Dec 21, 2017

try run service with --endpoint-mode dnsrr, it works for me. BTW, I'm using a user define overlay network for all my services

@binakot
Copy link

binakot commented Dec 21, 2017

If you configure a service to use DNS round-robin (DNSRR) service discovery, there is not a single virtual IP. Instead, Docker sets up DNS entries for the service such that a DNS query for the service name returns a list of IP addresses, and the client connects directly to one of these.

DNS round-robin is useful in cases where you want to use your own load balancer, such as HAProxy. To configure a service to use DNSRR, use the flag --endpoint-mode dnsrr when creating a new service or updating an existing one.

I use docker stack to deploy my services with user define overlay too. But how DNSRR can help to Eureka registration? With DNS ribbon's client-side load balancing won't work.

Service discovery is the mechanism Docker uses to route a request from your service’s external clients to an individual swarm node, without the client needing to know how many nodes are participating in the service or their IP addresses or ports.

The problem is in internal communications, when one service inside the cluster call the other one in the cluster. Server-side and Docker Swarm built-in load balancing doesn't work for internal commucations.

@lostsquirrel
Copy link

I haven't figure out why does this is work, but at first I had the same problem to yours, I deploy a service with 3 replacs but eureka only have one, then I add flag --endpoint-mode dnsrr , eureka has all the three, you may have try and tell me the results

@Ryaryu
Copy link

Ryaryu commented Jan 3, 2018

This solution also seems to correct the problem, at least for me.

@dhanvi
Copy link
Contributor

dhanvi commented Jan 19, 2018

This answer (https://stackoverflow.com/a/45233417/3514300) is working for me.

@dhanvi
Copy link
Contributor

dhanvi commented Jan 19, 2018

#2084

@efranceschi
Copy link

After a few weeks trying to solve this situation, I finally get it to work! I don't know if this is the best way to handle, but worked for me.

I solve the issue deploying the Eureka Server like this:

version: '3.4'
services:
  eureka-1:
    image: xxxxx
    networks:
      my-net:
        aliases:
          - eureka
  eureka-2:
    image: xxxxx
    networks:
      my-net:
        aliases:
          - eureka
  eureka-3:
    image: xxxxx
    networks:
      my-net:
        aliases:
          - eureka
networks:
  my-net:
    driver: overlay

The aliases force an alternative host name to bind the ip address to the my-net network. So eureka server will resolve to 3 different IPs (using round robbin) and that way I could mark the correct interface (I've multiple interfaces in containers: db, elk, kafka, etc).

Now we need the eureka clients to use the same network to "talk to" eureka server:

version: '3.4'
services:
  my-client:
    image: xxxxx
    networks:
      - my-net
    environment:
      eureka.client.serviceUrl.defaultZone: http://eureka:8761/eureka

Note that my-net network is the same we bind the eureka alias before. Also, the service url points to the alias eureka (RR) instead of directly to the host.

To identify the correct interface, I'm using the SubnetUtils class from commons-net to check when the IP address from eureka client matches the IP address from eureka server. When both IPs has been in the same subnet, we found the correct IP address and hostname to register:

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.util.SubnetUtils;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.cloud.netflix.eureka.metadata.ManagementMetadataProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.ConfigurableEnvironment;

import java.net.*;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

@Profile("docker")
@Configuration
@Slf4j
public class DockerEurekaClientConfiguration extends EurekaClientAutoConfiguration {

	public DockerEurekaClientConfiguration(ConfigurableEnvironment env) {
		super(env);
	}

	@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
	@Bean
	@Primary
	public EurekaInstanceConfigBean eurekaInstanceConfigBean(
			InetUtils inetUtils,
			ManagementMetadataProvider managementMetadataProvider) throws MalformedURLException {
		EurekaInstanceConfigBean defaultResult = super.eurekaInstanceConfigBean(inetUtils, managementMetadataProvider);
		EurekaInstanceConfigBean result = null;
		try {
			List<String> servers = eurekaClientConfigBean().getEurekaServerServiceUrls(null);
			Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
			external_loop:
			for (NetworkInterface networkInterface : Collections.list(networkInterfaces)) {
				if (networkInterface.getName().startsWith("lo")) {
					continue;
				}
				for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
					if (interfaceAddress.getAddress() instanceof Inet4Address) {
						log.info(
								"Interface {}: {}/{}",
								networkInterface.getName(),
								interfaceAddress.getAddress(),
								interfaceAddress.getNetworkPrefixLength()
						);
						SubnetUtils subnet = new SubnetUtils(
								interfaceAddress.getAddress().getHostAddress() +
										"/" + interfaceAddress.getNetworkPrefixLength()
						);
						for (String server : servers) {
							URL serverUrl = new URL(server);
							InetAddress eurekaServerAddress = InetAddress.getByName(serverUrl.getHost());
							boolean matches = subnet.getInfo().isInRange(eurekaServerAddress.getHostAddress());
							log.info("Testing server {} ({}): {}", server, eurekaServerAddress.getHostAddress(), matches);
							if (matches) {
								log.info(
										"Found Interface {}: {} ({})",
										networkInterface.getName(),
										interfaceAddress.getAddress().getHostName(),
										interfaceAddress.getAddress().getHostAddress()
								);
								result = createEurekaInstanceConfigBean(inetUtils, defaultResult, interfaceAddress);
								break external_loop;
							}
						}
					} else {
						log.info(
								"Skipping IPv6 from Interface {}: {}/{}",
								networkInterface.getName(),
								interfaceAddress.getAddress(),
								interfaceAddress.getNetworkPrefixLength()
						);
					}
				}
			}
		} catch (Exception e) {
			log.error("Error while detecting eureka client address", e);
		}
		return result == null ? defaultResult : result;
	}

	private EurekaInstanceConfigBean createEurekaInstanceConfigBean(InetUtils inetUtils, EurekaInstanceConfigBean defaultResult, InterfaceAddress interfaceAddress) {
		EurekaInstanceConfigBean result;
		result = new EurekaInstanceConfigBean(inetUtils);
		result.setPreferIpAddress(true);
		result.setHostname(interfaceAddress.getAddress().getHostName());
		result.setIpAddress(interfaceAddress.getAddress().getHostAddress());
		result.setSecurePortEnabled(defaultResult.isSecurePortEnabled());
		result.setSecurePort(defaultResult.getSecurePort());
		result.setNonSecurePortEnabled(defaultResult.isNonSecurePortEnabled());
		result.setNonSecurePort(defaultResult.getNonSecurePort());
		result.setMetadataMap(defaultResult.getMetadataMap());
		if (result.isSecurePortEnabled()) {
			result.setInstanceId(result.getIpAddress() + ":" + result.getHostname() + ":" +
					result.getSecurePort());
			result.setSecureHealthCheckUrl("https://" + result.getIpAddress() + ":" +
					result.getSecurePort() + defaultResult.getHealthCheckUrlPath());
		} else {
			result.setInstanceId(result.getIpAddress() + ":" + result.getHostname() + ":" +
					result.getNonSecurePort());
			result.setHealthCheckUrl("http://" + result.getIpAddress() + ":" +
					result.getNonSecurePort() + defaultResult.getHealthCheckUrlPath());
			result.setStatusPageUrl("http://" + result.getIpAddress() + ":" +
					result.getNonSecurePort() + defaultResult.getStatusPageUrlPath());
		}
		return result;
	}
}

I hope this could help who got stuck!

Tested with:

  • Spring Boot 1.5.9.RELEASE
  • Spring Cloud Edgware.SR1
  • Docker 17.12.0-ce (Swarm Mode)

@saadlu
Copy link

saadlu commented Feb 7, 2018

I was having a similar problem (don't know how similar the problem is, but reading the some of the above makes me think it is similar, so I am posting :P)

Basically I followed

https://spring.io/guides/gs/service-registration-and-discovery/

to create a eureka server and a eureka client. Played with them by launching few clients manually (without swarm) and server and all looked fine. Then wanted to start playing with swarm. My docker-compose was this:

version: "3.3"
services:
  eureka-server:
    image: eureka-server
    deploy:
      replicas: 1
    ports:
      - "8761:8761"

  eureka-client:
    image: eureka-client
    deploy:
      replicas: 3
    environment:
      - EUREKA_SERVER=eureka-server

nothing special, the images are created corresponding to the service in the tutorial and the client defined the eureka URL like the following, and hence the ENV var, EUREKA_SERVER

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

launched the above with

docker stack deploy -c docker-compose.yml eureka

but all three client replicas were registering with same IP, in my case 10.0.0.5. Eureka of course counted them as one, so only one client was found.

looked around the web (including this thread) like a headless chicken and tried few things (including setting the property, eureka.instance.hostName but no luck. Thereafter, found the endpoint_mode on docker:

https://docs.docker.com/compose/compose-file/#endpoint_mode

and setting to endpoint_mode: dnsrr does the trick. I won't pretend to know what DNS round robin does to the eureka client, but it allowed each of my client to register with unique IP (the container-id):

version: "3.3"
services:
  eureka-server:
    image: nexus.psenterprise.com:18079/eureka-server
    deploy:
      replicas: 1
    ports:
      - "8761:8761"

  eureka-client:
    image: nexus.psenterprise.com:18079/eureka-client
    deploy:
      replicas: 3
      endpoint_mode: dnsrr
    environment:
      - EUREKA_SERVER=eureka-server

The IP set now is fine for what I am doing :)

@PaulGobin
Copy link

PaulGobin commented Apr 1, 2018

I am using Rancher, my stack are all SpringBoot netflix 1.5.10-RELEASE

  • Eureka
  • Config Service
  • API gateway with zuul.
  • etc

Since containers can communicate with each other and across hosts, I created a load balancer in rancher and connect it to Eureka, and also the API Gateway.
In all my services, I have the following settings which is pulled from the config server.
spring.cloud.inetutils.preferred-networks=10.0
eureka.instance.preferIpAddress=true

When my service register with Eureka, they all register with the docker container IP and since my Eureka and API Gateway services are also containerized, when a request goes thru the API Gateway, it grabs the service/ip from Eureka and forward the request to the corresponding container/service, it is able to route it via the Rancher/Kubernets network. The only thing I need to do is expose port 9002 as a public accessible port on every host, this also has the side effect of not having to open all the ports on the hosts that my services binds to. In Rancher, you can leave the public (host) port and container port blank and Rancher will assign a random container port, however, this is not really needed by our service but allows us to scale our service to more instances than hosts without running into port conflicts on the host. Additionally, I set the server.port in my config service to 0, this allows spring boot to assign a random port to my service which is what is registered with Eureka. So if the container IP assigned is 10.45.205.145 and the service port is 9999, In Eureka, you will see the containerid:servicename:port, however, if you hover over the service name, you will see the containerIP (10.45.205.145:9999) and not the containerId, this is how the API Gateway is able to route the request to the applicable container.
I also have a generic host file which maven uses when building the docker image:

Dockerfile
FROM openjdk:8-jdk-alpine
MAINTAINER Paul Gobin Paul.Gobin@mycompany.com

VOLUME /tmp
ARG JAR_FILE
ADD target/${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

This seem to work really well for me and does not require any special Eureka registration bean.
The trick is to run everything in Rancher and you only need to expose the load balancer port of the API Gateway to the outside world, in production, you may want to have an external load balancer fronting the Rancher Load Balancer to the API Gateway.

@junneyang
Copy link

junneyang commented Nov 20, 2018

just this:
eureka.instance.preferIpAddress=true
eureka.instance.ipAddress=your-service-name

pranav-patil referenced this issue in pranav-patil/spring-microservices Nov 27, 2018
@SaeedMasoumi
Copy link

@efranceschi What is your application.yml config for Eureka servers?

@efranceschi
Copy link

Hi @SaeedMasoumi. This is my application.yml from Eureka:

server:
  port: 8761

eureka:
  client:
    registerWithEureka: false
    fetchRegistry: false
  instance:
    preferIpAddress: false
  server:
    waitTimeInMsWhenSyncEmpty: 5
    enableSelfPreservation: false

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests