Подписка

Горизонтальное масштабирование - балансировка нагрузки

При увеличении нагрузки или посещаемости проекта, рано или поздно вертикальное маштабирование (увеличение ресурсов сервера, таких как память, скорость диска и т.д) упирается в некий предел и не дает ощутимого прироста. В таком случае в ход идет горизонтальное масштабирование - добавление новых серверов c перераспределением нагрузки между ними.

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

Ниже рассмотрим одну из простых схем горизонтального маштабирования, состоящую из двух серверов приложений, одного сервера БД и балансировщика нагрузки. Такая схема оптимальна для приложений с большой нагрузкой на PHP и неинтенсивном использовании базы данных.

Все эксперименты с горизонтальным маштабированием можно проводить в "домашних условиях" используя виртуальные машины. Я использую VirtualBox, в котором для текущей схемы создал 4 виртуальных сервера с Ubuntu Server на борту. Схема работы системы будет выглядеть следующим образом:

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

При получении запроса от клиента, балансировщику нужно определить, какому из веб серверов переслать запрос. Алгоритм принятия решения называется методом или стратегией балансировки, наиболее распространенные стратегии:

  • Round robin. Из доступных серверов строится очередь и балансировщик выбирает первый в очереди. После выполнения запроса сервер перемещается в конец очереди.
  • Меньшее количество соединений. Балансировщик ведет учет количества незакрытых соединений и выбирает тот сервер, у которого таких соединений меньше.
  • Использование "веса" серверов. Каждому серверу в зависимости от мощности присваивается вес, который используется для ранжирования.

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

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

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

Для реализации программного балансировщика есть много решений, например:

  • Nginx
  • Apache + mod_proxy_balancer
  • прокси сервер

Мы остановимся на последнем. Попробуем настроить Haproxy, согласно описанной выше стратегии.

Устанавливается прокси-сервер очень просто:

sudo apt-get install haproxy

Для конфигурации, нам нужно знать ip адреса веб серверов, у моих виртуальных машин они 192.168.1.1 и 192.168.1.2 соответственно.

Настроим простую балансировку, для чего пропишем в конфигурации прокси-сервера /etc/haproxy/haproxy.cfg:

global
        maxconn 4096
        user haproxy
        group haproxy
        daemon
 
defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        retries 3
        option  redispatch
        maxconn 2000
        contimeout      5000
        clitimeout      50000
        srvtimeout      50000
 
listen webcluster *:80
        mode    http
        balance leastconn
        option httpchk HEAD / HTTP/1.0
        option forwardfor
        cookie serv insert
        option httpclose
        server webserver1 192.168.1.1:80 cookie serv1 check
        server webserver2 192.168.1.2:80 cookie serv2 check

Последний блок наиболее интересен, так как в нем настраиваются главные параметры балансировки, рассмотрим каждую строку детально.

mode http
режим работы прокси касающийся обработки пакетов
balance leastconn
важный момент, это алгоритм выбора сервера, о котором мы говорили выше. leastconn - перенаправляем запрос серверу из наименьшим количеством соединений.
option httpchk HEAD / HTTP/1.0
проверяем состояние сервера через http
option forwardfor
так как прокси сервер отправляет запросы веб серверам, для них его ip адрес будет адресом клиента. Чтобы передавать адрес клиента, используем опцию forwardfor, которая добавляет в тело запроса заголовок X-Forwarded-For с адресом клиента.
cookie serv insert
для обеспечения работы сессий, идентификаторы которых хранятся в куки, а данные на сервере, добавляем куку serv, которая будет хранить адрес сервера, работающего с текущим клиентом. Таким образом клиент всегда будет попадать на один сервер и иметь доступ к данным сессии.
option httpclose
автоматически закрывать соединения после завершения передачи данных
server webserver1 192.168.1.1:80 cookie serv1 check
добавляем первый веб сервер указывая адрес, значение куки для определения сервера и необходимость проверки состояния.
server webserver2 192.168.1.2:80 cookie serv2 check
аналогично второй.
Стартуем прокси сервер:
sudo haproxy -f /etc/haproxy/haproxy.cfg

Балансировка настроена! Если открыть несколько браузеров одновременно и попробовать зайти на сайт, в куки браузеров должны установиться разные значения переменной serv, в зависимости от назначенного сервера.

Конфигурация haproxy конечно же сильно зависит от задачи, например для раздачи статического контента или работы сайта без сессий незачем привязывать конкретный сервер к клиенту. Поэтому если нужна более специфическая настройка, документация (кстати очень обширная) в помощь: http://cbonte.github.com/haproxy-dconv/configuration-1.5.html

На этом все, вопросы, замечания и пожелания приветствуются в комментариях.

В тему:

Если пост понравился - нажмите на +1 - мне будет приятно.

@kkooler

@kkooler

Занимаюсь разработкой высоконагруженных проектов и распределенных систем на PHP.
В свободное время разрабатываю нано-проекты:

Следить за блогом

RSS канал Twitter