Scaling Flask with Docker: Deploying a Portfolio Project with NGINX Load Balancing

Pravesh SudhaPravesh Sudha
6 min read

💡 Introduction

Welcome to the World of Exploration!

Today, we’re diving into an exciting experiment with NGINX as a load balancer to see how we can distribute traffic across multiple instances of an application. For those of you who are new here, I regularly create blogs around DevOps projects, experimenting with various tools and technologies. If you’re interested in deploying projects to the cloud, check out my recent blog on How to Deploy a Flask Project on AWS Elastic Beanstalk.

In this project, we’ll revisit the same Flask portfolio project and take it a step further. Here’s what we’ll be doing:

  1. Create three Docker images of the project with minor changes (changing the navbar color) and name them v1, v2, and v3.

  2. Use a Docker Compose file to run these three versions of the application on separate ports.

  3. Install NGINX, modify its configuration file, and use it as a load balancer to evenly distribute traffic between these versions.


💡 Pre-requisites

Before diving into the project, let’s ensure we’re ready with the basics. Here’s what you’ll need:

  • A basic understanding of Docker and Docker Compose: If you’re new to these tools, no worries! Docker allows us to containerize applications, and Docker Compose helps us manage multi-container setups effortlessly.

💡 Understanding NGINX as a Load Balancer

Before we jump into the practical demo, let’s take a moment to understand what NGINX is and how it acts as a load balancer.

In a nutshell, NGINX is a reverse proxy server that handles HTTP requests and allows us to host web applications on a server. One of its key features is its ability to act as a load balancer, directing incoming traffic to multiple servers for better load management, improved performance, and efficiency.

Load Balancing Methods in NGINX

  1. Round-Robin (default): Distributes requests sequentially and cyclically to each server in the group.

  2. Least Connections: Routes traffic to the server with the fewest active connections.

  3. Others: NGINX supports more advanced methods tailored to specific needs.

Additional Features of NGINX

  • Caching: Temporarily stores data to improve performance by reducing the need to fetch it repeatedly.

  • Proxy: Acts as a single entry point for internet access, securing all connected servers using HTTPS. This minimizes exposure, consolidates security, and centralizes logs and monitoring.

  • Compression: Compresses large files (e.g., high-quality videos) to save bandwidth and improve load times. It also supports segmentation, breaking down large files into smaller chunks for efficient streaming.

NGINX configurations are stored in the nginx.conf file, typically located in /etc/nginx/.

Why Choose NGINX Over Apache?

  • Performance: NGINX is faster and more lightweight.

  • Static Files: It’s best suited for serving static files and powering high-performance web servers.

  • Simplicity: Configurations are straightforward and easy to manage.

Enough theory! Let’s dive straight into the demonstration and bring our setup to life.


💡 Building the Project

Now let’s get hands-on with the project! Follow these steps to build and set up your Flask portfolio application.

Step 1: Clone the Repository

Start by heading to my GitHub repository, ebs-demo, and clone the project:

git clone https://github.com/Pravesh-Sudha/ebs-demo.git

This repository contains the basic Flask portfolio project along with the Dockerfile to build a Docker image of the application.

Step 2: Build the Docker Images

Navigate into the project directory:

cd ebs-demo

Build the first version (v1) of the application using the docker build command:

docker build -t <your-dockerhub-username>/flask-portfolio-app:v1 .

This will create a Docker image named flask-portfolio-app:v1.

Next, let’s create two more versions, v2 and v3, with some minor changes. Here’s how:

  1. Open the static/style.css file in the project directory.

  2. In the Navbar section, modify the background color to something different for each version.

Now, build the additional versions:

docker build -t <your-dockerhub-username>/flask-portfolio-app:v2 .
docker build -t <your-dockerhub-username>/flask-portfolio-app:v3 .

Make sure to update the background color again before building v3 so that each version has a unique look.

Step 3: Push the Images to DockerHub

Once you’ve built the three versions, log in to DockerHub:

docker login

Provide your DockerHub username and password (or PAT token).

Push the images to your DockerHub account:

docker push <your-dockerhub-username>/flask-portfolio-app:v1
docker push <your-dockerhub-username>/flask-portfolio-app:v2
docker push <your-dockerhub-username>/flask-portfolio-app:v3

Step 4: Run the Applications Using Docker Compose

Now that the images are uploaded, let’s use the docker-compose.yaml file in the project directory to run all three versions simultaneously:

  1. Open the docker-compose.yaml file.

  2. Update the image names to match your DockerHub images (<your-dockerhub-username>/flask-portfolio-app:v1, v2, v3).

Run the following command to bring up all three applications:

docker-compose up

You’ll see three different versions of your application running on three separate ports:


💡 Installing and Configuring NGINX for Load Balancing

Now that we have our three Flask applications running on different ports, it’s time to set up NGINX as a load balancer to distribute traffic across these instances.

Step 1: Install NGINX

Depending on your operating system, use the following commands to install NGINX:

  • For macOS:
brew install nginx
  • For Linux:
sudo apt install nginx -y

Step 2: Locate the NGINX Configuration File

After installation, you can locate the default nginx.conf file by running:

nginx -h

The output will display the location of the configuration file. Navigate to that directory to edit the nginx.conf file.

Step 3: Update the nginx.conf File

Replace the contents of the nginx.conf file with the following configuration:

worker_processes 1;

events {
    worker_connections 1024;
}

http {
    include mime.types;

    # Define the upstream cluster of Flask servers
    upstream flask_cluster {
        server 127.0.0.1:5000;
        server 127.0.0.1:5001;
        server 127.0.0.1:5002;
    }

    server {
        listen 8080;
        server_name localhost;

        location / {
            proxy_pass http://flask_cluster;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

What Does This Configuration Do?

  1. upstream flask_cluster: This defines the three Flask applications running on 127.0.0.1:5000, 5001, and 5002.

  2. listen 8080: Sets the NGINX server to listen on port 8080.

  3. proxy_pass: Forwards requests to the flask_cluster.

  4. Headers: The proxy_set_header directives ensure that client details like the host and IP address are preserved when requests are passed to the Flask servers.

Step 4: Restart NGINX

After updating the configuration, save the file and restart NGINX to apply the changes:

nginx -s reload

Step 5: Verify the Setup

Make sure your Docker containers are running using:

docker-compose up

Now, navigate to http://localhost:8080 in your browser. NGINX will load balance the traffic across the three versions of your Flask application, distributing requests to v1, v2, and v3 seamlessly. Everytime you reload the page, you will see a different version.

You can Checkout the Video in the above link to see the result.


💡 Conclusion

Congratulations! 🎉 You've successfully set up a load-balanced Flask portfolio project using Docker containers and NGINX. Here's what we've accomplished in this project:

  1. Built three versions of a Flask application with minor visual differences.

  2. Containerized the applications using Docker and pushed the images to Docker Hub.

  3. Used Docker Compose to run the three application instances on different ports.

  4. Configured NGINX as a load balancer to distribute traffic across these instances.

This hands-on project demonstrates the power of combining Docker and NGINX to build scalable, high-performance applications. The load balancing feature ensures that traffic is efficiently distributed across multiple instances, improving application reliability and user experience.

Feel free to experiment further by tweaking the NGINX configuration, exploring advanced load-balancing algorithms, or integrating HTTPS for secure connections.

If you found this blog helpful, do check out my other blogs on DevOps projects and tools. Let’s keep learning and building!

🚀 For more informative blog, Follow me on Hashnode, X(Twitter) and LinkedIn.

Till then, Happy coding! 🚀

12
Subscribe to my newsletter

Read articles from Pravesh Sudha directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Pravesh Sudha
Pravesh Sudha

Bridging critical thinking and innovation, from philosophy to DevOps.