Basic load balancer with NGINX
Tags: devops, systemdesign, nginx, go
Published: 2023-05-16 06:20:00

NGINX is an advanced web browser that can be used as a reverse proxy, load balancer, mail proxy, and caching mechanism. In this short tutorial, we scale a golang REST API service with docker-compose and distribute all the load from the client to these multiple service instances.


![System diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pf317d3mk45n58cjiqwn.png)

The client makes an API call to `http://localhost:8000/` endpoint, which hits the nginx service that internally proxies the request to one of the 3 instances of the REST servers running on the port `8000`. We will be using the [Round robin](https://www.nginx.com/resources/glossary/round-robin-load-balancing/) load-balancing technique that can help us to almost evenly distribute the load across every instance.

1. **build golang servers image with Dockerfile**
    
    ```dockerfile
    FROM golang:1.18-alpine AS builder
    
    RUN mkdir /app
    WORKDIR /app
    COPY . .
    
    RUN CGO_ENABLED=0 GOOS=linux go build -o app cmd/api/main.go
    
    FROM alpine:latest AS production
    WORKDIR /app
    COPY --from=builder /app/app .
    
    CMD [ "./app" ]
    ```
    

1. **docker-compose file -** run the golang servers and nginx load balancer
    
    ```dockerfile
    version: "3.8"
    
    services:
      rest_api:
        build:
          context: ./server
          dockerfile: Dockerfile
        environment:
          - PORT=:8000
        networks:
          - server-network
        command: /start
      
      nginx:
        image: nginx:latest
        volumes:
          - ./conf.d/nginx.conf:/etc/nginx/nginx.conf
        depends_on:
          - rest_api
        ports:
          - 8000:8000
        networks:
          - server-network
          
    networks:
      server-network:
        driver: bridge
    ```
    

1. **nginx conf file** - We'll define events and set the expected maximum client concurrent connections to 1024. We can define a group of REST servers(image=***rest\_api***) inside the **upstream block.** We can also mention the load balancing method inside the **upstream block**. NGINX server will listen on the port `8000` of the host. NGINX will proxy all the requests hitting at `/` route to `app_servers` group.
    
    ```nginx
    events {
        worker_connections 1024;
    }
    
    http {
        upstream app_servers {
            # default load balancing method is Round Robin
            # other ex. least_conn, ip_hash, etc
            server rest_api:8000;
        }
    
        server {
            listen 8000;
    
            location / {
                proxy_pass http://app_servers;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            }
        }
    }
    ```
    

Now we can start multiple instances of golang REST API servers with the following command -

```bash
$ docker-compose up --build --scale rest_api=3
```

NGINX will serve the same request through different REST server instances -


![Output 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2o7ikp6dpnh5dxy26mh.png)


![Output 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfu0xksf3dcfasoex108.png)


![Output 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kqsnakfuip0ctz4l6bs1.png)