Ранее я немного касался настройки nginx в качестве web сервера или proxy сервера для web приложения. Сегодня я расскажу, как настроить балансировку нагрузки бэкендов с помощью nginx. В качестве примера возьму простую ситуацию, когда трафик будет распределяться между тремя одинаковыми серверами.
Научиться настраивать MikroTik с нуля или систематизировать уже имеющиеся знания можно на углубленном онлайн-курcе по администрированию MikroTik. Автор курcа – сертифицированный тренер MikroTik Дмитрий Скоромнов. Более 40 лабораторных работ по которым дается обратная связь. В три раза больше информации, чем в MTCNA.
Содержание:
Введение
Ранее я подробно рассказывал о базовой настройке nginx и настройки его в качестве proxy сервера. Так же у меня есть подробная статья по настройке web сервера на базе Centos 7. Сегодня я расскажу о том, как настроить балансировку нагрузки с помощью nginx. Чаще всего это необходимо для балансировки нагрузки между бэкендами и обеспечения отказоустойчивости в работе web сервиса. Существуют различия в возможностях тонкой настройки балансировщика в бесплатной и платной версии nginx plus. Это один из первых моментов, где я столкнулся с тем, что мне стали необходимы функции платной версии, которых не было в бесплатной. Но цена nginx plus велика.
Я рассмотрю простой пример, где у нас в качестве web сервера выступает nginx, который распределяет запросы на три равноценных сервера. В случае выхода из строя одного из серверов, он все запросы будет адресовать оставшимся в работе серверам. Теоретически это должно проходить незаметно для клиентов. Практически же есть масса нюансов в работе этого инструмента. Постараюсь обо всем этом рассказать.
Добавление бэкендов
Для того, чтобы начать балансировать нагрузку, необходимо добавить бэкенды в настройки nginx. Для примера, я возьму отдельный виртуальный хост. Идем в его конфиг и в самое начало добавляем три бэкенда через директиву upstream.
upstream cache-api { server 10.32.18.6:8080; server 10.32.18.7:8080; server 10.32.18.8:8080; }
Сейчас я не касаюсь вопросов тонкой настройки балансировки. Будем идти от простого к сложному. На текущий момент мы добавили три сервера, на которые будет распределяться нагрузка. Далее в настройках виртуального хоста добавляем location, запросы к которому будем равномерно распределять.
location / { proxy_pass http://cache-api/; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; }
Этого минимального набора настроек достаточно, чтобы nginx начал равномерно распределять запросы между двумя серверами. Но в реальной ситуации требуется более детальная настройка балансировщика. Для этого используются следующие параметры:
weight | Задает вес сервера, по умолчанию 1. Чем больше вес сервера, тем пропорционально больше запросов он будет принимать от балансировщика. |
max_conns | Ограничивает максимальное число одновременных активных соединений к проксируемому серверу Значение по умолчанию равно 0 и означает, что ограничения нет. |
max_fails | Задаёт число неудачных попыток работы с сервером, которые должны произойти в течение времени, заданного параметром fail_timeout, чтобы сервер считался недоступным на период времени, также заданный параметром fail_timeout. Дефолтное значение - 1. |
fail_timeout | Задаёт время, в течение которого должно произойти заданное число неудачных попыток работы с сервером для того, чтобы сервер считался недоступным и время, в течение которого сервер будет считаться недоступным. По умолчанию параметр равен 10 секундам. |
backup | Помечает сервер как запасной сервер. На него будут передаваться запросы в случае, если не работают основные серверы. |
down | Помечает сервер как постоянно недоступный. |
Подробнее об этом написано в официальной документации, в описании модуля ngx_http_upstream_module. К примеру, конфиг бэкендов для балансировки может быть таким.
server 10.32.18.6:8080 max_fails=2 fail_timeout=10s; server 10.32.18.7:8080 max_fails=2 fail_timeout=10s; server 10.32.18.8:8080 max_fails=2 fail_timeout=10s;
С такими настройками после двух неудачных попыток соединения в течении 10 секунд, бэкенд будет выведен из работы на те же 10 секунд.
Метод балансировки
Соединения к серверам для балансировки нагрузки могут распределяться по различным правилам. Существуют несколько методов распределения запросов. Я перечислю основные:
- round-robin - используется по умолчанию. Веб сервер равномерно распределяет нагрузку на сервера с учетом их весов. Специально указывать этот метод в конфигурации не надо.
- least-connected - запрос отправляется к серверу с наименьшим количеством активных подключений. В конфигурации данный параметр распределения запросов устанавливается параметром least_conn.
- ip-hash - используется хэш функция, основанная на клиентском ip адресе, для определения, куда направить следующий запрос. Используется для привязки клиента к одному и тому же серверу. В предыдущих методах один и тот же клиент может попадать на разные серверы.
- hash - задаёт метод балансировки, при котором соответствие клиента серверу определяется при помощи хэшированного значения ключа. В качестве ключа может использоваться текст, переменные и их комбинации.
- random - балансировка нагрузки, при которой запрос передаётся случайно выбранному серверу, с учётом весов.
В платной версии существуют дополнительный более продвинутый метод распределения нагрузки - least_time, при котором запрос передаётся серверу с наименьшими средним временем ответа и числом активных соединений с учётом весов серверов.
Пример настройки:
upstream cache-api { ip_hash; server 10.32.18.6:8080; server 10.32.18.7:8080; server 10.32.18.8:8080; }
Проблемы балансировки нагрузки
Проблем при балансировки нагрузки с помощью nginx может быть масса. В обычном рабочем проекте нельзя просто взять и разделить нагрузку на несколько серверов. Само приложение, его БД должны быть готовы к этому. Первое, с чем вы столкнетесь - как правильно определить, что с сервером проблемы. В бесплатной версии nginx нет никаких инструментов для того, чтобы определить, что ваш бэкенд отвечает правильно.
Допустим, один из серверов перегружен и он отдает неправильные ответы. То есть он жив, отвечает, но ответы нам не подходят. Балансировщик будет считать, что все в порядке, запросы на проблемный сервер будут продолжать идти. Nginx исключит его из списка бэкендов только тогда, когда он полностью перестанет отвечать. Но это лишь малая часть проблем, которые могут приключиться. По моему опыту, чаще всего сервера не отваливаются полностью, а начинают тупить или отдавать, к примеру 502 ошибку. В бесплатной версии nginx будет считать, что все в порядке.
Для того, чтобы анализировать ответ бэкенда и в зависимости от этого ответа, решать, в каком состоянии находится сервер, вам необходим модуль ngx_http_upstream_hc_module. Он доступен только в коммерческой подписке. С помощью этого модуля можно тестировать код ответа, наличие или отсутствие определённых полей заголовка и их значений, а также содержимое тела ответа. Без этих данных качественно настроить работу балансировщика трудно.
Отмечу еще несколько полезных настроек, на которые надо обратить внимание, при настройке балансировки нагрузки с помощью nginx:
- proxy_connect_timeout - задаёт таймаут для установления соединения с проксированным сервером. При отсутствии ответа за указанное время сервер будет считаться неработающим. Дефолтное значение 60 секунд. Это очень много, если у вас большие нагрузки. За минуту скопится огромное количество висящих соединений, которые мог бы обработать другой бэкенд.
- proxy_read_timeout - задаёт таймаут при чтении ответа проксированного сервера. Дефолт тоже 60 секунд. Чаще всего имеет смысл уменьшить значение.
Полный конфиг балансировщика nginx
Привожу пример полного конфига виртуального хоста для балансировки нагрузки на примере nginx. Это не пример с рабочего сервера, так что возможны какие-то мелкие ошибки или опечатки. Не тестировал конфиг, так как это не готовое how to, а просто рекомендации и описание. Составил его чтобы было представление, как все это выглядит в единой конфигурации.
log_format upstream '$remote_addr - $host [$time_local] "$request" ' 'request_length=$request_length ' 'status=$status bytes_sent=$bytes_sent ' 'body_bytes_sent=$body_bytes_sent ' 'referer=$http_referer ' 'user_agent="$http_user_agent" ' 'upstream_status=$upstream_status ' 'request_time=$request_time ' 'upstream_response_time=$upstream_response_time ' 'upstream_connect_time=$upstream_connect_time ' 'upstream_header_time=$upstream_header_time'; upstream cache-api { ip_hash; server 10.32.18.7:8080 max_fails=2 fail_timeout=10s; server 10.32.18.6:8080 max_fails=2 fail_timeout=10s; server 10.32.18.8:8080 max_fails=2 fail_timeout=10s; } server { listen 443 ssl http2; server_name cache-api.sample.com; access_log /var/log/nginx/cache-api-access.log upstream; error_log /var/log/nginx/cache-api-error.log; ssl_certificate /etc/ssl/sample.com.crt; ssl_certificate_key /etc/ssl/sample.com.key; root /var/www/html; location / { proxy_pass http://cache-api/; proxy_read_timeout 15; proxy_connect_timeout 3; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; } }
За подробностями параметров секции с proxy_pass переходите в отдельную статью на эту тему.
Заключение
Мне не приходилось пробовать в деле никаких других балансировщиков, кроме Nginx. Знаю, что есть haproxy, но попробовать так и не дошли руки. Бесплатная версия nginx очень слабо подходит для полноценной балансировки крупного проекта, либо я просто не понимаю, как его правильно использовать. Реально не хватает тех фич, которые есть в Nginx Plus. До того момента, как не начал использовать балансировщик, не понимал толком, что там такого в платной версии. Теперь прекрасно понимаю :)
Готовые примеры балансировки с использованием фич Nginx Plus приведены в этой статье. Так же обращаю внимание на формат логов, который для удобства стоит подправить под использование бэкендов. Этот вопрос я рассмотрел отдельно в статье про мониторинг производительности бэкендов с помощью elk stack.
Буду рад любым комментариям, ссылкам, советам по существу затронутой темы. Я в ней новичок. Ни на что не претендую, поделился своим опытом.
На углубленном курсе "Архитектура современных компьютерных сетей" вы с нуля научитесь работать с Wireshark и «под микроскопом» изучите работу сетевых протоколов. На протяжении курса надо будет выполнить более пятидесяти лабораторных работ в Wireshark.
Благодарю за статью!
Кратко и понятно, особенно оценка между nginx и nginx plus.
сколько стоит балансировка нагрузки ?
В каком плане? Не понял вопроса.
Frontend это пользовательская сторона сайта, backend - админская. А вы в эти термины вкладываете совсем другой смысл.
Почему это backend - админская? Фронт, это то, с чем взаимодействует пользователь, а бэк - остальная логика, скрытая от пользователя. К админам это не имеет никакого отношения.
Привет, отличная статья. Есть вопрос, если у меня в рамках одного сервера по несколько веб-приложений и нужно равномерно распредять по ним тоже - это можно как-то учесть в настройке nginx load balancer?
Не понял, в чем конкретно вопрос. Nginx может заниматься балансировкой. Его чаще всего для этого и используют. Равномерной балансировкой в том числе.
Если у запараллеленных серверов порты 100 то выходит у балансировщика должен быть гиг ? чтобы не было узкого места?
По идее да, если канал утилизируется полностью.
Спасибо. Просто ещё нет понимания что будет узким местом в проекте выполнение php или раздача файлов. Если php то будет привалировать процессорах нагрузка и надо будет покупать больше ядер на арендованных Vps . Если трафиковая то увеличивать скорость портов на Vps.
Чтобы канал 100 мегабит стал узким местом веб проекта на php, он должен быть либо очень сильно нагруженным, либо отдавать много статики. Если статики реально много, то имеет смысл рассмотреть вопрос ее хранения где-то отдельно, не на vps. Есть специализированные сервисы для этого (CDN).
Средняя отдача сайта пусть по минимуму 1 метр на страницу, скважность скачиваний каждые 10 секунд. время использования 1 час (учебный курс). 1 пользак генерирует 100 кб сек трафика, что равняется 120 пользователям в час( что не много, но учебные курсы можно разложить по разным серверам и вручную направлять пользователей а для доступного инета при среднем времени нахождения на сайте равном 5 мин возможно всего 1440 уникальных пользователей в час на 100 мбитах( что оч мало((
Достаточно много провайдеров предоставляют канал 1 гигабит. Правда, чтобы он был гарантированный и безлимитный, придется заплатить много денег. Точно не помню цифр, но вроде 10-30 т.р. это может стоить в зависимости от прова.
Есть провы со скоростью до гигабита за цену в 1 т р как доберусь и протестирую отпишусь ещё есть вариант комбинировать с хостингом тяжелую статику класть туда а динамику оставлять на vps
Там где 1 т.р. скорее всего не гарантированная скорость, а максимально возможная в гигабит. Это две большие разницы.
Знаю, нужно будет тестить а чвс пик и то нижний предел будет плавать постоянно. можно поступить по хитрому, набрать 5 вдс по 200мбит или до 1 гига и получить гарантированный гиг за 1 т.р или не гарантированые 5 гигов)) а своим днс ом , балансировать адреса)
Добрый день , Владимир спасибо за статью и за вашу труд
меня зовут Нурмухаммад из Ташкента , я новичок в ИТ сфере поэтому не посмейтесь если неправильный вопрос задам
у меня есть один сервер к нему подключаються по порту 6677 сейчас смотрю активных соединений очень много и это приводить тормозить сервиса за портом 6677 теперь вопрос если я nginx настрою и все запросы первым в nginx приниму к серверу чуть легче станет? здесь у меня получается nginx как прокси.
Серверу станет легче только в том случае, если вы на nginx будете как-то обрабатывать соединения или лимитировать. В общем случае он их как есть будет передавать дальше и никакой разницы не будет. Это не способ снизить нагрузку. Nginx в режиме прокси позволяет управлять запросами в зависимости от потребностей. Например, отбрасывать вредные подключения, или как-то ставить ограничение на количество запросов в единицу времени или на основе ip адреса.
Благодарю, статья очень пригодилась.
Есть вопрос к автору, как организовать реверсивный прокси для доступа к 2-м одинаковым бекендам которые реализуют websockets?
Проблема в том что в текущей настройке установлен алгоритм round-robin, а для websockets я так понимаю это не подходит.
Благодаря Вашей статье я понял что проблема могла бы быть решена алгоритмом ip-hash, но это не подходит для моего случая так как у меня в таком случае будет большой перекос в нагрузке к одному из бэкенд серверов т.к. подавляющее большинство клиентов выходят с одного ip.
Спасибо.
А какая конкретно проблема с websockets? Из описания не понял.
Честно говоря тот участок который отвечает за вебсокеты делал не я но насколько я понимаю проблему, для вебсокетс нужен персистентный коннект с единственным бэкендом а в случае с раунд-робин этого добиться невозможно.
Хорошая статья. Спасибо. А какие вы дадите рекомендации для работы например с wordpress? Базу выносим на отдельный сервер а контент на nfs-шару и биндить ее к каждому бекенд серверу? Или какие существуют best practices?
Тут нету best practices, все индивидуально, в зависимости от бюджетов и нагрузок. В общем случае лучше нарастить мощности сервера, если это возможно. Так проще во всех отношениях. Дальше отдельно выносится сервер с БД, подключаются различные кэши, если еще не было, могут тоже на отдельные сервера. Контент, в основном, стараются вынести на S3 совместимые хранилища, чаще всего не свои. Но тут нужно по деньгам считать, что и как удобнее и выгоднее.