Oops... your message was not sent

Your message has been successfully sent

тематические истории, основанные на опыте компании JetRuby
DevOps

Как сбалансировать нагрузку на серверы с помощью HAProxy

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

HAProxy активно используется в различных высоконагруженных веб-сайтах, таких как Instagram, Twitter, Reddit и т.д. К числу его основных функций можно отнести:

  • Балансировка HTTP и TCP соединений
  • Возможность перезапуска без потери активных соединений
  • Встроенный веб-интерфейс для просмотра статистики с HTTP basic аутентификацией
  • Возможность терминировать SSL (можно ставить перед веб-приложениями вместо nginx)

Балансировка HTTP и TCP соединений

HAProxy поддерживает балансировку, как  HTTP/HTTPS/HTTP2 (например, веб-серверы или Elasticsearch), так и TCP соединений (например, postgres или redis). Поддерживаются разные алгоритмы балансировки запросов. Из наиболее важных можно выделить:

roundrobin: запросы отправляются к бэкендам по очереди. Это крайне удобно для балансировки stateless протоколов, вроде HTTP. Однако если в приложениях есть данные, которые хранятся не в разделяемом хранилище, а на конкретном бэкенде (например, сессии или загруженные файлы), этот метод не подходит.

leastconn: запрос отправляется к бекенду с наименьшим количеством открытых соединений.

source: запросы с одного IP всегда отправляются на один и тот же бэкенд. Может быть полезным для таких сервисов, как memcached.

Существуют вариации и других алгоритмов, некоторые из них специфичны для HTTP. Они детально описаны в документации HAproxy.

Возможность перезапуска без потери активных соединений

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

Использование разных алгоритмов балансировки

Source-based балансировка

Часто, особенно для TCP бэкендов, возникает необходимость гарантировать что запросы от одного и того же клиента всегда пойдут на один и тот же бэкенд. Для этого применяется алгоритм балансировки «source». Таким образом можно распределить нагрузку между разными экземплярами memcached или redis (при условии, что все redis’ы независимы и не являются master/slave друг друга).

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

Этого можно добиться следующим конфигом:

listen memcached *:11211

option tcplog

balance source # ip-based balancing

server memcached-1 host-1.example.com:11211 check inter 3s fall 3 minconn 50

server memcached-2 host-2.example.com:11211 check inter 3s fall 3 minconn 50

server memcached-3 host-3.example.com:11211 check inter 3s fall 3 minconn 50

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

Round-robin балансировка

Если нет разницы, какой именно бэкенд обслужит запрос, удобнее использовать round-robin алгоритм. Он позволяет при одинаковых весах бэкендов, распределять запросы циклично и равнозначно. Это можно применять для балансировки как чтения, так и записи в Elasticsearch, поскольку всю сложную работу по распределению записи кластер узлов Elasticsearch выполняет самостоятельно. Соответственно, конфиг будет выглядеть следующим образом:

listen *:9200

option tcplog

server elastic-1 host-1.example.com:9200 check inter 3s fall 3 minconn 50

server elastic-2 host-2.example.com:9200 check inter 3s fall 3 minconn 50

server elastic-3 host-3.example.com:9200 check inter 3s fall 3 minconn 50

Использование TCP-бекендов редисов в схеме master-slave

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

В качестве примера можно рассмотреть несколько экземпляров redis,  один из которых является мастером, а остальные — его репликами. Запросы от пользователя должны уходить только на мастер, поэтому необходимо, чтобы HAProxy понял, какой из бэкендов в любой момент времени является мастером.

По умолчанию для того чтобы понять — жив ли бэкенд, HAProxy использует простую проверку доступности порта. Помимо этого он поддерживает и сложные проверки. Они состоят из отсылки в указанный порт неких данных и проверки полученного ответа. Таким образом, HAProxy может общаться непосредственно с redis-демоном для получения его роли (master/slave). Вот один из примеров такой настройки.

Несмотря на то, что настройка является сравнительно простой и подходит для большинства случаев, она не обеспечивает 100% корректности определения мастера. Так как в случае с network split возможна ситуация, когда два разных redis’а будут считать себя мастером, что может ввести HAProxy в заблуждение. В таком случае, предпочтительно использовать redis sentinel в качестве источника. Дискуссию на эту тему и пример такого решения можно найти в комментариях к данной статье. Тем не менее, это значительно усложняет конфиг HAProxy.

В случае же, если вам нужно не просто слать все запросы в мастер, но и отправлять их на чтение на слейвы, лучше использовать специализированные решения (например, pgpool для postgres), а не HAProxy.

Особенности применения haproxy в docker-контейнере

Перезапуск без потери соединений

До версии 1.5.16 HAProxy запускался, как основной процесс в стандартном docker-контейнере (т.е. как PID 1). Это делало невозможным перезапуск без потери соединения. Так как при завершении PID 1 контейнер также завершался. Поэтому нам необходимо, чтобы таким процессом не был сам HAProxy. Начиная с haproxy: 1.5.16 в качестве PID 1 запускается haproxy-systemd-wrapper, который обеспечивает перезапуск HAProxy, но при этом остается живым. Дополнительный плюс — контейнер теперь нормально завершается, как по Ctrl+С, так и через docker stop.

Логирование

Ожидается, что приложения, запускаемые в docker-контейнерах, будут выводить все свои логи в stdout. В таком случае логи будут доступны через docker logs. HAProxy не поддерживает логирование в файл или stdout, только syslog. Существует способ получить логи, указав HAProxy. А дальше просто логировать в /dev/log. Для этого в конфиге haproxy указывается:

defaults

log /dev/log local0 debug

option dontlognull

option dontlog-normal

Внутрь контейнера монтируется /dev/log:

docker run haproxy -v /dev/log:/dev/log

При этом docker logs по-прежнему не будет работать. Но будет работать системный лог. В современных дистрибутивах до него можно добраться через journalctl.

Зачем использовать HAProxy?

Сегодня HAProxy не является единственным решением, предоставляющим проксирование и HTTP, и TCP. Например, c 2014 года nginx также поддерживает TCP-проксирование. Тем не менее, существуют причины, по которым, стоит отдать предпочтение именно HAProxy:

  1. Зрелость продукта. Первый релиз HAProxy состоялся в декабре 2001 года, с тех пор он активно развивается.
  2. Скорость работы. HAProxy написан на C и используется в высоконагруженных системах.
  3. Высокая настраиваемость. Документация очень обширна, настроить можно все.
  4. Надежность. HAProxy — пример такого продукта, который можно настроить и забыть, он просто будет работать.
  5. Широкий спектр применения. Reverse proxy, Load Balancing, Failover, поддержка HTTP и TCP, высокая настраиваемость — всё это позволяет использовать HAProxy во множестве сценариев.
  6. Встроенный веб-интерфейс со статистикой. Мелочь, но мелочь полезная.
  7. Мониторинг. Тот же веб-интерфейс может выводить данные не только как HTML-страницу, но и в CSV-формате, что позволяет интегрировать его с системами мониторинга.

Вывод

Наши поздравления! Вы ознакомились с основными понятиями HAProxy и балансировки нагрузки. В мире современной веб-разработки они очень и очень важны.

HAProxy — один из самых популярных открытых балансировщиков нагрузки TCP/HTTP с открытым программным кодом. И одновременно — прокси сервер для таких систем, как Linux, FreeBSD и Solaris. Чем же он вам пригодится? Прежде всего, вы сможете в значительной степени улучшить производительность и ошибкоустойчивость серверного окружения при помощи распределения рабочей нагрузки между несколькими серверами. Впечатляет, не правда ли? Итак, если вы еще не уверены в HAProxy, мы идем к вам!

department
Статью подготовил
Отдел разработки и системного администрирования
Компания обеспечивает теснейшую взаимосвязь программистов и специалистов по эксплуатации ПО. Scrum решения и прочие методолгоии управления проектами помогают нам в разработке качественного программного продукта.
New Articles