Простая балансировка нагрузки в Nginx

Балансировка нагрузки — оптимальное распределение входящего сетевого трафика между набором бэкенд серверов. Задача балансировщика — распределить между ними нагрузку, чтобы избежать перегрузки и обеспечить доступность, масштабируемость, безопасность и производительность приложения.

Есть разные варианты как типов балансировки так и предназначенного для этого ПО.
Мы рассмотрим на примере Nginx.
Nginx — это веб-сервер, который может использоваться в качестве балансировщика нагрузки, распределяя с помощью различных алгоритмов трафик как для балансировки, так и для отказоустойчивости в случае выхода одного из серверов из строя. Среди других функций можно выделить обратный прокси сервер, HTTP кэш и ряд других. Вернемся к load balancer.

Архитектура нашего балансировщика будет следующая:

Запрос приходит на сервер nginx, далее распределяется между сервером 1 и сервером 2.

Запрос -> nginx —-> server1

                      |-------> server2

По умолчанию используется round-robin, это метод при котором запросы распределяются равномерно между заданными серверами (учитывая их вес).

Вес (weight) применяется для того, чтобы сервера не простаивали. Каждому серверу назначается приоритет, на основе которого и распределяется трафик. Из плюсов — серверы с большей пропускной способностью гарантированно получат большую нагрузку и не произойдет сбоев.

Можно сделать еще лучше и к параметру weight добавить least_conn, который распределяет новые запросы на сервера с наименьшим количеством соединений в текущий момент.

На выходе получаем баланс нагрузки с минимальным откликом до ресурса.

Такая конфигурация выглядела  бы следующим образом

events { worker_connections 1024; }

http {
    upstream devopslife_servers {   # Задаем upstream 
        least_conn;
        server server1:80 weight=5;    # сервер 1
        server server2:80 weight=3;    # сервер 2
    }

    server {
        listen 80;

        location / {
            proxy_pass http://devopslife_servers;  # балансировка трафика
        }
    }
}

У нас всего два сервера, мы будем использовать самый простой вариант

events { worker_connections 1024; }

http {
    upstream devopslife_servers {   # Задаем upstream 		
        server server1:80;    # сервер 1
        server server2:80;    # сервер 2
    }

    server {
        listen 80;

        location / {
            proxy_pass         http://devopslife_servers;  # балансировка трафика
        }
    }
}

Мы можем задать еще много параметров, например добавить третий сервер и прописать

server server3:80 backup;

Который будет использоваться только когда первые два недоступны.

Кроме того

server server3:80 max_fails=1 fail_timeout=70s max_conns=500;

max_fails — количество неудачных попыток, чтобы сервер считался недоступным

fail_timeout — сколько считать сервер недоступным и не посылать на него запросы

max_conns — максимальное количество подключений, по умолчанию стоит безлимитное значение. Если заданное количество превышено, запросы на backend перестают приходить.

Перейдем к практике

Подготовим два простых html файла, чтобы было видно что балансировка работает

nano server1.html

<html><body>Server 1 - devopslife.ru</body></html>


nano server2.html

<html><body>Server 2 - devopslife.ru</body></html>

Запускать будем через Docker

nano docker-compose.yml

version: '3'

services:
  # балансировщик
  nginx:
    image: nginx:1.25.0-alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    ports:
      - "8080:80"

  # Сервер 1
  server1:
    image: nginx:1.25.0-alpine
    volumes:
      - ./server1.html:/usr/share/nginx/html/index.html

  # Сервер 2
  server2:
    image: nginx:1.25.0-alpine
    volumes:
      - ./server2.html:/usr/share/nginx/html/index.html

Запускаем и проверяем

docker-compose up -d

Переходим по адресу сервера http://ip_address:8080, обновляем страницу несколько раз и видим как происходит балансировка

Завершаем работу

 docker-compose down

Photo by Nathan Dumlao on Unsplash