Docker Compose permite crear múltiples replicas de una mismo servicio, proporcionando escalabilidad a las aplicaciones. Al tener multiples replicas de un mismo servicio, estos pueden distribuir la carga de trabajo, por lo tanto soportando un mayor número de operaciones.
version: '3.1' services: myservice: deploy: replicas: 3
Esto se realiza por medio del campo services.<myservice>.deploy.replicas
. La cual permite indicar cuantas replicas del mismo servicio serán creadas.
Ahora bien, Docker-Compose es un orquestador básico, por lo cual esto requiere algunos pasos extra para hacerlo posible. A diferencia de orquestadores más completos como Docker-Swarm o Kubernetes, es necesario crear un servicio que sirva de “reverse proxy” y realice el balanceo de carga entre las distintas instancias de un mismo servicio.
Indicando replicas a un servicio
Vamos a comenzar con un archivo docker-compose.yaml
que declara 2 servicios, el primero para una base de datos MySQL y el segundo para una aplicación que consume dicha instancia base de datos.
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
Se puede entonces agregar los campos deploy.replicas
al servicio book-service
indicando la cantidad de instancias deseadas. Y además remover las lineas correspondientes a los puertos indicados en este servicio, de otra manera, al haber 2 o más instancias/replicas del mismo servicio habría un conflicto con el uso de puertos y el deployment fallaría.
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
.
$ 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
Sin embargo, hay dos problemas que se deben corregir aún, para que se pueda hacer uso de la escabilidad proporcionada por esta nuevas instancias.
- No hay una distribución de cargas entre las instancias.
- Los puertos no están exponiendo ningún endpoint para acceder a sus endpoints.
Usando un Reverse Proxy
Se utilizará Nginx como reverse proxy, para que sea el punto de entrada de los servicios que están replicados. Nginx recibirá las solicitudes HTTP, y las redireccionará al servicio de docker-compose y este a una de sus múltiples instancias, resolviendo a la vez el problema del balanceo de cargas.
- Se crea un archivo
reverse_proxy.conf
, y lo se guarda junto al archivodocker-compose.yaml
. Este archivo es la configuracion de NGINX para un 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; } }
Esta instrucciones dicen que cuando NGINX lea el puerto 80, en localhost, redireccione al URL http://book-service:8080/
. Es decir, que redireccione al DNS interno del servicio book-service
.
- En el archivo
docker-compose.yaml
se agrega un nuevo servicioreverse-proxy
:
reverse-proxy: image: nginx:latest volumes: - ./reverse_proxy.conf:/etc/nginx/conf.d/default.conf ports: - "8080:80" depends_on: - book-service
Este servicio se llama reverse-proxy
, su imagen es nginx:latest
, utiliza el archivo de configuracion creado anteriormente reverse_proxy.conf
, y mapeo el puerto 80
de nginx con el 8080
que es expuesto externamente.
- Finalmente el archivo
docker-compose.yaml
debe lucir como este:
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
- Se inicia la orquestración con docker-compose con la instrucción:
$ docker-compose up
Una vez iniciados las aplicaciones, se puede ver como hay 3 instancias para el servicio book-service
, además de un reverse-proxy escuchando al puerto 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
Finalmente se accede al URL del servicio:
- Book Service : http://localhost:8080/ (a traves de NGINX que distribuye a los servicios)
Conclusión
Docker-Compose permite escalar sus servicios para proporcionar multiples instancias, y por medio de una configuración sencilla agregar un reverse-proxy que nos ayude a proporcionar el balanceo de carga y complementar funcionalidades necesarias.