HAProxy EP 9: Load Balancing with Weighted Round Robin
Load balancing helps distribute client requests across multiple servers to ensure high availability, performance, and reliability. Weighted Round Robin Load Balancing is an extension of the round-robin algorithm, where each server is assigned a weight based on its capacity or performance capabilities. This approach ensures that more powerful servers handle more traffic, resulting in a more efficient distribution of the load.
What is Weighted Round Robin Load Balancing?
Weighted Round Robin Load Balancing assigns a weight to each server. The weight determines how many requests each server should handle relative to the others. Servers with higher weights receive more requests compared to those with lower weights. This method is useful when backend servers have different processing capabilities or resources.
Step-by-Step Implementation with Docker
Step 1: Create Dockerfiles for Each Flask Application
We’ll use the same three Flask applications (app1.py
, app2.py
, and app3.py
) as in previous examples.
- Flask App 1 (
app1.py
):
from flask import Flask app = Flask(__name__) @app.route("/") def home(): return "Hello from Flask App 1!" @app.route("/data") def data(): return "Data 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 home(): return "Hello from Flask App 2!" @app.route("/data") def data(): return "Data from Flask App 2!" if __name__ == "__main__": app.run(host="0.0.0.0", port=5002)
- Flask App 3 (
app3.py
):
from flask import Flask app = Flask(__name__) @app.route("/") def home(): return "Hello from Flask App 3!" @app.route("/data") def data(): return "Data from Flask App 3!" if __name__ == "__main__": app.run(host="0.0.0.0", port=5003)
Step 2: Create Dockerfiles for Each Flask Application
Create Dockerfiles for each of the Flask applications:
- Dockerfile for Flask App 1 (
Dockerfile.app1
):
# Use the official Python image from Docker Hub FROM python:3.9-slim # Set the working directory inside the container WORKDIR /app # Copy the application file into the container 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 the HAProxy Configuration File
Create an HAProxy configuration file (haproxy.cfg
) to implement Weighted Round Robin Load Balancing
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 roundrobin server server1 app1:5001 weight 2 check server server2 app2:5002 weight 1 check server server3 app3:5003 weight 3 check
Explanation:
- The
balance roundrobin
directive tells HAProxy to use the Round Robin load balancing algorithm. - The
weight
option for each server specifies the weight associated with each server:server1
(App 1) has a weight of 2.server2
(App 2) has a weight of 1.server3
(App 3) has a weight of 3.
- Requests will be distributed based on these weights: App 3 will receive the most requests, App 2 the least, and App 1 will be in between.
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 docker-compose.yml
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 the services (app1
,app2
,app3
, andhaproxy
) and their respective configurations. - HAProxy depends on the three Flask applications to be up and running before it starts.
Step 6: Build and Run the Docker Containers
Run the following command to build and start all the containers
docker-compose up --build
This command builds Docker images for all three Flask apps and HAProxy, then starts them.
Step 7: Test the Load Balancer
Open your browser or use curl
to make requests to the HAProxy server
curl http://localhost/ curl http://localhost/data
Observation:
- With Weighted Round Robin Load Balancing, you should see that requests are distributed according to the weights specified in the HAProxy configuration.
- For example, App 3 should receive three times more requests than App 2, and App 1 should receive twice as many as App 2.
Conclusion
By implementing Weighted Round Robin Load Balancing with HAProxy, you can distribute traffic more effectively according to the capacity or performance of each backend server. This approach helps optimize resource utilization and ensures a balanced load across servers.