Answer a question

I want to put a spring boot application inside a docker container. It uses @EnableDiscoveryClient which integrate it with consul => registration and health check are working perfectly outside of docker.

I embeded this sample application (movie app from http://rpi-cloud.com/apigatewaypattern/ ) with this dockerfile:

# VERSION 1.0
FROM java:8

ADD ./movie-0.0.1-SNAPSHOT.jar /

# server port
EXPOSE 9000

WORKDIR /
CMD ["java", "-jar", "/movie-0.0.1-SNAPSHOT.jar"]

And then run it from the following docker-compose.yml:

# service discovery
consul:
    image: consul    
    ports:
       - "8500:8500"    # 8500 (HTTP)
    command: agent -server -bootstrap-expect 1 -ui -data-dir /tmp -client=0.0.0.0

## test service
movie:
    build: ./movie
    ports:
      - "9000:9000"
    links:
        - consul:localhost  # for consul registry and configuration

As you can see I tried to be smart: to avoid embedding consul itself into the movie container I have bind the consul host (ip) to localhost of movie container (using docker network functionnality).

It works for well for service discovery : consul ui show me that movie application was able to register itself, but it does not work the other way: health check is failing when consul service try to reach /health api of movie service. Here is an output from the consul logs:

 2016/05/19 15:50:38 [INFO] agent: Synced service 'movie-9000'

and latter:

2016/05/19 15:50:43 [WARN] agent: http request failed 'http://ca676cad169e:9000/health': Get http://ca676cad169e:900
0/health: dial tcp: lookup ca676cad169e on 10.0.2.3:53: server misbehaving

which repeat itself indefinitely. I don't understand why ? Any idea ?

Edit When using docker-compose version 2:

version: '2'
services:
   consul: ... (unchanged)
   movie: ... (unchanged)
    links:
        - consul:localhost  # for consul registry and configuration

consul start fine, but movie service fail to register itself => connection refused. links seems to have no effect in v2. because https://github.com/docker/compose/issues/3127

links are no longer provided by /etc/hosts, they are now provided by an embedded DNS server available in Docker 1.10.x. which breaks the localhost trick.

Edit2

If I explicitly set additional host for version 2 (with consul ip) it works fine.

version: '2'

services:
  movie:
  # removed link keyword and replaced by
      extra_hosts:
        - "localhost:172.19.0.2"
  # specifying consul ip

Edit3

this is the /etc/hosts seen by the movie container with version 2 without adding extra_hosts:

127.0.0.1  localhost
::1        localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet
ff00::0    ip6-mcastprefix
ff02::1    ip6-allnodes
ff02::2    ip6-allrouters
172.19.0.3 45210c319110

whereas in version 1:

127.0.0.1  localhost
::1        localhost ip6-localhost ip6-loopback
... # same +
172.17.0.2 localhost 88a07a35f228 ci_consul_1

How can I to automate that and recreate behavior of version 1, while keeping network feature ?

Edit4 - conclusion

=> this seems unfeasible for the localhost tricks. So am falling back using a different configuration in docker (as suggested).

This is finally not a big issue because the default consul host can be overridden on the command line within the CMD of the Dockerfile, and then I can use consul as the hostname. (see spring overload mechanism http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html)

# in movie Dockerfile
CMD ["java", "-jar", "/movie-0.0.1-SNAPSHOT.jar", "--spring.cloud.consul.host=consul"]

Docker version

I am using docker 1.11.1 on windows with docker-machine

Docker version 1.11.1, build 5604cbe

docker-compose version 1.7.0, build 0d7bf73

Answers

Since you are not using compose version 2. The two containers do not share the same network.

Consul executes periodic health checks to ensure the service is up and running and hence needs access to the application healthcheck service. Which is "http://ca676cad169e:9000/health"

The registration of service works since you have defined a link from app container to the consul container. The app registers itself with consul when it comes up.

You can define a network which is shared by the containers or you can run the containers on the host network.

You will need to use net: "host" option

Here is the link to the documentation https://docs.docker.com/compose/compose-file/#net

With docker the existing docker compose version, you can update the healthcheck endpoint of the movie app. It will then not use the defaults and will query the app on externalized port.

spring:
  cloud:
    consul:
      discovery:
        healthCheckPath: localhost:9000/health
        healthCheckInterval: 15s`

I tried this with docker compose version 2 and could access containers both ways, The networking should allow you to communicate either ways.. in my sample I downloaded nginx page from with consul container.

version: "2"

services:
  app:
    image: nginx
    ports:
      - "9000:80"
  discovery:
    image: consul
    ports:
        - "8500:8500"
    command: agent -server -bootstrap-expect 1 -ui -data-dir /tmp -client=0.0.0.0
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐