NGINX – Configuration of a simple Load Balancing

There comes a time when your server cannot handle with queries anymore. With the increase of the consumer traffic, with greater server loading due to some kind of marketing advertising, or with non-optimized applications these problems usually emerge. For a more optimal load balancing a load balancer can be used. In doing so, application is spread on more than one server and there is a balancer before it which distributes queries to the servers. If all of a sudden one of the servers fails, all queries will be distributed among the rest.

Nginx is one of the most popular web servers with an option of a very powerful load balancer designed to increase the availability and efficiency of server resources.

By way of illustration, here is a graphical view of what client’s server requests architecture looks like under Load Balancing.

In this article we will explain how to set up load balancing with nginx. It stands to mention that balancing can be fixed for any service: HTTP, HTTPS, FastCGI, uwsgi, SCGI, or Memcached.

Here are the nginx-supported balancing mechanisms:

  • round-robin — requests to the application servers are distributed in a round-robin fashion;
  • least-connected — next request is assigned to the server with the least number of active connections;
  • ip-hash — a hash-function is used to determine what server should be selected for the next request (based on the client’s IP address).

The article will proceed with installation and configuring of the balancer itself. We will deploy 3 additional servers for an application to be balanced between them. All commands are described based on the DEBIAN 9 operating system.

1. NGINX Installation

As a first step, deploy those 3 servers to be balanced for an application. Install NGINX on all those 3 servers.

Install NGINX on all those 3 servers.

# apt-get install nginx

At this stage we will not deploy additional applications; we will only edit the major index.nginx-debian.html file. In this file, save each server using the specified name SERVER 1, SERVER 2, SERVER 3.

# echo ‘Hello! My name SERVER 1’ > /var/www/html/index.nginx-debian.html

Done. NGINX is installed and the server names are derivered. Now, when clicking this link in a browser, we will be able to see the message that the NGINX web server is backed up for each server.

Let us establish for each server an IP: 10.10.10.1, 10.10.10.2 and 10.10.10.3. The Balancing Server IP will be 10.10.10.99

When trying to make a query to the first server, we wll get the following response:

# curl http://10.10.10.1/

Hello! My name SERVER 1

Repeat the installing and text-writing procedure for the two remaining servers.

2. NGINX configuring for load balancing

After the installaton of all three servers with NGINX default settings, we will deploy the balancer server. We will also proceed with the installation of NGINX servers:

# apt-get install nginx

Now let us configure server balancing for the new configuration servers. In order to display 3 previous servers for balancing, let’s deploy the following values into the NGINX configuration:

echo 'upstream balanced {
     server 10.10.10.1;
     server 10.10.10.2;
     server 10.10.10.3;
}

server {
     listen 80;

     location / {
         proxy_pass http://balanced;
     }
}' > /etc/nginx/sites-available/default

In this configuration, we can see that those three previously deployed servers are in the configuration of an `upstream` server group. The type of balancing mechanism is also indicated in the `upstream` directive. If it is not specified, the `round-robin` is taken as a default. To specify those 2 other mechanisms `least_conn` or` ip_hash` is to be specified. The following `balanced` value is to be specified as a title of a group for balancing. Any random name will suffice to be used in the configurations later as a link to this particular server group.

Then, if we transition to, we will see the `server` configuration group. At this stage we are operating over the HTTP protocol with port 80. For this, a port that will listen to the `listen 80` web server has to be specified. Then, the algorithms for locations have to be written according to the directories. If `location /` is specified, all queries will be served by this block. Then, we can see `proxy_pass` inside responsible for proxying the query to a third-party server. At this stage we specify a `balanced` server group as well as transmission control protocol and also leave http on port 80. For this purpose we put http:// at the beginning.

At that load-balancing installation is completed with the simplest configurations. Let us proceed to testing.

Restart your web server to apply new configurations:

# service nginx restart

For testing, we can start CURL query to the balancing server:

# for((r=0; r < 10; r++)); do curl http://10.10.10.99/; done
Hello! My name SERVER 1
Hello! My name SERVER 2
Hello! My name SERVER 3
Hello! My name SERVER 1
Hello! My name SERVER 2
Hello! My name SERVER 3
Hello! My name SERVER 1
Hello! My name SERVER 2
Hello! My name SERVER 3
Hello! My name SERVER 1

Thus, we can observe 10 queries being processed by different servers. Each new request has been sent to a new server.

For example, what if one of the servers fails. Let’s go to server 2, for example, and turn it off:

# service nginx stop

When making requery, we will see that the second server is automatically ignored . Thus, the fault-tolerance of the project grows.

# for((r=0; r < 10; r++)); do curl http://10.10.10.99/; done
Hello! My name SERVER 1
Hello! My name SERVER 3
Hello! My name SERVER 1
Hello! My name SERVER 3
Hello! My name SERVER 1
Hello! My name SERVER 3
Hello! My name SERVER 1
Hello! My name SERVER 3
Hello! My name SERVER 1
Hello! My name SERVER 3

Apparently, responses come in circulating mode, the mechanism is called `round-robin`. For a different balancing mechanism, a new directive is to be added to the ʻupstream’ configuration group. The new directive can be `ip_hash` or`least_conn`. This is what the NGINX configuration file will look like if we set up the `ip_hash` method.

upstream balanced {
     ip_hash;
     server 10.10.10.1;
     server 10.10.10.2;
     server 10.10.10.3;
}

server {
     listen 80;

     location / {
          proxy_pass http://balanced;
     }
}

3. Additional Load Balancing Configurations

We will only take for consideration some additional configurations that are typically usedopted for in Load Balancing settings. These are such optional parameters as: `weight`,` max_fails`, `fail_timeout`.

All the above-mentioned parameters are to be specified in the upstream directory group for servers.

The `weight` parameter specifies all the query balancing between servers. Let us consider a situation with a server with the following configuration:

upstream balanced {
     server 10.10.10.1 weight=3;
     server 10.10.10.2 weight=5;
     server 10.10.10.3;
     server 10.10.10.4; 
}

Every 10 queries will be balanced as follows:3 queries will go to the 10.10.10.1 server, the next 5 queries will go to the 10.10.10.2 server, and one query will come to 10.10.10.3 and 10.10.10.4.

The `max_fails` and` fail_timeout` parameters are interrelated. They determine the number of attempts to reestablish connection with the disconnected server . For example, if the `max_fails` parameter is set as 3, and` fail_timeout` = 60, then if there are 3 unsuccessful processing attempts on a server within 60 seconds, then in the next 60 seconds there will be no attempts to send queries to this server. 10 seconds are set for` fail_timeout` and `max_fails` is set 1 as a default.

Here is an example of configurations with these parameters:

upstream balanced {
     server 10.10.10.1 weight=3;
     server 10.10.10.2 weight=5;
     server 10.10.10.3 max_fails=3 fail_timeout=60;
     server 10.10.10.4; 
}

Other additional configurations are available in the official NGINX project by reference:

https://nginx.org/en/docs/http/ngx_http_upstream_module.html

4. Proper Load Balancing logging configuring

If you have an unhandy application running on multiple servers and configured for balancing. Sometimes it is necessary to track the query up to the recieving server. For this purpose, an accurate logging for balancing is required.

Add the major configuring file NGINX /etc/nginx/nginx.conf to the `http`group with a new log format:

http {
        ….
        log_format upstreamlog '[$time_local] $remote_addr $remote_user $server_name  '
           'to: $upstream_addr: $request upstream_response_time '
           '$upstream_response_time msec $msec request_time $request_time';

        include /etc/nginx/sites-enabled/*;
}

Then open the file  /etc/nginx/sites-available/default and add the following  logging values:

upstream balanced {
     ip_hash;
     server 10.10.10.1;
     server 10.10.10.2;
     server 10.10.10.3;
}

server {
     listen 80;

     access_log  /var/log/nginx/default-access.log  upstreamlog;

     location / {
         proxy_pass http://balanced;
     }
}

Then, save the file, check the configuration and restart the web server.

# nginx -t && service nginx restart

If we make 4 queries to Load Balancing, in the Load Balancing /var/log/nginx/default-access.log logs we will find the following log fragment:

[28/Feb/2019:12:12:06 -0500] 172.16.10.12 -   to: 10.10.10.1:80: GET / HTTP/1.1 upstream_response_time 0.002 msec 1551373926.589 request_time 0.002
[28/Feb/2019:12:12:11 -0500] 172.16.10.12 -   to: 10.10.10.2:80: GET / HTTP/1.1 upstream_response_time 0.001 msec 1551373931.577 request_time 0.001
[28/Feb/2019:12:12:12 -0500] 172.16.10.12 -   to: 10.10.10.3:80: GET / HTTP/1.1 upstream_response_time 0.001 msec 1551373932.187 request_time 0.001
[28/Feb/2019:12:12:13 -0500] 172.16.10.12 -   to: 10.10.10.1:80: GET / HTTP/1.1 upstream_response_time 0.001 msec 1551373933.266 request_time 0.001

5. Balancing of DNS Servers under the UTP Protocol

As has been mentioned before, nginx can also perform load balancing at the transport level. In this example we will consider balancing for DNS servers.

If earlier we were setting configures in the / etc / nginx / sites-available / * file, now we will turn to the /etc/nginx/nginx.conf file. In this file we can see the code responsible for contents of the configuration files being included from the ./sites-enabled file.

http {
      ….
      include /etc/nginx/sites-enabled/*;
}

The  /etc/nginx/sites-available/default file is automatically set  as a shortcut (symboli link) in sites-enabled. For example, if we create a new configuration file /etc/nginx/sites-available/new-config, to make this file readable for the web server , we will need to make a shortcut (symbolic link) to this file in a sites-enabled folder. This can be performed with the following command:

ln -s /etc/nginx/sites-available/new-config /etc/nginx/sites-enabled/new-config

Let us go back to these settings that were responsible for  /etc/nginx/sites-available/default, and so, these configs are included in the `http` configuration group. DNS queries cannot be included to this group. We will create for the purpose a new `stream` configuration at the end of the /etc/nginx/nginx.conf file after closing the http {} bracket.

Here is what the configuration file will look like:

http {
        ….
        include /etc/nginx/sites-enabled/*;
}
stream {
      upstream dns_backends {
          server 8.8.8.8:53;
          server 8.8.4.4:53;
      }

      server {
           listen 53 udp;
           proxy_pass dns_backends;
          proxy_responses 1;
     }
}

We have specified here several DNS backend servers, and then configured nginx as a listener for UDP packets through port 53. The `proxy_pass` directive sends backend to the servers. Nginx expects as a default the backend  to send one or more responses. Since there will be one response to one request, we have configured `proxy_responses 1`.

Load Balancing for TCP is relatively similar:

stream {
  upstream tcp_backend {
    server srv1.example.com:3306;
    server srv2.example.com:3306;
  }
  server {
    listen 3306;
    proxy_pass tcp_backend;
  }
}

For more information about Load Balancing visit the official NGINX website:

http://nginx.org/en/docs/http/load_balancing.html