HAProxy Ep 6: Load Balancing With Least Connection
Load balancing is crucial for distributing incoming network traffic across multiple servers, ensuring optimal resource utilization and improving application performance. One of the simplest and most popular load balancing algorithms is Round Robin. In this blog, weβll explore how to implement Least Connection load balancing using Flask as our backend application and HAProxy as our load balancer.
What is Least Connection Load Balancing?
Least Connection Load Balancing is a dynamic algorithm that distributes requests to the server with the fewest active connections at any given time. This method ensures that servers with lighter loads receive more requests, preventing any single server from becoming a bottleneck.
Step-by-Step Implementation with Docker
Step 1: Create Dockerfiles for Each Flask Application
Weβll create three separate Dockerfiles, one for each Flask app.
Flask App 1 (app1.py) β Introduced Slowness by adding sleep
from flask import Flask import time app = Flask(__name__) @app.route("/") def hello(): time.sleep(5) return "Hello from Flask App 1!" if __name__ == "__main__": app.run(host="0.0.0.0", port=5001)
Flask App 2 (app2.py)
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello from Flask App 2!" if __name__ == "__main__": app.run(host="0.0.0.0", port=5002)
Flask App 3 (app3.py) β Introduced Slowness by adding sleep.
from flask import Flask import time app = Flask(__name__) @app.route("/") def hello(): time.sleep(5) return "Hello from Flask App 3!" if __name__ == "__main__": app.run(host="0.0.0.0", port=5003)
Each Flask app listens on a different port (5001, 5002, 5003).
Step 2: Dockerfiles for each flask application
Dockerfile for Flask App 1 (Dockerfile.app1)
# Use the official Python image from the Docker Hub FROM python:3.9-slim # Set the working directory inside the container WORKDIR /app # Copy the current directory contents into the container at /app COPY app1.py . # Install Flask inside the container RUN pip install Flask # Expose the port the app runs on EXPOSE 5001 # Run the application CMD ["python", "app1.py"]
Dockerfile for Flask App 2 (Dockerfile.app2)
FROM python:3.9-slim WORKDIR /app COPY app2.py . RUN pip install Flask EXPOSE 5002 CMD ["python", "app2.py"]
Dockerfile for Flask App 3 (Dockerfile.app3)
FROM python:3.9-slim WORKDIR /app COPY app3.py . RUN pip install Flask EXPOSE 5003 CMD ["python", "app3.py"]
Step 3: Create a configuration for HAProxy
global log stdout format raw local0 daemon defaults log global mode http option httplog option dontlognull timeout connect 5000ms timeout client 50000ms timeout server 50000ms frontend http_front bind *:80 default_backend servers backend servers balance leastconn server server1 app1:5001 check server server2 app2:5002 check server server3 app3:5003 check
Explanation:
frontend http_front
: Defines the entry point for incoming traffic. It listens on port 80.backend servers
: Specifies the servers HAProxy will distribute traffic evenly the three Flask apps (app1
,app2
,app3
). Thebalance leastconn
directive sets the Least Connection for load balancing.server
directives: Lists the backend servers with their IP addresses and ports. Thecheck
option allows HAProxy to monitor the health of each server.
Step 4: Create a Dockerfile for HAProxy
Create a Dockerfile for HAProxy (Dockerfile.haproxy)
# Use the official HAProxy image from Docker Hub FROM haproxy:latest # Copy the custom HAProxy configuration file into the container COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg # Expose the port for HAProxy EXPOSE 80
Step 5: Create a Dockercompose file
To manage all the containers together, create a docker-compose.yml
file
version: '3' services: app1: build: context: . dockerfile: Dockerfile.app1 container_name: flask_app1 ports: - "5001:5001" app2: build: context: . dockerfile: Dockerfile.app2 container_name: flask_app2 ports: - "5002:5002" app3: build: context: . dockerfile: Dockerfile.app3 container_name: flask_app3 ports: - "5003:5003" haproxy: build: context: . dockerfile: Dockerfile.haproxy container_name: haproxy ports: - "80:80" depends_on: - app1 - app2 - app3
Explanation:
- The
docker-compose.yml
file defines four services:app1
,app2
,app3
, andhaproxy
. - Each Flask app is built from its respective Dockerfile and runs on its port.
- HAProxy is configured to wait (
depends_on
) for all three Flask apps to be up and running.
Step 6: Build and Run the Docker Containers
Run the following commands to build and start all the containers:
# Build and run the containers docker-compose up --build
This command will build Docker images for all three Flask apps and HAProxy and start them up in the background.
You should see the responses alternating between βHello from Flask App 1!β, βHello from Flask App 2!β, and βHello from Flask App 3!β as HAProxy uses the Round Robin algorithm to distribute requests.
Step 7: Test the Load Balancer
Open your browser or use a tool like curl
to make requests to the HAProxy server:
curl http://localhost
You should see responses cycling between βHello from Flask App 1!β, βHello from Flask App 2!β, and βHello from Flask App 3!β according to the Least Connection strategy.