Scaling & Load-balancing NodeJS Containers with Nginx
Tags: docker, nginx, node, containerapps
Published: 2022-10-08 12:33:35

So, your NodeJS application is getting loads of requests, and it has become too much for a single server to handle. Now, you don't have a fancy AWS managed service to help you. The solution would be to manually scale your servers horizontally. Horizontally scaling is provisioning replica servers as opposed to increasing the CPU/memory combination of a single server to handle more load.

Luckily, Docker and Docker-compose with Nginx could easily get the job done for you.
Docker could easily scale your servers to any number. With Docker-compose, the job becomes even easier as you don't always have to type in a bunch `docker` commands.

Okay, let's assume you've used Docker to replicate your servers to 3, meaning your NodeJS application is now running in 3 replica containers. The question becomes how would each of the server know when to handle a request? In comes Nginx.

> Nginx: The software load balancer, reverse proxy, web server, & content cache with the enterprise features and support you expect.

Nginx at its core is a web server. 
While your 3 containers are running and not knowing when any of them should rise to the occasion and handle a request, Nginx can help with that situation with its load-balancing feature.

> Load balancing refers to efficiently distributing incoming network traffic across a group of backend servers, also known as a server farm or server pool. - _Nginx_

Nginx in this case, will act as a proxy server, standing between your client requests and your 3 servers.

![Depiction of application load balancing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ezylpnt3ifzgvie112nz.png) © _phoenixNAP_


When a new request comes in Nginx will accept it and route it to one of the servers. It has 3 algorithms it uses to tell which server to route traffic per time. The default of those algorithm is round-robin.

**Round-robin:**
First request to to server1, second request to server2, and third to server3, then repeat

Say your NodeJS server is listening on port 3000, a simple docker-compose script would look something like this:

```
version: '3.9'

services:
  nodeapp:
    build: .
    ports:
      - "3000"
  
  nginx:
    image: nginx:latest
    ports:
      - "4000:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
```
_docker-compose.yml_

The compose script builds your NodeJS app from your Dockerfile and pulls Nginx server from docker public registry. The `nodeapp` service is not mapped to any specific host port because only a single machine can map to a host port at every given time. But the nginx server would be taken requests on port 80, mapped to the host's 4000.

Furthermore, we added the `volumes` directive, to enable to us write a config file to nginx to build. This config is the crux of this setup.

```
events {
  worker_connections  4096;
}

http {
    server {
        listen 80;
        location / {
            proxy_pass http://nodeapp:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}
```
_nginx.conf_

Nginx listens on `/` (home route) on port `80`, then routes traffic to `nodeapp` on port `3000` using its `proxy_pass` directive.

_Complete scripts including Dockerfile and simple Express app are available on my Github_ [https://github.com/zeelz/zeelz-developer/tree/nodejs-docker-compose-nginx](https://github.com/zeelz/zeelz-developer/tree/nodejs-docker-compose-nginx)


[Video Walkthrough](https://www.youtube.com/watch?v=4Bm2lA1m7xk)