View Categories

Create Simple NGINX LB on Docker

8 min read

AFTER Docker is installed if you need to add a user

sudo adduser https://eventguyz.com/wp-content/uploads/2024/05/hosang-i-t-python-02.png

Make sure that user you don’t have to keep typing sudo in front of every docker command

sudo usermod -aG docker,sudo https://eventguyz.com/wp-content/uploads/2024/05/hosang-i-t-python-02.png

NOTE: for CentOS or RHEL change docker,sudo to docker,wheel

CREATE Project Folder (this can be named whatever you want)

mkdir docker-nginx-lb-v2
cd docker-nginx-lb-v2

CREATING APP SERVERS TO TEST WITH

CREATE a Server folder inside your project folder

mkdir server
cd server

START a node project (package.json) in this folder by running:

npm init -y

ADD the required files that index.js will need when we create it next. So while still in the server folder run this command:

npm install express

CREATE index.js also in the server folder

vi index.js
JavaScript
const express = require("express")
const os = require('os')
const app = express()

app.get("/", (req,res) => {
    //res.send("This is the server's response...")
    res.json({ message: 'Ok it works....', hostname: os.hostname()})
})
app.listen(5050,() => {
    console.log( `Server on port 5050,I am sending a response ${os.hostname()}`)
})

To test it, you can run it with the node index.js command and access it from the localhost:5050 port.

curl http://localhost:5050
This is the serverโ€™s responseโ€ฆ

Now that has been validated that the server works, letโ€™s create dockerfile still in the server folder:

vi Dockerfile
Dockerfile
FROM node:19-alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5050
CMD [ "node", "index.js" ]

Since nginx and our virtual servers will run on docker we need to create a common network for them to communicate with each other on.
Here we create a network named loadbalance_net with the following command (you only need to do this once):

docker network create loadbalance_net

Now letโ€™s build and create our 4 virtual servers by running the following commands in order (if you made changes to index.js then rerun this commandโ€ฆ it doesnโ€™t hurt to run it again anyhow):

docker build -t server .

Now run these one by one to spin up the application servers

Bash
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

Now you can validate you can get to each server

Bash
curl http://localhost:1010
curl http://localhost:2020
curl http://localhost:3030
curl http://localhost:4040

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.

NGINX LOADBALANCER section

move up to the root of your project directory

cd ..

verify you are in the right place with pwd command

/home/https://eventguyz.com/wp-content/uploads/2024/05/hosang-i-t-python-02.png/docker-nginx-lb-v2

CREATE nginx folder

mkdir nginx
cd nginx

CREATE nginx.conf file

vi nginx.conf

ROUND ROBIN

This is the simplest and most common type of load balancing, where incoming requests are distributed to servers in a cyclical order. Each server is given an equal chance to process a request, regardless of its current load or performance.

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

ROUND ROBIN – weight

This is a variant of round-robin load balancing, where each server is assigned a weight based on its processing capacity, and requests are distributed accordingly. Servers with higher weights are given a higher proportion of incoming traffic.

Bash
upstream backend {
 server backend_server_1:5050 weight=2;
 server backend_server_2:5050 weight=3;
 server backend_server_3:5050 weight=1;
 server backend_server_4:5050 weight=4;
}          
server {
 listen 80;
 include /etc/nginx/mime.types;
 location / {
 proxy_pass http://backend/;
 }
}

LEAST CONNECTION

In this type of load balancing, incoming requests are sent to the server with the fewest active connections, in order to avoid overloading any one server. This is particularly useful for applications that involve long-lived connections, such as streaming media or gaming.

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

This type of load balancing operates at the application layer of the network stack and uses information such as HTTP headers, cookies, and URL paths to distribute traffic across servers. This allows for more intelligent routing based on application-specific criteria, such as session affinity, content-based routing, or SSL offloading.

Bash
upstream backend {
 server backend_server_1:5050;
 server backend_server_2:5050;
 server backend_server_3:5050;
 server backend_server_4:5050;
 sticky cookie srv_id expires=1h domain=.example.com path=/
}
#expires = time Sets the time for which a browser should keep the cookie. 
###The special value max will cause the cookie to expire on โ€œ31 Dec 2037 23:55:55 GMTโ€. 
###If the parameter is not specified, it will cause the cookie to expire at the end of a browser session.
#domain= Defines the domain for which the cookie is set. Parameter value can contain variables (1.11.5).
#path= Defines the path for which the cookie is set.

SOURCE IP PERSISTENCE

This method uses the IP address of the client to determine which server to send the request to. The IP address is hashed, and the resulting value is used to select the server. This ensures that requests from the same client are always sent to the same server, which can be useful for maintaining session state or other application-specific requirements.

Bash
upstream backend {
 ip_hash;
 server backend_server_1:5050;
 server backend_server_2:5050;
 server backend_server_3:5050;
 server backend_server_4:5050;
}

NGINX LB ADDITIONAL OPTIONS

The following parameters can be defined:

STANDARD OPTIONS (comes with free version)

weight=number

sets the weight of the server, by default, 1.

max_conns=number

limits the maximumย numberย of simultaneous active connections to the proxied server. Default value is zero, meaning there is no limit. If the server group does not reside in theย shared memory, the limitation works per each worker process.

Ifย idle keepaliveย connections, multipleย workers, and theย shared memoryย are enabled, the total number of active and idle connections to the proxied server may exceed theย max_connsย value.

max_fails=number

sets the number of unsuccessful attempts to communicate with the server that should happen in the duration set by theย fail_timeoutย parameter to consider the server unavailable for a duration also set by theย fail_timeoutย parameter. By default, the number of unsuccessful attempts is set to 1. The zero value disables the accounting of attempts. What is considered an unsuccessful attempt is defined by theย proxy_next_upstream, ย fastcgi_next_upstream,ย  uwsgi_next_upstream,ย  scgi_next_upstream,ย  memcached_next_upstream, andย grpc_next_upstreamย directives.

fail_timeout=timesets
  • the time during which the specified number of unsuccessful attempts to communicate with the server should happen to consider the server unavailable;
  • and the period of time the server will be considered unavailable.

By default, the parameter is set to 10 seconds.

backup

marks the server as a backup server. It will be passed requests when the primary servers are unavailable.

The parameter cannot be used along with theย hash,ย ip_hash, andย randomย load balancing methods.

down

marks the server as permanently unavailable.

PAID/COMMERCIAL OPTIONS

resolve

monitors changes of the IP addresses that correspond to a domain name of the server, and automatically modifies the upstream configuration without the need of restarting nginx. The server group must reside in theย shared memory.

In order for this parameter to work, theย resolverย directive must be specified in theย httpย block or in the correspondingย upstreamย block.

route=string

sets the server route name.

service=name

enables resolving of DNSย SRVย records and sets the serviceย nameย (1.9.13). In order for this parameter to work, it is necessary to specify theย resolveย parameter for the server and specify a hostname without a port number.

If the service name does not contain a dot (โ€œ.โ€), then the RFC-compliant name is constructed and the TCP protocol is added to the service prefix. For example, to look up the _http._tcp.backend.example.com SRV record, it is necessary to specify the directive:

server backend.example.com service=http resolve;

If the service name contains one or more dots, then the name is constructed by joining the service prefix and the server name. For example, to look up the _http._tcp.backend.example.com and server1.backend.example.com SRV records, it is necessary to specify the directives:

server backend.example.com service=_http._tcp resolve;
server example.com service=server1.backend resolve;

Highest-priority SRV records (records with the same lowest-number priority value) are resolved as primary servers, the rest of SRV records are resolved as backup servers. If theย backupย parameter is specified for the server, high-priority SRV records are resolved as backup servers, the rest of SRV records are ignored.

slow_start=time

sets theย timeย during which the server will recover its weight from zero to a nominal value, when unhealthy server becomesย healthy, or when the server becomes available after a period of time it was consideredย unavailable. Default value is zero, i.e. slow start is disabled.

The parameter cannot be used along with theย hash,ย ip_hash, andย randomย load balancing methods.

drain

puts the server into the โ€œdrainingโ€ mode. In this mode, only requestsย boundย to the server will be proxied to it.


While still under the nginx folder create Dockerfile

vi Dockerfile
Dockerfile
FROM nginx:stable-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Now we have all the nginx files created, we need to build the Dockerfile

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:

Bash
docker run -p 3000:80 โ€“name nginx_server -d โ€“network loadbalance_net nginx_load_balancer

After this process, there will be 5 containers running. Check it out with docker ps (4 backend app servers and 1 nginxLB)

Now validate the nginx LB is working by running:

curl http://localhost:3000

ROLLBACK or DECOMM

Remove NGINX run the following command

Bash
docker remove /nginx_server

Stop the test APP (nodejs) Servers (you can use the container id instead of /backend_server_1)

Bash
docker stop /backend_server_1
docker stop /backend_server_2
docker stop /backend_server_3
docker stop /backend_server_4

Remove test APP (nodejs) Servers run the following command (this is removing them from the loadbalancer network. you created)

Bash
docker remove /backend_server_1
docker remove /backend_server_2
docker remove /backend_server_3
docker remove /backend_server_4
Protected By
Shield Security