Scability with Docker Compose

Advertisements

Docker Compose allows you to create multiple replicas of the same service, providing scalability to your applications. By having multiple replicas of the same service, you can distribute the workload, thus supporting a larger number of operations.

version: '3.1'
services:
	myservice:   
    	deploy:
      		replicas: 3

This is done through the services.<my-service>.deploy.replicas field, which allows you to indicate how many replicas of the same service will be created.

Now, Docker-Compose is a basic orchestrator, so this requires some extra steps to make it possible. Unlike more complete orchestrators like Docker-Swarm or Kubernetes, it is necessary to create a service that acts as a “reverse proxy” and performs load balancing between different instances of the same service.

Setting up replicas in a service

Let’s start with a docker-compose.yaml file that declares 2 services, the first for a MySQL database and the second for an application that consumes that database instance.

version: '3.1'
services:
	book-service:
      image: book-service:latest
      restart: always
      environment:
        MYSQL_HOST: 'book_db'
        MYSQL_PORT: '3306'
        MYSQL_USER: 'user'
        MYSQL_PASSWORD: 'password'
        APP_PORT: '8080'
      ports:
        # <Port exposed> : <MySQL Port running inside container>
        - '8080:8080'
      expose:
        # Opens port 8080 on the container
        - '8080'
      depends_on:
        - book_db

You can then add the deploy.replicas fields to the book-service service indicating the number of instances desired. And also remove the lines corresponding to the ports indicated in this service, otherwise, if there are 2 or more instances/replicas of the same service there would be a conflict with the use of ports and the deployment would fail.

version: '3.1'
services:
	book-service:
      image: book-service:latest
      restart: always
      environment:
          MYSQL_HOST: 'book_db'
          MYSQL_PORT: '3306'
          MYSQL_USER: 'user'
          MYSQL_PASSWORD: 'password'
          APP_PORT: '8080'
          STOCK_SERVICE_URL: 'http://stock-service:8080'
      depends_on:
          - book_db
      deploy:
          replicas: 3
          endpoint_mode: dnsrr        ## Round Robin Load Balancing

Si iniciamos los servicios docker-compose up -d se puede observar como se han creado 3 instancias del servicio book-service.

If we start the services docker-compose up -d , then we can see how 3 instances of the book-service have been created.

Advertisements
$ docker-compose ps                                                                                                                                                                                     
NAME                    COMMAND                  SERVICE             STATUS              PORTS
book-service-1    "java -jar book-serv…"   book-service        running             8080/tcp
book-service-2    "java -jar book-serv…"   book-service        running             8080/tcp
book-service-3    "java -jar book-serv…"   book-service        running             8080/tcp
book_db-1         "docker-entrypoint.s…"   book_db             running             0.0.0.0:3306->3306/tcp

However, there are two issues that still need to be fixed before the scalability provided by these new instances can be used.

  1. There is no load balancing between instances.
  2. The ports are not exposing any endpoints to access your endpoints.

Using a Reverse Proxy

Nginx will be used as a reverse proxy to be the entry point for the replicated services. Nginx will receive the HTTP requests, and will redirect them to the docker-compose service, and this one to one of its multiple instances, solving the load balancing problem at the same time.

  • A reverse_proxy.conf file is created and saved alongside the docker-compose.yaml file. This file is the NGINX configuration for a reverse proxy.
 server {
  listen 80;
  server_name localhost;
  
  location / {
        proxy_pass http://book-service:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

These instructions say that when NGINX reads port 80, on localhost, it should redirect to the URL http://book-service:8080/. That is, it should redirect to the internal DNS of the book-service.

  • In the file docker-compose.yaml a new service is added with name reverse-proxy:
reverse-proxy:
  image: nginx:latest
  volumes:
  - ./reverse_proxy.conf:/etc/nginx/conf.d/default.conf
  ports:
  - "8080:80"
  depends_on:
  - book-service

This service is named reverse-proxy, its image is nginx:latest, it uses the configuration file created previously reverse_proxy.conf, and it maps nginx port 80 to 8080 which is exposed externally.

  • Finally the file docker-compose.yaml should look at this:
version: '3.1'
services:
	book-service:
      image: book-service:latest
      restart: always
      environment:
          MYSQL_HOST: 'book_db'
          MYSQL_PORT: '3306'
          MYSQL_USER: 'user'
          MYSQL_PASSWORD: 'password'
          APP_PORT: '8080'
          STOCK_SERVICE_URL: 'http://stock-service:8080'
      depends_on:
      	- book_db
      deploy:
          replicas: 3
          endpoint_mode: dnsrr        ## Round Robin Load Balancing
            
    reverse-proxy:
        image: nginx:latest
        volumes:
          - ./reverse_proxy.conf:/etc/nginx/conf.d/default.conf
        ports:
          - "8080:80"
        depends_on:
          - book-service
  • The orchestration is started with docker-compose with the instruction:
$ docker-compose up

Once the applications are started, you can see that there are 3 instances for the book-service, plus a reverse-proxy listening on port 8080.

$ docker-compose ps                                                                                                                                                                                     
NAME                    COMMAND                  SERVICE             STATUS              PORTS
book-service-1    "java -jar book-serv…"   book-service        running             8080/tcp
book-service-2    "java -jar book-serv…"   book-service        running             8080/tcp
book-service-3    "java -jar book-serv…"   book-service        running             8080/tcp
book_db-1         "docker-entrypoint.s…"   book_db             running             0.0.0.0:3306->3306/tcp
reverse-proxy-1   "/docker-entrypoint.…"   reverse-proxy       running             0.0.0.0:8080->80/tcp

Finally, accessing the URL of the service:

Conclusion

Docker-Compose allows you to scale your services to provide multiple instances, and through a simple configuration add a reverse-proxy that helps us provide load balancing and complement necessary functionalities.

References

Advertisements

Leave a Reply

Your email address will not be published. Required fields are marked *