Современные приложения могут обрабатывать множество запросов одновременно, и при этом, даже при высокой нагрузке, они должны возвращать пользователям корректную информацию. Масштабировать приложения можно разными способами:
Горизонтальное масштабирование, с одной стороны, может быть дешевле и менее ограничивать нас в железе — можно просто добавить ещё инстансов приложения. Однако теперь нам необходимо распределять пользовательские запросы между разными экземплярами приложения.
Балансировка нагрузки (load balancing) — это способ распределения запросов к приложению (сетевого трафика) между некоторым количеством устройств.
Балансировщик нагрузки — это программа-посредник, которая располагается между пользователем и группой приложений. Общая логика следующая:
Существует довольно много приложений, которые могут выступать в качестве балансировщика нагрузки, однако одним из самых популярных является Nginx. Его и рассмотрим в данной статье.
Nginx — это универсальный веб-сервер. Он отличается хорошей производительностью, низким потреблением ресурсов и своими широкими возможностями. Nginx можно использовать как:
На сайте можно узнать подробнее про возможности Nginx. Ну а мы перейдём к практике.
Nginx можно установить на все популярные дистрибутивы Linux: Ubuntu, CentOS и другие. В статье мы будем использовать Ubuntu. Чтобы установить Nginx, используем следующие команды:
sudo apt update
sudo apt install nginx
Чтобы убедиться, что всё прошло успешно, можно использовать команду:
systemctl status nginx
Файлы конфигураций для Nginx находятся в каталоге /etc/nginx/sites-available/
. По умолчанию в этом каталоге создаётся файл default
. В нём мы и будем писать нашу конфигурацию.
начала откроем файл конфигурации по умолчанию:
cd /etc/nginx/sites-available/
sudo nano default
Поместим сюда следующую конфигурацию:
upstream application{
server 10.2.2.11; # ip-адреса серверов для распределения запросов между ними
server 10.2.2.12;
server 10.2.2.13;
}
server {
listen 80; # по этому порту будет открываться nginx
location / {
# описываем, куда перенаправлять трафик от nginx
proxy_pass http://application;
}
}
Для настройки балансировки нагрузки в Nginx в конфигурации нужно определить два блока:
upstream
— определяет адреса серверов, между которыми будет распределяться сетевой трафик. Тут мы указываем IP-адреса, порты и, при необходимости, методы балансировки нагрузки. Их мы обсудим далее.server
— определяет способ, с помощью которого Nginx будет получать запросы. Обычно тут указывается порт, доменное имя и другие параметры.proxy_pass
— описывает, куда эти запросы надо перенаправлять. Это название указанного выше upstream
.Таким образом, Nginx используется не только как балансировщик нагрузки, но ещё и как обратный прокси (reverse proxy). Реверс-прокси — это сервер, который располагается между клиентом и экземплярами приложений. Он перенаправляет запросы от клиентов в бэкенд, и при этом может обеспечивать нас дополнительными функциями, например, такими как SSL-сертификаты, логирование и др.
Существует довольно много методов балансировки. Nginx по умолчанию использует алгоритм Round Robin. Он довольно прост. Допустим, у нас есть приложения 1, 2 и 3. Балансировщик нагрузки отправит первый запрос на первое приложение:
Затем на 2:
Затем на 3:
И далее снова на 1:
Рассмотрим на примере. Я развернул два приложения и настроил балансировку нагрузки с помощью Nginx для них.
upstream application {
server 172.25.208.1:5002#first
server 172.25.208.1:5001; #second
}
Давайте посмотрим, как это работает на практике:
Первый запрос отправляет нас на первый сервер, второй запрос — на второй сервер, а затем снова на первый. Однако этот алгоритм имеет ограничение — экземпляры бэкенда будут простаивать просто потому, что ждут своей очереди.
Чтобы избежать простоя серверов, можно использовать некоторый числовой приоритет. У каждого сервера появляется свой вес, который определяет, как много трафика распределяется на конкретный экземпляр приложения. Таким образом мы гарантируем, что более мощные серверы получат больше трафика.
В Nginx приоритет указывается с помощью server weights следующим образом:
upstream application{
server 10.2.2.11 weight=5;
server 10.2.2.12 weight=3;
server 10.2.2.13 weight=1;
}
При такой настройке сервер с адресом 10.2.2.11 получит наибольшее количество трафика, так как у него указан наибольший вес.
Такой подход более надёжен, чем обычный Round Robin, однако он всё ещё имеет недостаток — вручную мы можем указать вес, основываясь на мощности сервера, но при этом сами запросы также могут различаться по скорости выполнения: есть более долгие и тяжёлые, а есть быстрые и незатратные.
Рассмотрим этот метод на примере.
Настройка:
upstream application {
server 172.25.208.1:5002 weight=3; #first
server 172.25.208.1:5001 weight=1; #second
}
Результат:
Как мы видим, теперь каждый четвёртый запрос отправляется на второй сервер.
Что, если отойти от Round Robin? Распределять запросы между серверами можно не просто по порядку, а, например, основываясь на каких-либо параметрах. Отлично подойдёт количество активных соединений с сервером.
Алгоритм Least Connection обеспечивает равномерное распределение нагрузки между экземплярами приложения, как раз основываясь на количестве соединений с сервером. Чтобы его настроить, в блоке upstream надо указать least_conn;
:
upstream application{
least_conn;
server 10.2.2.11;
…
}
Вернёмся к нашему примеру.
Чтобы проверить работу этого алгоритма, я написал скрипт, который отсылает 500 запросов параллельно и смотрит, на какое из приложений попал каждый запрос.
Вот вывод этого скрипта:
Кроме того, этот алгоритм может использоваться вместе с весами для адресов, по аналогии с Round Robin. В этом случае веса будут обозначать количество соединений с этим адресом по отношению к другим адресам — например, в случае с весами 1 и 5, на адрес с весом 5 попадёт в пять раз больше соединений, чем на адрес с весом 1.
Пример такой настройки:
upstream application{
least_conn;
server 10.2.2.11 weight=5;
…
}
А вот настройка для примера:
upstream loadbalancer {
least_conn;
server 172.25.208.1:5002 weight=3; #first
server 172.25.208.1:5001 weight=1; #second
}
И вывод скрипта:
Как мы видим, запросов на первый сервер ровно в три раза больше, чем на второй.
Этот метод работает на основе IP-адреса клиента. Он гарантирует, что все запросы с одного адреса будут доставлены на один и тот же экземпляр приложения. Алгоритм работает следующим образом: высчитывает хэш у адреса клиента и адреса сервера, и использует этот результат как уникальный ключ при балансировке.
Такой подход может быть полезен при blue green deployment, когда мы по очереди обновляем версию каждого бэкэнда. Мы сможем направить все запросы на адрес бэкэнда со старой версией, затем обновить новый и направить часть пользователей на него. Если всё хорошо, можно направить всех пользователей уже на новую версию бэкэнда и обновить старую.
Пример настройки:
upstream app{
ip_hash;
server 10.2.2.11;
…
}
При такой настройке в нашем примере все запросы теперь уходят только на одно приложение:
При настройке балансировщика также важно обнаруживать неполадки с серверами, и, в случае чего, прекращать направлять трафик на «упавшие» экземпляры приложения.
Чтобы балансировщик мог пометить адрес сервера как недоступный, необходимо определить дополнительные параметры в блоке upstream
: failed_timeout
и max_fails
.
failed_timeout
— тут мы указываем время, в течение которого должно произойти определённое количество ошибок соединения, чтобы адрес из блока upstream
стал помечен как недоступный.max_fails
— задаём это самое количество ошибок соединения.Пример настройки:
upstream application{
server 10.2.0.11 max_fails=2 fail_timeout=30s;
…
}
Рассмотрим пример на практике. Я «положу» один из тестовых бэкэндов и добавлю соответствующую настройку.
Первый экземпляр бэкэнда из примера теперь отключен.
Nginx перенаправляет трафик только на второй сервер.
Алгоритм |
Плюсы |
Минусы |
Round Robin |
|
|
Weighted Round Robin |
|
|
Least Connection |
|
|
Weighted Least Connection |
|
|
IP Hash |
|
|
В этой статье мы погрузились в тему load balancing. Узнали, какие существуют методы балансировки нагрузки в Nginx и разобрали их на примере. Эти примеры можно найти в моем профиле на Гитхабе.
Дмитрий, спасибо! Очень хорошо и интересно рассказано.
Подскажите пожалуйста, какую минимальную конфигурацию вы рекомендуете заложить на сервер, используемый для балансировки нагрузки? Можно ли совсем простенький использовать (2Гб памяти, 1 ядро) или надо что-то посущественнее?
Добрый день! Сам Nginx потребляет немного ресурсов. В целом всё зависит от планируемой нагрузки, количества узлов и т.д., но указанной конфигурации должно быть достаточно. Можно воспользоваться мониторингом сервера и самого Nginx и уже в процессе увеличивать ресурсы сервера балансировки, если это необходимо.