Automatic Load Balancing Of Spring Boot Micro Services In Docker With Haystack

In Spring Boot Micro Services In Docker we saw how to create a Docker container from a Spring Boot Micro Service. In this article we shall look at load balancing the service using Haystack. Haystack is a DNS based load balancer that is integrated with the Docker API automatically creating service groups as containers stop and start.

Haystack monitors the Docker API using a tcp socket. In this case the Docker API is on port 2375 on the Docker host.

docker run \
  -e DOCKER_HOST=tcp://172.16.1.218:2375 \
  --name haystack \
   --detach \
   shortishly/haystack

Build a gsa image following the instructions in this article, starting 3 instances of our Spring Boot Micro Service:

docker run --name srv-001 -d gsa
docker run --name srv-002 -d gsa
docker run --name srv-003 -d gsa

We can confirm that we have 3 gsa services by running docker ps as follows:

$ docker ps -a --format="{{.Names}}"

srv-003
srv-002
srv-001

Haystack automatically creates a service group srv.gsa.services.haystack in DNS. Starting a new gsa container will automatically add it to the service group. Stopping a gsa container automatically will remove it from the service group.

Startup a busy box instance using Haystack’s embedded DNS:

docker run \
  --dns=$(docker inspect --format='{{.NetworkSettings.IPAddress}}' haystack) \
  --tty \
  --interactive \
  --rm busybox /bin/sh

Lookup srv.gsa.services.haystack in DNS:

nslookup srv.gsa.services.haystack

Server:    172.17.0.7
Address 1: 172.17.0.7 cb1i9a6.containers.haystack

Name:      srv.gsa.services.haystack
Address 1: 172.17.0.7 cb1i9a6.containers.haystack

Note that srv.gsa.services.haystack is actually pointing to the Haystack container. This is because Haystack acts as a proxy to HTTP requests, automatically load balancing requests randomly over members of the service group. Issue a wget to the service group and the requests will be load balanced randomly over the members:

# wget -q -O /dev/stdout http://srv.gsa.services.haystack/hello-world
{"id":1,"content":"Hello, Stranger!"}

# wget -q -O /dev/stdout http://srv.gsa.services.haystack/hello-world
{"id":1,"content":"Hello, Stranger!"}

# wget -q -O /dev/stdout http://srv.gsa.services.haystack/hello-world
{"id":2,"content":"Hello, Stranger!"}

# wget -q -O /dev/stdout http://srv.gsa.services.haystack/hello-world
{"id":1,"content":"Hello, Stranger!"}

# wget -q -O /dev/stdout http://srv.gsa.services.haystack/hello-world
{"id":3,"content":"Hello, Stranger!"}

# wget -q -O /dev/stdout http://srv.gsa.services.haystack/hello-world
{"id":2,"content":"Hello, Stranger!"}

# wget -q -O /dev/stdout http://srv.gsa.services.haystack/hello-world
{"id":4,"content":"Hello, Stranger!"}

# wget -q -O /dev/stdout http://srv.gsa.services.haystack/hello-world
{"id":3,"content":"Hello, Stranger!"}

# wget -q -O /dev/stdout http://srv.gsa.services.haystack/hello-world
{"id":4,"content":"Hello, Stranger!"}

# wget -q -O /dev/stdout http://srv.gsa.services.haystack/hello-world
{"id":5,"content":"Hello, Stranger!"}

Congratulations! You now have a Spring Boot Micro Service Docker container that is automatically being load balanced by Haystack. You can add or remove further gsa services and Haystack will automatically update the service pool.

Spring Boot Micro Services In Docker

Building a RESTful Web Service with Spring Boot Actuator is a great example of building a very simple micro service using Spring Boot Actuator. In this article we will go on to package that service into a Docker container.

Firstly we need to clone the source code from the original article:

$ git clone https://github.com/spring-guides/gs-actuator-service.git
$ cd gs-actuator-service/complete/

Build it using gradle:

$ ./gradlew clean assemble

Or with maven:

$ mvn package -DskipTests=true

We need to create a Dockerfile that we will use to build a docker image of the service as follows:

FROM java:8
EXPOSE 80
COPY target/gs-actuator-service-0.1.0.jar /
ENTRYPOINT ["java", \
           "-Djava.security.egd=file:/dev/urandom", \
           "-jar", \
           "gs-actuator-service-0.1.0.jar", \
           "--server.port=80"]

A couple of notes on the Dockerfile:

  • We are using the official Java 8 docker image as our base and copying our jar into the root directory of the image. If you used gradle instead of maven replace target/gs-actuator-service-0.1.0.jar with build/libs/gs-actuator-service-0.1.0.jar
  • We are using /dev/urandom as explained by this article to avoid issues in slow Tomcat startup when there is insufficient entropy (also the reason why we skipped the tests when building above)
  • We expose the service on port 80. When managing more than a couple of containers it is much simpler to just use standard ports.

Using your favourite editor put the Dockerfile in the complete directory, and build a docker image as follows:

docker build -t gsa .

Our image has been created an has been tagged as ‘gsa’:

$ docker images
REPOSITORY TAG    IMAGE ID     CREATED       SIZE
gsa        latest 6acbba251766 4 seconds ago 683.1 MB

We can run the image using docker run as follows:

docker run --name gsa -d gsa

We can find the IP address being used by the gsa container by running docker inspect:

$ docker inspect \
    --format={{.NetworkSettings.IPAddress}} gsa

172.17.0.7

In this case our ‘gsa’ container has been given 172.17.0.7 as its IP address. We can make a HTTP request for /hello-world from our new service with the following:

curl 172.17.0.7/hello-world

Be sure to replace IP address 172.17.0.7, with the output that your docker host gives you on docker inspect.

In response you will get something like:

{"id":1,"content":"Hello, Stranger!"}

Congratulations! You now have a Spring Boot Actuator Micro Service running inside a Docker container.