avatar Ubuntu Балансировка сетевой нагрузки с помощью Nginx под Ubuntu

Когда проект дорастает до такого масштаба, что выделенный сервер перестает справляться с нагрузкой, возникает вопрос о ее разделении между несколькими серверами. В данном вопросе довольно много тонкостей и зачатую, когда нагрузка уже возросла, а бюджет за ней не поспевает, данный вопрос начинает стоять довольно остро и тут уже появляется необходимость выжать максимум из имеющегося в наличии железа. В принципе, тюнить и оптимизировать можно почти бесконечно, но мы предположим ситуацию что у нас есть оборудование, на основе которого нам необходимо все это развернуть.

Данное руководство, не имеет привязки к версии ОС и актуально для всех версий Debian и Ubuntu, а также легко адаптируется под другие дистрибутивы Linux.
Для лучшего понимания, привожу схему работы, в которой у нас будет использоваться 3 web сервера и 1 сервер, в качестве балансировщика нагрузки. SQL сервер мы использовать не будем, вообще-то, там можно установить целую кучу различных серверов реляционных баз данных (MySQL, MSSQL, PostgreSQL,ORACLE, и многие другие, да на худой конец Sybase-будь он не ладен) или NoSQL системы, на схеме изображено исключительно для понимания процесса, т.к. это тема отдельная и не простая. Там можно поставить несколько SQL серверов с репликацией данных, но там возникают проблемы с разрешением конфликтов, в принципе проблема решаемая, но не относящаяся, в данный момент, к этой теме, короче, будет все, кроме SQL сервера!

Схема работы системы балансирования сетевой нагрузки

Для лучшего понимания, попробую объяснить подробнее то, что я изобразил на схеме: У нас есть небольшая сеть из 4х серверов, 3 из них работают как WEB сервера и находятся внутри локальной сети, в интернет торчит только 1 сервер, на нем работает Nginx, он виден в интернет и пользователи подключаются непосредственно к нему, он, в свою очередь, передает пользовательские запросы в локальную сеть, на основные WEB сервера, которые его обрабатывают и возвращают готовый результат, который отдается пользователям. Зачастую, никаких других операций на сервере, балансирующим сетевую нагрузку, не производится и для его работы достаточно самого простого и дешевого железа или VPS. Операционную систему можно раскатывать из заранее подготовленного образа, тогда время простоя, при выходе балансировщика из строя, будет минимальным. Пользователи не будет знать-какое количество серверов скрывается за ним, а их могут быть десятки и сотни, но в сети виден всего один, можно развернуть дополнительный балансировщик и запросы к нему распределять на основе службы DNS как это реализовано например у mail.ru
Если в командной строке Windows набрать:

nslookup mail.ru


В ответ мы получим нечто подобное:


Из чего можно сделать вывод, что в интернет у них торчит только 4 сервера или маршрутизатора. Там конечно много нюансов, думаю что там используется и GeoIP и многое другое, для определения местоположения пользователя и предоставления ему ближайшего сервера, скорость соединения с которым, будет максимально быстрой.
Если набрать повторно:
nslookup mail.ru

В ответ мы получим:


Как видно из скриншота, первым в ответе будет уже другой IP, из тех 4х, что были присланы ранее. Сколько и чего, находится за этими IP адресами, знают только в mail.ru, кстати, на досуге пробуйте пройтись по крупным сетевым сервисам, можете узнать для себя что-то новое… Алгоритм распределения запросов называется Round-robin, в нашей схеме он также будет использован и является, по сути, основой всей системы.

Где еще используются подобные схемы распределения нагрузки?! Например: Для создания непотопляемых torrent треккеров, сами сервера могут тихо стоять в дата-центре, а наружу торчит только 1 в совершенно левой стране и все запросы идут через него, который в свою очередь передает их на основные для обработки. В случае отключения данного сервера по жалобе, то ему быстро находится замена в виде покупки самого дешевого VPS в другой стране и в течении 1-2 часов треккер снова доступен, ведь основные мощности никуда не переезжали -привет правообладателям, и все работает как раньше. Можно конечно и данную схему поломать, но это отдельная песня и без административного ресурса будет довольно сложно.
Думаю что смысл понятен, если нет, то прошу в комментарии- постараюсь и на эти вопросы ответить.

Работа системы, отдаленно напоминает технологию NLB от Microsoft (кто с ней работал, тот думаю поймет о чем я), которая не отслеживает нагрузку на каждой отдельной ноде, а просто раскидывает пользовательские подключения между серверами, по заранее определенному алгоритму, например 50/50, когда все пользовательские подключения равномерно распределяются между узлам серверной фермы. Правда у NLB есть ограничение в 32 сервера, на одну серверную ферму, система построенная на Linux данного ограничения лишена, а узким местом можно рассматривать сетевую подсистему на самом балансировщике. По сути мы будем с вами стоить кластер распределения нагрузки, что такое кластер и на кой, нужна вся эта кластеризация, можно прочитать в Википедии
Для примера, буду использовать доменное имя example.org, для него мы и будем распределять нагрузку.
На этом с теорией все, переходим к практике.

1) Настраиваем сервер балансировки нагрузки


Предполагается что на сервере установлено 2 сетевых интерфейса, один будет смотреть в интернет, другой в локальную сеть.
Нам понадобится собрать Nginx и установить его, поверх более старой версии.
Сама сборка из исходников стандартна и не изменилась ни капли.

Поднимаем права до root:
sudo su


Сначала нам необходимо установить необходимые компоненты для сборки+древнюю версию nginx из репозиториев:
aptitude install libpcre3-dev libcurl4-openssl-dev gcc nginx


Качаем свежие исходники, стабильного релиза Nginx, на момент написания, была доступна версия 1.0.10:
wget http://nginx.org/download/nginx-1.0.10.tar.gz


Распакуем их:
tar -zxvf nginx-1.0.10.tar.gz


Переходим в распакованную директорию:
cd nginx-1.0.10


Сборка и установка:
./configure --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx.pid \
--user=www-data \
--group=www-data \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_gzip_static_module \
--with-mail \
--with-mail_ssl_module
make
make install


Запускаем собранное:
/etc/init.d/nginx start


Теперь нам необходимо создать виртуальный хост example.org запросы которого, мы и будем распределять.
nano /etc/nginx/site-avelible/example.org


Добавим в него следующее:

upstream backend {
    server 192.168.10.11:8080;
    server 192.168.10.12:8080;
    server 192.168.10.13:8080;
}

server {
    listen    80;
    server_name  example.org;
    location ~* \.()$ {
    root   /var/www/example.org;  }
    location / {
    client_max_body_size    10m;
    client_body_buffer_size 128k;
    proxy_send_timeout   90;
    proxy_read_timeout   90;
    proxy_buffer_size    4k;
    proxy_buffers     16 32k;
    proxy_busy_buffers_size 64k;
    proxy_temp_file_write_size 64k;
    proxy_connect_timeout 30s;
    proxy_pass   http://backend;
    proxy_set_header   Host   $host;
    proxy_set_header   X-Real-IP  $remote_addr;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
       }
location ~* /.(jpg|jpeg|gif|png|css|mp3|avi|mpg|txt|js|jar|rar|zip|tar|wav|wmv)$ {
root    /var/www/example.org;}
 }


Сохраняем и выходим, нам также необходимо добавить символическую ссылку в sites-enabled.
ln -s /etc/nginx/sites-available/example.org /etc/nginx/sites-enabled/


Если мы попробуем перезапустить nginx, то он отвалится с ошибкой, т.к. отсутствует директория по пути /var/www/example.org, создадим ее:
mkdir -p -m 754 /var/www/example.org


Предоставим права пользователю www-data
chown -R www-data: /var/www/example.org

На этом можно считать основную работу, по настройке балансировщика, завершенной. Переходим к настройке непосредственно WEB серверов

2) Настройка backend сервера

Собственно ее и настройкой трудно назвать, нам необходимо установить любой WEB сервер это может быть Apache, Nginx, Lighthttpd -не важно, или даже IIS если необходимо распределить нагрузку между серверами под Windows, значения даже не имеет, какая ОС будет на этом сервере! Давайте рассмотрим для примера Ubuntu с уставленным, из репозитория, Nginx.

Настройка ноды №1

Ставим Nginx.
sudo apt-get install nginx


Для опытов, нам будет достаточно и стандартного виртуального хоста Nginx, для того чтобы увидеть работу системы распределения нагрузки и все что нам требуется это перевести этот сервер на другой порт, отличный от 80го, для этого слегка подправим дефолтный виртуальный хост:

sudo  nano /etc/nginx/sites-available/default

В секции server { находим строку:
listen 80 default;

и меняем порт на тот, что мы указали в конфигурационном файле, виртуального хоста example.org на балансировщике, для забывчивых, напоминаю, это порт 8080. Его и вписываем, чтобы выглядело.
listen 8080 default;


Сохраняем изменения и перезапускаем Nginx:
sudo /etc/init.d/nginx

да и меняем IP этого сервера на 192.168.10.11, но данная конфигурация исключительно для примера, когда потребуется, поставите IP адреса той подсети которую будете использовать.

Далее, для того чтобы увидеть как происходит перенаправление запросов между серверами, нам необходимо отредактировать index.html который лежит в корневой директории этого сервера:
sudo nano /var/www/nginx-default/index.html


Находим там строку:
<center><h1>Welcome to nginx!</h1></center>

Вписываем туда NODE 01 чтобы выглядело:
Welcome to nginx! (NODE 01)
Делается это для того, чтобы было видно, какой сервер прислал ответ в данный момент.

Настройка ноды №2 и №3
Настройка производится аналогичным образом как и у ноды №1 только IP адреса подставляем другие
Для ноды №2 192.168.10.12
Для ноды №3 192.168.10.13

В файлы index.html на второй ноде вписываем NODE 02, а на третьей NODE 03 соответственно. Надеюсь что смысл понятен?!
Если эти сервера будут ставиться на виртуальной машине, то можно настроить первую ноду кластера, потом её просто клонировать, заменив IP адреса на нужные и вписав номера серверов в index.html — это здорово сократит время создания тестовой инфраструктуры.
Допустим балансировщик мы настроили, backend сервера тоже. Необходимо все это протестировать.
Поднимать DNS для поддержки зоны example.org — все равно что стрелять из пушки по воробьям, по этому просто добавим запись в файл hosts.

Если дело происходит в Windows, то идем в:
C:\WINDOWS\system32\drivers\etc\hosts

Если в Linux
/etc/resolv.conf


Вписываем туда следующее:
000.000.000.000 example.org


Где: вместо 000.000.000.000 указываем IP адрес, сетевого интерфейса, сервера балансировки нагрузки, который должен смотреть в интернет.

Открываем броузер и переходим по адресу example.org
В ответ мы получим следующее:

Жмем F5.

Еще раз F5 и получаем ответ от третей ноды.


Если обновлять страницу и дальше, то станет понятно, что ответы от серверов идут по кругу.
В общем, видно что распределение запросов происходит нормально!
Что еще можно улучшить, в данной схеме?!
Например Nginx позволяет указывать вес серверов, по умолчанию, он у всех равен нулю.
Делается это на балансировщике, добавляем в файл:
nano /etc/nginx/site-avelible/example.org


В секцию upstream backend

upstream backend {
    server 192.168.10.11:8080 weight=1;
    server 192.168.10.12:8080 weight=2;
    server 192.168.10.13:8080 weight=3;
}


Чем больше вес сервера, тем больший приоритет он имеет и тем больше запросов поступает на него, все остальные будут перенаправляться по мере уменьшения веса, это можно использовать в той ситуации, когда необходимо четко определить порядок перенаправления запросов.

Для того чтобы вывести из эксплуатации один сервер, например, для обслуживания, достачно в секцию upstream backend добавить down сделаем это на примере ноды №3
upstream backend {
    server 192.168.10.11:8080;
    server 192.168.10.12:8080;
    server 192.168.10.13:8080 down;
}


Перезапустим Nginx:
sudo /etc/init.b/nginx restart


Заходим на exmaple.org и обновляем страницу, нам приходят ответы от NODE 01 и NODE 02, чтобы все вернуть как было, достаточно убрать down перезапустить Nginx, на балансировщике сетевой нагрузки, третий сервер вернется в эксплуатацию и все будет как раньше.

Недостатки данной системы
Один существенный недостаток, если на сервере хранятся файлы пользовательских сессий, то в случае перенаправления пользователей на другой сервер, они будут недоступны.
Данную проблему, можно решить двумя способами:
1) Перенаправлением пользователя на определенный сервер, используя, в качестве идентификатора, IP клиента.
2) Использованием распределенной файловой системы, с репликацией файлов между узлами.

Первый способ выполнить довольно просто, используя ip_hash, делается это путем добавления директивы в
upstream backend {
    ip_hash
    server 192.168.10.11:8080;
    server 192.168.10.12:8080;
    server 192.168.10.13:8080;
}


В случае использования ip_hash директива указывающая вес сервера weight игнорируется и все запросы с одного клиентского IP, будут попадать на один и тотже сервер.

Второй способ это использование распределенных файловых систем, например-GlusterFS, но это тема для отдельной статьи, которую я планирую написать в будущем.

За более подробной информацией всегда можно обратиться на сайт разработчика.
Осталось только на backend сервера поставить Fast-CGI и можно приступать к работе. Статья о том, как установить поддержку PHP для Nginx. Естественно, поддержку PHP, необходимо установить на каждый backend сервер.

В целом данная схема позволяет распределять нагрузку уже работающих проектов, убирая основной(боевой) сервер за балансировщик, параллельно добавляя к нему дополнительные сервера, а конце всего этого просто меняются DNS записи, которые начинают указывать на IP сетевого интерфейса нового балансировщика, который торчит в интернет. Если все делается именно так, то зачастую, пользователи даже ничего не заметят, а сервис тихо масштабируется без приостановки обслуживания.
На этом можно и завершить эту эпопею…
Есть вопросы, задавайте их в комментариях, буду рад на них ответить.

Статья была написана с использованием материалов:
nginx.org
Документация по Nginx и его модулям

8 комментариев

avatar
Ждем поста про подключения к всему этому делу баз данных.
avatar
Найти бы на все это время-чтобы сесть и написать.
avatar
Интересная статья, спасибо.
avatar
Спасибо за интересную статью! Подскажите, а есть смысл использовать балансировщик, если проект работает не с БД, а с хранилищем файлов на диске? В нём лежат заархивированные письма… много-много писем. Хранилище непрерывно пополняется и время от времени требуется доставать из него внушительные объёмы, тысяч по 5-10 писем. Проект написан на Java.
avatar
Да, забыл добавить. Пользователей не очень много, но за счёт большого объёма входящих писем (иногда приходит 100-150 писем в секунду), которые потом архивируются, шифруются и раскладываются система очень сильно нагружается.
avatar
Здравствуйте.
Данная схема рассчитана на отдачу контента и факторов которые влияют на результативность работы-на вскидку я вижу как минимум 4:
1) Ширина канала к балансировщику.
2) Количество пользователей.
3) Скорость дисковой подсистемы рассчитанной на отдачу контента.
4) Возможность кеширования часто использующихся данных.
Это не в даваясь в нюансы, которых я не знаю, в общем я не смогу вам ответить на ваш вопрос не видя технического задания…
avatar
Есть конечно. Можно использовать NFS (сетевая файловая система). Сервера будут обмениваться между собой файлами, типа синхронизации.
avatar
Спасибо за статью! Попробовал — завелось с первого раза. Автор в теме.
Есть что добавить? Регистрируйся и оставляй комментарии!