Кластер JBoss 7 — балансировка нагрузки с помощью Apache

  • Tutorial
В прошлой статье мы настроили репликацию сессий пользователей на все узлы кластера JBoss. Само по себе такое действие никак не улучшает отказоустойчивость, и для того, чтобы использовать полученную функциональность, требуется балансировщик нагрузки, который будет распределять внешние обращения между узлами кластера. Распределять нагрузку между узлами будет веб-сервер Apache, в соответствии с рекомендациями в документации по JBoss. Вся информация в статье доступна в различных источниках, но она разрознена, и мне не попалось ни одного ресурса, где все было бы собрано в одном месте, с описанием решения проблем, которые могут возникнуть на практике (лично у меня возникли, поэтому делюсь рецептами). Статья не претендует на полноту информации, скорее наоборот — описана минимальная конфигурация. Предназначена как для специалистов, которых интересуют конкретные конфигурации и настройки, так и для людей далеких от данной темы, но которым интересно, что такое кластеризация.

Общие принципы работы

Специалисты, знакомые с принципами кластеризации, могут пропустить этот раздел. Для наглядности буду рассматривать конкретную ситуацию, с которой встречаются многие: существующая система, состоящая из одного сервера приложений, переходит на кластерное решение. Самый лучший вариант в таких случаях — не пытаться сразу объять необъятное, а построить минимальную работающую конфигурацию: два сервера приложений в кластере, а перед ними веб-сервер в качестве интерфейса (frontend) к клиентам.

Схема работы следующая: все запросы, ранее отправляемые непосредственно на http/https коннектор JBoss-а, теперь должны обращаться к веб-серверу (Apache). Apache настраивается таким образом, чтобы «знать» о существовании «за его спиной» двух серверов приложений. При первом обращении клиента к Системе, веб-сервер выбирает один из серверов приложений (Узел-1) и перенаправляет запрос к нему. Создается сессия, в нее добавляется Cookie, которая в дальнейшем используется веб-сервером для того, чтобы «приклеить» все последующие запросы того же клиента к выбранному серверу приложений. Узел-1, обрабатывающий запросы клиента, при создании/изменении сессии реплицирует её состояние на остальные узлы кластера, где они и висят мёртвым грузом, пока что не принося никакой пользы.
Подробнее о Сессии
Упрощенно. Сессия — объект, создаваемый на стороне JBoss, имеющий различные атрибуты. Сессия имеет идентификатор, который передается в браузер при создании сессии, и при каждом обращении клиента к серверу передается браузером обратно серверу (обычно, в виде Cookie). Сервер по идентификатору находит сессию и обрабатывает запрос в контексте данных найденной сессии. Задача репликации в кластере — передача данных о сессии на остальные узлы таким образом, чтобы при обращении клиента к ним, любой другой узел смог бы так же, как и Узел-1 найти сессию (и данные в ней сохраненные) по идентификатору и обработать запрос клиента в её контексте.
В случае «падения» Узла-1, при очередном обращении клиента, Apache обнаруживает, что сервер недоступен, и перенаправляет запрос на другой узел (Узел-2). Вот тут и начинает использоваться тот самый «мертвый груз» — сессия, состояние которой уже есть на всех узлах кластера. Запрос обрабатывается Узлом-2, и Apache «приклеивает» сессию к нему, т.е. все последующие запросы пользователя теперь сразу направляются на Узел-2.

Базовая кофигурация

Если на стороне JBoss уже настроена репликация сессий, то дополнительных настроек не требуется, всё, что нужно (AJP коннектор), уже настроено в конфигурации по умолчанию. Требуется скачать и установить Apache HTTP Server (процесс установки выходит за рамки данной статьи). Вся настройка осуществляется путем редактирования файла conf/httpd.conf.
  1. Подключение модулей. Раскоментируйте либо добавьте следующие строки:
    LoadModule proxy_module modules/mod_proxy.so
    LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
    LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
    LoadModule headers_module modules/mod_headers.so

    Модули mod_proxy.so, mod_proxy_ajp.so и mod_proxy_balancer.so требуются для балансировщика нагрузки. Если в Системе нет понятия «сессия», то их достаточно. Я, например, работал с системой, которая обрабатывала запросы посредством веб-сервисов, это единичные обращения, не связанные между собой, поэтому сессии не создавались. В нашем случае есть пользователи и их сессии, поэтому требуется добавить модуль mod_headers.so, который позволит отслеживать данные о сессии, передаваемые браузером.
  2. Настройка балансировки. Добавьте следующие строки:
    NameVirtualHost 192.168.1.0:80
    <VirtualHost 192.168.1.0:80>
    	Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
    	<Proxy balancer://ajp-cluster>
    		BalancerMember ajp://192.168.1.1:8009 route=node1
    		BalancerMember ajp://192.168.1.2:8009 route=node2
    		ProxySet stickysession=ROUTEID
    	</Proxy>
    	<Location />
    		ProxyPass balancer://ajp-cluster/
    	</Location>
    </VirtualHost>

    Вместо 192.168.1.0 указываете адрес Apache.
    В разделе Proxy указываете список адресов AJP-коннекторов узлов кластера, по одному вхождению BalancerMember для каждого узла. В конфигурации JBoss 7 по умолчанию AJP-коннектор прослушивает порт 8009. Параметр route присваивает уникальное имя узла, используемое только самим Apache, т.е. совсем необязательно совпадение с именами, указанными при старте JBoss (параметр jboss.node.name). Больше ничего менять не требуется, кластер готов к запуску и тестированию.
    Подробнее о конфигурации
    Директива "Header add Set-Cookie" действует совместно с директивой "ProxySet stickysession" следующим образом. При первом обращении клиента к Apache, после того, как балансировщик выбрал на какой узел он будет перенаправлен, в сессию пользователя добавляется Cookie с именем "ROUTEID", значением которой является идентификатор выбранного узла (node1/node2 в конфигурации выше). При последующих обращениях клиента, Apache уже не надо «думать» о том, какой был ранее выбран узел, Cookie явно сообщает веб-серверу на какой узел следует перенаправить запрос. Директива «Header» ответственна за существование Cookie в сессии, а директива «ProxySet stickysession» за использование ее при маршрутизации для «приклеивания» сессии к конкретному узлу. Если ваш сервер не работает с сессиями (например, как в моем примере выше, только веб-сервисы), то директивы «Header» и ProxySet не нужны.
    JSESSIONID
    Во многих примерах конфигурации предлагается использовать в качестве маркера узла идентификатор сессии JSESSIONID. Это кажется логичным, т.к. такой идентификатор используется в большинстве веб-приложений на Java. Пример с apache.org:
    ProxyPass /test balancer://mycluster stickysession=JSESSIONID|jsessionid scolonpathdelim=On
    <Proxy balancer://mycluster>
    BalancerMember http://192.168.1.50:80 route=node1
    BalancerMember http://192.168.1.51:80 route=node2
    </Proxy>
    Сразу скажу: для JBoss это не работает, по крайней мере, в конфигурации по-умолчанию. Данный метод предполагает, что значение Cookie с именем JSESSIONID, сформированное на стороне сервера приложений, состоит из двух частей: идентификатор сессии дополняется идентификатором сервера, они разделяются точкой. JBoss 7 формирует простую Cookie JSESSIONID, которая содержит только идентификатор сессии, значение которого сгенерировано случайным образом и не несет никакой смысловой нагрузки.
    Таким образом, основные отличия от описанной мной конфигурации следующие:
    • В описанной в статье конфигурации идентификатор узла и идентификатор сессии находятся в разных Cookie, здесь два значения объединены в одну Cookie.
    • В описанной в статье конфигурации идентификатор узла формируется на стороне балансировщика (логично: кто использует, тот и формирует), а идентификатор сессии на стороне сервера приложений (тот же принцип: идентификатор сессии нужен JBoss-у, а не Apache). Здесь же оба значения генерируются на стороне сервера приложений (либо веб-сервера на бэкенде).



Настройка SSL (HTTPS)

Если до кластеризации вы использовали шифрование при обмене данными с клиентом, общаясь с ним по протоколу HTTPS, то настройки сертификатов осуществлялись на стороне JBoss, в конфигурации HTTPS-коннектора. При переходе на кластерное решение сертификатами должен управлять Apache.

Общие принципы
Процесс следующий: HTTPS-запросы от клиента поступают на веб-сервер Apache, расшифровываются и передаются серверу приложений в открытом виде посредством протокола AJP. Ответ от сервера приложений передается на веб-сервер также в открытом виде, шифруется там и передается клиенту по протоколу HTTPS. Таким образом, между Apache и JBoss существует так называемая «демилитаризованная зона», т.е. закрытое от внешнего неавторизованного доступа окружение, в котором между серверами настроены доверительные отношения, не требующие дополнительной защиты от несанкционированного доступа на прикладном уровне. Доступным извне должен быть только Apache, по HTTPS порту (обычно, 443), все остальное должно быть наглухо закрыто.

Настройки веб-сервера в conf/httpd.conf:
  1. Прослушка порта 443. Веб-сервер Apache в конфигурации по умолчанию «слушает» только порт 80. Для того, чтобы он стал прослушивать порт 443, следует добавить строчку:
    Listen 443
  2. Подключение модулей. Раскоментируйте либо добавьте следующую строчку:
    LoadModule ssl_module modules/mod_ssl.so

  3. Настройка балансировки. Добавьте следующие строки:
    NameVirtualHost 192.168.1.0:443
    
    <VirtualHost 192.168.1.0:443>
    	Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
    	<Proxy balancer://ajp-cluster>
    		BalancerMember ajp://192.168.1.1:8009 route=node1
    		BalancerMember ajp://192.168.1.2:8009 route=node2
    		ProxySet stickysession=ROUTEID
    	</Proxy>
    	<Location />
    		ProxyPass balancer://ajp-cluster/
    	</Location>
    
    	ProxyRequests off
    	SSLEngine on
    	SSLCertificateFile c:/Apache2/conf/my.cer
    	SSLCertificateKeyFile c:/Apache2/conf/my.key
    </VirtualHost>
    


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

Выше описана конфигурация Apache 2.2, начиная с версии 2.4 следует добавить модули mod_lbmethod_byrequests.so и mod_slotmem_shm.so, и удалить объявления NameVirtualHost, они теперь не нужны.
Проблемы с сертификатами и их решение
При настройке шифрования на стороне JBoss, требовался один файл, содержащий ключ и сертификат, например, в формате PKCS#12. Apache требует разделения на два файла, в одном файле сертификат, в другом — закрытый ключ. Кроме этого, Apache имеет два ограничения:
  1. Распознается только формат PEM. Узнать, соответствует ли ваш ключ этому формату, можно открыв файл ключа текстовым редактором. В содержании должны присутствовать строки "-----BEGIN PRIVATE KEY-----" и "-----END PRIVATE KEY-----", длина строк между ними не должна превышать 64 символа. То же касается сертификата, со строками, соответственно "-----BEGIN CERTIFICATE-----" и "-----END CERTIFICATE-----".
  2. Сборка Apache под ОС Windows не умеет открывать хранилища ключей, защищенные паролем.

Все вышеперечисленные проблемы решаются при помощи утилиты Openssl. Из вашего файла хранилища ключа и сертификата, используемого в JBoss, следует извлечь ключ и сертификат в отдельные файлы, осуществив при этом конвертацию в нужный формат и сняв защиту паролем. Вот пример конвертации ключа из формата PKCS#12 (поддерживаются и другие форматы, Google вам в помощь):
openssl pkcs12 -nocerts -nodes -in C:\key.p12 -out C:\Apache2\conf\my.key
openssl pkcs12 -clcerts -nokeys -nodes -in C:\key.p12 -out C:\Apache2\conf\my.cer

ВНИМАНИЕ! Не забудьте удалить со своего компьютера полученный файл my.key после переноса его на промышленный сервер, т.к. он ничем не защищен и может быть скомпрометирован.

Заключение

После применения описанного в статье решения, в принципе, можно вообще удалить из настроек JBoss описание коннекторов HTTP/HTTPS за ненадобностью, т.к. теперь все внешние обращения будут поступать через AJP коннектор. Это заметно повысит безопасность сервера, особенно для сканирующих утилит, которые особое внимание уделяют этим протоколам.

PS

Предлагаемый вариант кластеризации частично решает проблемы балансировки нагрузки и отказоустойчивости. Для полноценной конфигурации здесь не хватает кластеризации СУБД и дублирования единой точки входа — Apache. Также очень важна конфигурация аппаратной инфраструктуры: дублирование сетей и маршрутизаторов, серверных компонентов, бесперебойное питание и прочее. Тем не менее, данная статья поможет развить систему по принципу 80/20, т.е. путем небольших затрат получить значительное повышение производительности и отказоустойчивости. В своей практике мне не довелось встретить решений, в которых пошли дальше, разве что встречалась кластеризация СУБД, но если доведется столкнуться, обязательно напишу об этом. Если же ваши сервисы высококритичны, то предложенное мной решение — лишь первый шаг.

Есть похожая статья на Хабре, рекомендую ознакомиться также и с ней. Несмотря на тематическую схожесть, моя статья заметно отличается, и, на мой взгляд, имеет полное право на существование. Я постарался доходчиво рассказать о том, как и за счет чего все работает, там же просто выложено содержание конфигурационных и командных файлов. К тому же, в статье по ссылке, в Apache используется mod_jk для балансировки, у меня же mod_proxy. Stackoverflow пишет, что основным минусом mod_jk является "Need to build and maintain a separate module". Именно с этим столкнулись на практике наши системные администраторы, которые так и не смогли установить этот модуль на Apache под AIX, так что если у вас схожая проблема, можете попробовать mod_proxy, который поставляется в комплекте с Apache и, на мой взгляд, проще в настройке.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 12

    0
    Я бы не стал внутри сети закрывать «HTTP/HTTPS за ненадобностью», так как банально может пригодится доступ к внутреннему интерфейсу для внутренних пользователей — зачем им выходить в мир, что бы попасть на свой же сервис.
    Так же хотел бы услышать, как при такой конфигурации происходит деплой приложений. Возможно ли это сделать «на лету»?
      0
      Вообще, JBoss вроде бы умеет нормально редеплоить приложения, т.е. практически на лету. У нас на работе аналогичная конфигурация с томкатами — стопится первый, закидывается новый варник, стартует первый, стопится второй итд. Даунтайм равен нулю, не вижу проблем, кроме необходимости легкой автоматизации администратором.
        0
        Ну, закрытие HTTP/HTTPS скорее подсказка необычного решения, а не руководство к действию — кому-то интересно, кому-то нет. Да, вы правы, с доступом внутренних пользователей возникнут проблемы, особенно если они хотят посмотреть что происходит на конкретном сервере приложений, а не абстрактном, выбранном балансировщиком. С другой стороны, например, наши админы редко пользуются той же jmx-консолью, так что лично я серьезно подумываю о том, чтобы таки закрыть коннекторы.

        Насчет деплоя — зависит от вашей бизнес-логики. У меня есть система, в которой данное решение дало возможность выполнять «на горячую» любые действия (обновление, изменение конфигурации) с перезапуском JBoss-а, при этом оставаясь онлайн. Останавливается один узел, Apache это видит, перенаправляет запросы на другой узел, все прозрачно для внешних клиентов. При обновлении, после запуска ранее остановленного узла, он включается в кластер, НО важный момент — в этот период времени на разных узлах запросы обрабатывает разная версия вашего ПО. После обновления и перезапуска второго узла, версии соответственно уравниваются. Это пример для режима standalone, именно поэтому я его использую. Насколько я понял, режим domain использует совсем другие принципы, там обновлением управляет главный узел, а не человек. Для нас такой подход к версионности промышленного ПО приемлем, для кого-то — нет. Система обрабатывает не связанные между собой запросы к веб-сервисам, поэтому после восстановления работоспособности обоих узлов нагрузка сразу равномерно распределяется по мере поступления новых запросов.

        С «приклеенными» сессиями такого не будет: если посреди рабочего дня перезапустить один узел, все пользователи приклеятся к другому, и останутся там пока не выйдут из системы, перезапущенный будет простаивать. Что делать с этим во второй нашей системе я еще не думал, скорее всего, такого вольного обращения с серверами приложений в этом случае уже не получится.
        0
        А рассматривали ли вы вариант с Nginx вместо Apache? Какие плюсы/минусы могут быть в данном случае?
          0
          Отвечу за автора — nginx не умеет AJP, но для связи с бэкендами по HTTP — вполне подойдёт. У него есть балансировка, и она будет работать честно. В случае с описанной конфигурацией (с репликацией сессий по всем аппсерверам) — все будет работать правильно.
            0
            Спасибо за подсказку, я незнаком с nginx. Слышал, что nginx более производителен в сравнении с Apache. На личном опыте скажу, что и Apache очень шустр. У нас когда-то при переходе на кластер, Apache поставили на тот же сервер, что и один из узлов. Каких-то проблем с производительностью замечено не было, несмотря на то, что в обычном режиме работы JBoss-а на этом сервере, он забирает 80-95% процессорного времени. Думаю, что до тех пор, пока количество запросов в сутки не перевалило семизначные цифры, можно не беспокоиться о производительности Apache.
              0
              Всегда пожалуйста.
              В ближайшем рассмотрении, подумайте о том, что nginx-ом стоит отдавать статику — он делает это гораздо лучше апача — экономя память, и CPU. А «динамику» в таком случае можно отдавать цепочкой nginx -> Apache -> JBoss. Это сохранит имеющуюся конфигурацию, и добавит снаружи от неё лишь один элемент — сервер с nginx-ом.
              Настроить это достаточно просто — если у вас есть коллеги/друзья, хотя бы более-менее знающие nginx, то за полдня-день неспешно можно всё понять и сделать.

              А в JBoss видимо так и остался тяжеловесным на сетевые соединения, что печально :(
              Успехов!
          0
          Печально что полезная техническая статья за пару часов набрала 0 рейтинга и далека до попадания в захабренные.

            0
            Спасибо за моральную поддержку! Но я при написании статей думаю не только о хабросообществе или своем личном рейтинге (хотя и об этом тоже, конечно), но и о любых IT-специалистах, бродящих по Сети в поисках нужной информации. Я сам по крупинкам собирал рецепты для этой статьи из источников, в которых даже не зарегистрирован. Надеюсь, что немало людей, не зарегистрированных на Хабре прочитают статью и узнают из нее что-то новое. Я это к тому, что рейтинг любых статей на Хабре — это лишь часть их жизни в Сети, и даже будучи глубоко в архивах они могут приносить немало пользы.
            0
            В качестве бесплатного балансировщика я бы посоветовал использовать более гибкий и быстрый haproxy + heartbeat он умеет много больше того, что умее nginx или apache + mod_ajp, плюс умеет ssl offloading. Скажите, у меня вопрос больше по предыдущей статье, любое приложение написанное под jboss 7 имеет репликацию сессий на все ноды кластера?
              0
              Вот тут пишут что с heartbeat могут быть проблемы. Не исключаю того, что это только с Apache (или вообще, конкретно их конфигурация неудачная), и с haproxy проблем нет. У вас есть практический опыт применения haproxy + heartbeat, скажите, как работает — стабильно?

              По поводу ssl offloading (насколько я понял, более употребимый термин SSL acceleration) — стыдно признавать, впервые услышал о существовании подобной технологии. Опять же, если есть опыт применения, черканёте пару слов?

              Любое стандартное веб-приложение (обычно, с расширением .war), развернутое на JBoss 7, в случае добавления в web.xml тэга <distributable/> получит репликацию сессий.
                0
                я по этому комментарию, честно говоря, не понял в чем проблема с heartbeat, у нас проблем пока не было. SSL offloading подразумевает использования одного хоста с SSL и бэкэндов по HTTP, что снижает нагрузку на бэкэнд и ускоряет загрузку при отсутствии лишнего SSL handshake.

            Only users with full accounts can post comments. Log in, please.