Для очень маленьких веб-сайтов обычно достаточно одного сервера nginx, на котором запущено веб-приложение. Тем не менее, это не работает хорошо для крупных сайтов. В таких случаях мы используем метод “балансировки нагрузки”, который распределяет входящие запросы на ряд других серверов, на каждом из которых выполняется копия веб-приложения. Это одна из техник, которые используют крупные веб-сайты для поддержания работоспособности даже при больших объемах трафика.
Хотя большинство используют nginx в качестве HTTP-сервера, он также имеет встроенные функции балансировки нагрузки. В этой статье мы рассмотрим, как настроить его в вашей системе.
Основы балансировки нагрузки
Балансировка нагрузки – это метод распределения рабочих нагрузок по нескольким вычислительным ресурсам, таким как компьютеры, сетевые ссылки или диски. Он направлен на улучшение использования ресурсов, максимизацию пропускной способности, улучшение времени отклика и обеспечение отказоустойчивости. В этой статье мы поговорим конкретно о типах балансировки нагрузки, поддерживаемых nginx. Однако другие приложения (например, серверы баз данных) также могут использовать балансировку нагрузки.
Типичная настройка балансировки нагрузки HTTP выглядит так, как показано на рисунке ниже. Есть много пользователей, которые отправляют запросы, и балансировщик нагрузки распределяет их по многим серверам (так называемые бэкэнды). Каждый из этих бэкэндов запускает копию одного и того же приложения. Когда эти серверы отправляют ответ, балансировщик нагрузки отправляет их прокси клиентам.
Балансировщики нагрузки обычно классифицируются как аппаратные и программные. Первые, являются специализированным оборудованием и несут специализированные интегральные схемы (ASIC), которые очень эффективно маршрутизируют трафик. С другой стороны, программные балансировщики нагрузки, такие как nginx или HAproxy, выполняют балансировку нагрузки в программном обеспечении.
Кроме того, балансировка нагрузки может выполняться на различных сетевых уровнях. Как следует из названия, балансировщики нагрузки уровня 4 балансируют трафик, проверяя запросы и ответы на транспортном уровне. Кроме того, существуют подсистемы балансировки нагрузки уровня 7, которые проверяют трафик на уровне приложений. Последний немного более мощный, поскольку он имеет высокий уровень видения вещей. Например, балансировщик 7-го уровня, который знает о HTTP, может проверять URL-адреса и отправлять запросы различным наборам серверных частей в зависимости от URL-адреса.
Nginx может выполнять как балансировку нагрузки уровня 4 для TCP и UDP, так и балансировку нагрузки HTTP уровня 7. В следующих нескольких разделах мы рассмотрим, как настроить nginx для этой цели.
В большинстве дистрибутивов Linux файл конфигурации nginx находится в
/etc/nginx/nginx.conf
. Однако в Debian/Ubuntu этот файл разделен на два разных:/etc/nginx/nginx.conf
для основной конфигурации и/etc/nginx/sites-enabled/default
для отдельных веб-сайтов, которые вы размещаете. Если вы используете Debian/Ubuntu, вы должны добавитьupstream
блоки в первый, а блокlocation
во второй.
Балансировка нагрузки веб-приложений
Прежде чем продолжить, вам нужно настроить несколько внутренних серверов, на которых будет работать наше веб-приложение. На этих серверах будет работать HTTP-сервер на каждом из этих серверов. В нашем примере мы предположим, что мы уже настроили их, и эти серверы доступны на 192.168.0.1
, 192.168.0.2
и 192.168.0.3
. Конечно, они не должны быть внутренними IP-адресами – они также могут быть внешними IP-адресами или именами хостов.
Теперь есть две вещи, которые вам нужно настроить в качестве балансировщика нагрузки. Во-первых, в разделе http
необходимо указать набор внутренних серверов с блоком upstream
. Затем вам необходимо передать эти запросы на серверы. Для этого откройте файл конфигурации и добавьте следующие строки:
http {
upstream backend1 {
server 192.168.0.1;
server 192.168.0.2;
server 192.168.0.3;
}
server {
listen 80;
location / {
proxy_pass http://backend1;
}
}
}
Это говорит nginx о необходимости передавать HTTP-запросы для любого URL-адреса на набор внутренних серверов, которые мы назвали backend1
. Если вы хотите передать только определенные URL-адреса, вы можете сделать что-то вроде:
location /shop {
proxy_pass http://backend1;
}
Это будет соответствовать всем URL-адресам, начинающимся с “/shop”, и передавать их на серверы бэкэнда. Вы также можете расширить эту концепцию, чтобы передавать разные URL-адреса различным бэкэндам. В этом примере мы добавили еще один бэкэнд для серверов, на которых размещается блог. Затем мы настроили nginx для передачи URL-адресов, начинающихся с “/blog”, новому бэкэнду.
upstream backend1 {
# list of servers
}
upstream backend2 {
# list of servers
}
location /shop {
proxy_pass http://backend1;
}
location /blog {
proxy_pass http://backend2;
}
Nginx также поддерживает пару прокси для нескольких других протоколов с такими директивами, как FastCGI и uWSGI с директивами fastcgi_pass и uwsgi_pass. Скажем, например, что на каждом внутреннем сервере на порту 9000 работает демон PHP-FPM, и вы хотите передавать запросы к ним через прокси. Вот как будет выглядеть конфигурация:
http {
upstream backend1 {
server 192.168.0.1:9000;
server 192.168.0.2:9000;
server 192.168.0.3:9000;
}
server {
listen 80;
location / {
fastcgi_pass backend1;
}
}
}
Стратегии балансировки нагрузки
У nginx есть несколько стратегий выбора сервера для отправки запросов. По умолчанию он использует алгоритм циклического перебора для определения сервера, на который должен быть отправлен запрос. Однако доступны и другие стратегии, которые вы можете включить вручную. Стратегия least_conn
выбирает сервер, который обрабатывает наименьшее количество соединений. С другой стороны, стратегия ip_hash
выбирает серверы на основе результата запуска хэш-функции на IP. Это означает, что запросы с одного и того же IP-адреса попадают на один и тот же сервер.
Чтобы использовать стратегию, отличную от принятой по умолчанию, просто укажите ее имя в upstream
блоке:
upstream backend {
ip_hash;
server 192.168.0.1;
server 192.168.0.2;
server 192.168.0.3;
}
Существует также универсальная hash
директива, которую можно использовать с любым значением HTTP. В этом примере мы распределили запросы на основе URL:
hash $request_uri;
Кроме того, вы можете изменить стратегию по умолчанию, назначив приоритет серверу. По умолчанию все серверы имеют приоритет 1, что означает, что nginx не “предпочитает” один сервер другому, кроме применения правил используемой стратегии. Пример конфигурации с приоритетами выглядит следующим образом:
upstream backend {
server 192.168.0.1 weight=4;
server 192.168.0.2 weight=2;
server 192.168.0.3;
}
Как вы можете сказать, мы используем стратегию циклического перебора в этом примере. С приоритетами, которые мы показали в примере выше, каждые 4 из 7 запросов будут отправляться на первый сервер, 2 из них на второй и 1 на третий. (Как мы упоминали ранее, приоритет по умолчанию равен 1, поэтому мы оставили его в приведенном выше примере.)
Расширенные настройки
Когда nginx передает запросы на эти серверы, он также пассивно выполняет проверку работоспособности.
Если серверу не удается отправить ответ, он автоматически помечается
как таковой, и nginx в течение некоторого времени избегает отправки ему
запросов. Значение fail_timeout
контролирует время, до которого nginx избегает отправки запросов на сервер. Значение max_fails
устанавливает количество запросов, которые должны завершиться неудачей, после чего nginx помечает сервер как таковой.
В приведенном ниже примере мы установили max_fails
3, а fail_timeout
20 секундам для первого сервера:
upstream backend {
server 192.168.0.1 max_fails=3 fail_timeout=20s;
server 192.168.0.2;
}
Кроме того, вы можете пометить определенный сервер, который будет обрабатываться по-разному. Если вы выполняете обслуживание на одном из внутренних серверов, вы можете пометить его как down
. Запросы, которые будут обрабатываться этим сервером, теперь отправляются на следующий сервер в группе. Вы также можете пометить определенный сервер в качестве backup
сервера. Запросы будут переданы ему, когда все остальные серверы будут недоступны. Пример этих двух директив показан ниже:
upstream backend {
server 192.168.0.1;
server 192.168.0.2;
server 192.168.0.3 down;
server 192.168.0.4 backup;
}
Некоторые общие проблемы
В зависимости от типов приложений, которые вы развернули, балансировщик нагрузки может вызвать определенные проблемы. Часто вы получите 404 или другие ошибки из веб-приложения. Обычно это происходит потому, что заголовок “Host” содержит адрес внутреннего сервера вместо его исходного значения. Чтобы исправить это, вы можете вручную установить его в блоке location:
location / {
proxy_set_header Host $host;
proxy_pass http://backend;
}
Многие сложные веб-приложения хранят состояние локально, и они могут не работать, если балансировщик нагрузки распределяет запросы сеанса по разным серверам. Чтобы решить эту проблему, балансировщик нагрузки должен отслеживать исходный внутренний сервер, который ответил на первоначальный запрос. Эта концепция известна как “постоянство сеанса”. К сожалению, эта функция доступна только в коммерческой версии nginx. Однако, в качестве обходного пути, вы можете попробовать директиву ip_hash
, которая будет перенаправлять запросы с одного и того же IP на один и тот же внутренний сервер. IP-адреса обычно меняются не очень часто, поэтому это может работать достаточно хорошо для вашего варианта использования.
Балансировка нагрузки nginx транспортного уровня
Как мы упоминали ранее, nginx также может выполнять балансировку нагрузки на транспортном уровне. Синтаксис немного отличается от того, который мы видели ранее. В частности, как upstream
и server
разделы содержатся внутри stream
блока.
Чтобы использовать эту функцию, вы должны скомпилировать nginx с ключом --with-stream
. В Debian и Ubuntu версия nginx в репозиториях уже скомпилирована с этим флагом, поэтому вы можете использовать ее напрямую.
Предположим, у вас есть несколько DNS-серверов, и вы хотите использовать nginx в качестве балансировщика нагрузки. Вам просто нужно поместить это в файл конфигурации:
stream {
upstream dns_backends {
server 8.8.8.8:53;
server 8.8.4.4:53;
}
server {
listen 53 udp;
proxy_pass dns_backends;
proxy_responses 1;
}
}
Здесь мы определили несколько бэкэндов DNS, а затем настроили nginx для прослушивания входящих пакетов UDP через порт 53. Директива proxy_pass
отправляет их на бэкэнд-серверы. Также по умолчанию nginx ожидает, что серверная часть может отправить один или несколько ответов. Поскольку будет один ответ на один запрос, мы установили proxy_responses
в 1.
Балансировка нагрузки с TCP довольно похожа.
stream {
upstream tcp_backend {
server srv1.example.com:3306;
server srv2.example.com:3306;
}
server {
listen 3306;
proxy_pass tcp_backend;
}
}
Здесь также применяются стратегии балансировки нагрузки и другие параметры конфигурации, которые мы описали ранее.
Заключение
Nginx предоставляет мощные возможности балансировки нагрузки с широким спектром опций. Если вы хотите узнать больше, обязательно ознакомьтесь с официальной документацией .
Если вам понравился этот пост, пожалуйста, поделитесь им.