Load Balancing With NGINX

Ahmet Emre DEMİRŞEN
6 min readJan 24, 2023

--

Prerequisites: Basic Docker and Nodejs knowledge

Hello everyone, Previously, we answered the question of what is Nginx and we ran a react application using nginx web server. You can find the related article here.

In this article, I will talk about how we can control the backend traffic of the web application we serve with Nginx. For this process, I will use the Load Balancing function, which is one of Nginx’s own features. Let’s start with a brief definition:

1- What is Load Balancing?

In the simplest and shortest definition, Load Balancing is the process of managing the traffic between the server and the client. In today’s architectures, it is necessary to increase the capacity of the servers in case of needed. This process is called scaling. Two different methods can be used for scaling:

Increasing server capacity, i.e. multiplying its memory and speed.

Increasing the number of servers.

In today’s architectures, the second option is more widely used and instead of increasing the capacity, it is preferred to increase the number of servers. In such an architecture, it will be essential to manage the traffic. Because the requests from the client should be distributed to the servers in the most appropriate and optimal way in order to avoid conflicts and not to overload the servers. In this way, a busy server will not become busy and another server will have solved the problem. The structures that do this operation are called Load Balancers.

It will be better understood visually:

As seen in the image, there is a load balancer between the 4 servers and the client in the architecture. This structure will forward the incoming requests to the servers according to a certain algorithm and control the traffic.

So What Algorithms Does the Load Balancer Use To Control Traffic?

There are multiple algorithms or methods that load balancers use. This option is in the hands of the developer, a load balancing method can be selected according to the architecture and traffic. Here are a few of these methods:

  • Least Connection
  • Resource Based
  • Round robin
  • IP Hash

In addition to these methods, traffic control can be done as Random. You can find explanations of the above methods and more here.

2- Let’s Create Our Servers

Since it will be fast and simple, I will create our backend project that will run on the server with nodejs. I create a folder for the server in the project directory and start a node project in this folder with the following command.

npm init -y

The package.json file has been created, now let’s create the index.js file in the server folder. Since we will create a rest api, I will use node express, so let’s add the necessary library with the following command:

npm install express

The final version of our project directory will be as follows:

Now let’s code the index.js file:

const express = require("express")

const app = express()

app.get("/", (req,res) => {
res.send("This is the server's response...")
})
app.listen(5050,() => {
console.log("Listening...")
})

We have implemented a simple rest api serving from port 5050. To test it, you can run it with the node index.js command and access it from the localhost:5050 port.

Now let’s run this simple backend application as a docker container. Next we will virtually replicate this server as docker containers and make changes for load balancing in the nginx.conf file.

Let’s create a Dockerfile in the server folder:

FROM node:19-alpine

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 5050

CMD [ "node", "index.js" ]

Before we build the Dockerfile, both nginx and our virtual servers will run on docker. We need to create a common network for them to communicate with each other. Let’s create a network named loadbalance_net with the following command:

docker network create loadbalance_net

Now let’s build and create our 4 virtual servers by running the following commands in order:

docker build -t server .

docker run -p 1010:5050 --name backend_server_1 -d --network loadbalance_net server

docker run -p 2020:5050 --name backend_server_2 -d --network loadbalance_net server

docker run -p 3030:5050 --name backend_server_3 -d --network loadbalance_net server

docker run -p 4040:5050 --name backend_server_4 -d --network loadbalance_net server

We have 4 identical servers that we can access from ports 1010, 2020, 3030 and 4040. By opening your browser, you can access and test your backend service from these ports.

These ports are the ports opened outside of the docker network, so we can access these virtual servers with these ports from outside of docker. But since our nginx server is also a docker container, we will access our backend servers from 5050 port with their own special names, not from these ports.

3- NGINX as a Load Balancer

In this article, we will use NGINX as the Load Balancer. (In my previous article, we used nginx as a web server and served the react application.)

It uses the default Round Robin algorithm as the Nginx Load Balancing method. We will continue with this algorithm.

First, we will create an nginx folder in our project and the nginx.conf file in it:

Now let’s write the contents of the nginx.conf file:

upstream backend {
server backend_server_1:5050;
server backend_server_3:5050;
server backend_server_3:5050;
server backend_server_4:5050;
}
server {
listen 80;
include /etc/nginx/mime.types;
location / {
proxy_pass http://backend/;
}
}

With the upstream block, we have shown which backend servers can be accessed to our nginx server. Since we do not specify a specific load balancing algorithm, it will use the round robin algorithm. If we wanted it to use a different algorithm, we could specify it as follows:

upstream backend {
least_conn;
server backend_server_1:5050;
server backend_server_3:5050;
server backend_server_3:5050;
server backend_server_4:5050;
}

We stated that we will use the least_conn algorithm in the above configuration. It will suffice to specify the same for other options. Detailed information can be found here.

Yes, since we have handled the nginx configurations, let’s create a Dockerfile, create an nginx image, and then launch the Load Balancer serving from port 3000. The final state of the project directory will be as follows:

Nginx Dockerfile:

FROM nginx:stable-alpine

COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

It’s time to build the nginx Dockerfile. Let’s do the build with the following command:

docker build -t nginx_load_balancer .

Let’s get the image we built up by mapping it to port 3000 and including it in the network we created before:

docker run -p 3000:80 --name nginx_server -d --network loadbalance_net nginx_load_balancer

After this process, there will be 5 containers running as follows:

All transactions are ok. Now let’s go to our browser and send a request to localhost:3000

As you can see, we got the response from our backend servers. Every time we refresh the page, it connects to a different server using the load_balancing algorithm we set, but we don’t see it. In order to see that it is connecting to a different server, you can change the response it sends to the servers and test it again.

In summary, in this article, we performed a load balancing process between servers using the nginx load balancing feature. In the next article, we will deploy this entire process with docker-compose.

You can find the source code of the work here.

Buy me a coffee:

Thanks, Have a good work!

--

--