UMGUM.COM 

WildFly + Nginx ( Описание этапов развёртывания сервера приложений "WildFly" с фронтом из терминирующего HTTPS web-сервера "Nginx". )

20 февраля 2018  (обновлено 14 февраля 2019)

OS: "Linux Debian 9 (Stretch)", "Linux Ubuntu 16.04 (Xenial) LTS".
Applications: Java, WildFly и Nginx.

Задача: описание этапов развёртывания типового web-приложения ("сервлета") в среде "WildFly".


Последовательность дальнейших действий такова:

1. Устанавливаем интерпретатор языка Java.
2. Устанавливаем контейнеризатор WildFly.
3. Устанавливаем Nginx и связываем его с WildFly.
4. Развёртываем Java-сервлет.


Подготовка среды исполнения Java-сервлетов.

Будем считать, что для работы нашего web-приложения требуется "Java v9", причём производства "Oracle".

Следуя предписаниям, загружаем с сайта "Oracle" последний стабильный релиз "Java SE JDK 9 (Linux)", где:

JRE (Java Runtime Environment) - это среда исполнения полностью готовых Java-приложений, предназначенная для пользователей; её ещё можно загрузить с сайта "http://java.com/|java.com".

JDK (Java Development Kit) - среда разработки Java-приложений - специальный пакет для разработчиков, включающий в себя документацию, различные библиотеки классов, утилиты, документацию, компилятор, а также систему исполнения JRE.

Для работы "WildFly" в качестве сервера готовых приложений хватает и JRE, но на практике, с учётом того, что разработчики web-приложений часто не воспринимают отличия среды программирования от эксплуатационной и создают приложения зависимые от библиотек JDK - приходится использовать последнюю.

Установка "Java JRE/JDK" элементарна и сводится к распаковке в нужное место файловой системы дистрибутивного архива:

# mkdir -p /usr/lib/jdk
# tar -xvf ./jdk-9.0.1_linux-x64_bin.tar.gz -C /usr/lib/jdk

Сразу проверяем, запустится ли в нашей системе Java-интерпретатор как таковой:

# /usr/lib/jdk/jdk-9.0.1/bin/java -version

Не помешает сделать символическую ссылку на директорию с актуальной на данный момент инсталляцией JDK, чтобы в дальнейшем использовать её в конфигурации приложений, использующих Java:

# ln -s /usr/lib/jdk/jdk-9.0.1 /usr/lib/jdk/default

Регистрируем выбранную версию Java как предпочтительную в системе:

# update-alternatives --install /usr/bin/java java /usr/lib/jdk/jdk-9.0.1/bin/java 100
# update-alternatives --install /usr/bin/javac javac /usr/lib/jdk/jdk-9.0.1/bin/javac 100

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

# update-alternatives --config java
# update-alternatives --config javac

Сверяем текущую конфигурацию с нашими ожиданиями:

# update-alternatives --display java
# update-alternatives --display javac

Проверяем успешность регистрации приложения в системном окружении, вызывая его по короткому имени, без указания пути в файловой системе:

# java -version

Получение и развёртывание "WildFly".

Сервер приложений "Wildfly" - не вполне новый продукт. Это ребрендинг и развитие "JBoss Application Server", который в 2013-м году компания-разработчик "RedHat" явно отделила от своего коммерческого продукта "JBoss Enterprise Application Platform". Переименованием изменения не ограничились - в "WildFly" в качестве контейнера сервлетов вместо "Tomcat" стали использовать "Undertow".

"WildFly" - программный продукт с открытым исходным кодом, считается, что в его разработке участвует сообщество. Загрузка свободна, использование тоже - но поддержка платная (впрочем, она нам не требуется). Идём на сайт разработчиков, в раздел загрузки:


Для простого сервера web-приложений достаточно набора типа "Servlet-Only Distribution" (28 MB), но в реальности практически всегда приходится применять полный набор типа "Full & Web Distribution" (133 MB).

# cd /opt
# wget http://download.jboss.org/wildfly/10.1.0.Final/wildfly-10.1.0.Final.tar.gz
# tar -xzf wildfly-10.1.0.Final.tar.gz
# mv wildfly-10.1.0.Final wildfly-10

Создаём символическую ссылку на директорию с актуальной инсталляцией "WildFly", чтобы в дальнейшем использовать её в конфигурации приложений, использующих таковой:

# ln -s /opt/wildfly-10 /opt/wildfly

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

# groupadd --system wildfly
# useradd --system --home-dir /var/lib/wildfly --shell /bin/false --gid wildfly wildfly

Я предпочитаю по возможности разделять сущности, оставляя исполняемые файлы сервера "WildFly" и его разделяемые библиотеки в "/opt/", а web-приложения и их данные вынести в директорию динамичных данных "/var".

# mkdir -p /etc/wildfly /var/lib/wildfly

Файловые ресурсы "WildFly" передаём во владение его пользователю:

# chown -R wildfly:wildfly /etc/wildfly /opt/wildfly-10 /var/lib/wildfly
# chmod -R o-rwx /etc/wildfly /opt/wildfly-10 /var/lib/wildfly

Регистрация сервера приложений в "Systemd" и запуск сервиса.

Создаём файл описания параметров запуска и остановки "WildFly" в качестве сервиса, управляемого "Systemd":

# vi /etc/systemd/system/wildfly.service

[Unit]
Description=The WildFly Application Server
After=syslog.target network.target

[Service]
User=wildfly
Group=wildfly
LimitNOFILE=102642

Environment="WILDFLY_HOME=/opt/wildfly"
Environment="WILDFLY_CONFIG=standalone.xml"
Environment="WILDFLY_BIND=0.0.0.0"
Environment=LAUNCH_JBOSS_IN_BACKGROUND=1

PIDFile=/var/run/wildfly/wildfly.pid
ExecStart=/opt/wildfly/bin/standalone.sh -c ${WILDFLY_CONFIG} -b ${WILDFLY_BIND}
ExecStop=/opt/wildfly/bin/jboss-cli.sh --connect --command=:shutdown
StandardOutput=null
Restart=on-failure

[Install]
WantedBy=multi-user.target

Указываем "Systemd" перечитать и принять новую конфигурацию, а также явно активируем и запускаем новый сервис:

# systemctl daemon-reload
# systemctl enable wildfly
# systemctl start wildfly

Сразу же можно удостоверится, прослушивает ли "WildFly" сетевые порты в ожидании запросов от пользователей:

# netstat -apn | grep -i java

tcp ... 0.0.0.0:8080   0.0.0.0:* LISTEN 5026/java
tcp ... 0.0.0.0:8443   0.0.0.0:* LISTEN 5026/java
tcp ... 127.0.0.1:9990 0.0.0.0:* LISTEN 5026/java

Настройка доступа к консоли управления "WildFly".

Все протоколы взаимодействия с "WildFly" мультиплексированы на два порта: TCP:8080 (практикуется ещё HTTPS на TCP:8443) для приложений и TCP:9990 для управления - это видно на выводе утилиты "netstat" в примере выше.

По умолчанию сразу после инсталляции "WildFly" управление таковым возможно лишь с адреса интерфейса "локальной петли", но наверняка понадобится доступ к этой подсистеме с удалённых узлов сети.

Открываем доступ извне, с определённого IP или диапазона таковых:

# vi /opt/wildfly/standalone/configuration/standalone.xml

....
  <interfaces>
    <interface name="management">
      <!-- inet-address value="${jboss.bind.address.management:127.0.0.1}"/ -->
      <inet-address value="${jboss.bind.address.management:0.0.0.0}"/>
  </interface>
....

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

Подключение к подсистеме управления разрешено лишь после аутентификации соответствующих пользователей, аккаунты которых следует ещё создать:

# /opt/wildfly/bin/add-user.sh -u userOne -p passwordForUserOne

....
Added user 'userOne' to file '/opt/wildfly/standalone/configuration/mgmt-users.properties'
Added user 'userOne' to file '/opt/wildfly/domain/configuration/mgmt-users.properties'

Теперь можно обратится посредством CLI-консоли к "WildFly" и отдать ему какую-нибудь команду - перезагрузки сервиса, например:

# /opt/wildfly/bin/jboss-cli.sh --connect controller=127.0.0.1 --command=:reload

Также созданному пользователю "userOne" доступен web-интерфейс управления "WildFly":

http://wildfly.example.net:9990/console/

Установка фронтального web-сервера "Nginx".

Несмотря на то, что "WildFly" вполне способен выступать в роли web-сервера, на практике для приёма и первичной обработки клиентских подключений удобнее использовать более легковесное приложение:

# aptitude install nginx

Конфигурация принимающего подключения пользователей web-сервера "Nginx" проста и сводится к описанию параметров "проксирования" всех запросов нижележащему "WildFly":

# vi /etc/nginx/nginx.conf

....
http {
  ....
  # Велим Nginx не выдавать сведения о номере своей версии
  server_tokens off;

  # Отключаем проверку размера тела передаваемого WildFly запроса
  client_max_body_size 0;

  # Увеличиваем размер блоков данных, которыми обмениваются Nginx и WildFly
  client_body_buffer_size 4M;
....

# vi /etc/nginx/sites-available/wildfly.example.net.conf

# Блок перехвата обращений посредством открытого HTTP и перенаправления таковых на HTTPS
server {
  listen 80 default_server;
  server_name wildfly.example.net;
  access_log off;
  error_log off;
  rewrite ^(.+)$ https://wildfly.example.net$1 permanent;
}

# Блок описания web-сервиса приёма, терминирования SSL/TLS запросов и проксирования их нижележащему WildFly
server {
  listen      443 ssl http2;
  server_name wildfly.example.net;

  access_log /var/log/nginx/wildfly.example.net_access.log;
  error_log  /var/log/nginx/wildfly.example.net_error.log;

  # SSL Configuration
  ssl on;
  ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers   HIGH:!aNULL:!MD5;
  ssl_prefer_server_ciphers on;
  ssl_certificate     /etc/nginx/ssl/wildfly.example.net.crt;
  ssl_certificate_key /etc/nginx/ssl/wildfly.example.net.key.decrypt;

  # Перенаправление на страницу основного web-приложения, при запросе корня web-ресурса
  location = / {
    rewrite ^ $scheme://$host/site/ last;
  }

  # Отправляем все запросы на обработку в WildFly
  location / {
    proxy_pass http://localhost:8080;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_connect_timeout 600;
    proxy_send_timeout    600;
    proxy_read_timeout    600;
    send_timeout          600;

    # Включаем поддержку смешанных (HTTP/1.1 и WebSocket) запросов в одном потоке "клиент - прокси - сервер"
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}

Удаляем конфигурацию сайта "по умолчанию", активируем новую, проверяем её корректность и запускаем в работу:

# rm /etc/nginx/sites-enabled/default
# ln -s /etc/nginx/sites-available/wildfly.example.net.conf /etc/nginx/sites-enabled/wildfly.example.net.conf
# nginx -t
# /etc/init.d/nginx reload

Адаптация "WildFly" для работы в спарке с "Nginx".

С учётом того, что запросы пользователей будут приниматься web-сервером "Nginx", а обрабатываться скрытым за ним "WildFly", потребуется согласовать адреса FQDN и номера сетевых портов терминирования, которые должны сохранятся неизменными на протяжении всей цепочки обработки запроса, при передачи от сервера к серверу.

В самом простом случае достаточно уведомить наш web-сервис "WildFly" о параметрах терминирования клиентских запросов вышестоящим проксирующим web-сервисом, чтобы в ответах таковому вместо внутренних имени сайта "localhost", порта "8080" и протокола "http" было указано то, что видится клиентам снаружи. Для этого достаточно модифицировать всего пару секций в XML-файле конфигурации "WildFly":

# vi /opt/wildfly/standalone/configuration/standalone.xml

....
  <!-- Блок перечисления активируемых при запуске "WildFly" сетевых портов. -->
  <socket-binding-group name="standard-sockets" default-interface="public" ... >
    ....
    <!-- Единственная точка входа клиентских запросов. -->
    <socket-binding name="http" port="${jboss.http.port:8080}"/>

    <!-- Параметры терминирования запросов вышестоящим проксирующим web-сервисом. -->
    <socket-binding name="proxy-https" port="443" />
    ....
  </socket-binding-group>
  ....

  <!-- Блок перечисления активируемых при запуске "WildFly" сетевых интерфейсов. -->
  <interfaces>
    ....
    <interface name="public">
        <inet-address value="${jboss.bind.address:127.0.0.1}"/>
    </interface>
  </interfaces>
  ....

  <!-- Блок описания конфигурации web-сервиса, обслуживающего HTTP-запросы. -->
  <subsystem xmlns="urn:jboss:domain:undertow:3.0">
    ....

    <server name="default-server">
        ....

        <!-- Все запросы внутри схемы обработки данных проходят по HTTP. -->
        <!-- (защита HTTPS реализована на вышестоящем web-сервере проксирования) -->
        <http-listener name="default" socket-binding="http"

          <!-- Деактивируем невостребованный в схеме обслуживания SSL/TLS вышестоящим Nginx параметр редиректа для обслуживания входящих HTTPS-запросов. -->
          <!-- redirect-socket="proxy-https" -->

          <!-- Указываем "WildFly", что запросы к нему проходят через web-"прокси", точка внешнего терминирования которого передаётся в параметрах "X-Forwarded-Host", "X-Forwarded-For", "X-Real-IP" и "X-Forwarded-Proto". -->
          proxy-address-forwarding="true" />
        ....

    </server>
    ....
  </subsystem>
....

В конфигурационном файле "WildFly" есть секция описания поддержки соединений по специальном протоколу согласования (AJP) с вышестоящим web-сервером "Apache". Мы выбрали для этой роли "Nginx", так что прослушивание ненужного в схеме порта отключаем:

# vi /opt/wildfly/standalone/configuration/standalone.xml

....
  <!-- Блок перечисления активируемых при запуске "WildFly" сетевых портов. -->
  <socket-binding-group name="standard-sockets" default-interface="public" ... >
    ....

    <!-- Деактивируем обслуживание невостребованного протокла AJP. -->
    <!-- socket-binding name="ajp" port="${jboss.ajp.port:8009}"/ -->
    ....
  </socket-binding-group>
....

Следует иметь в виду, что указанные в настройках конфигурационного файла "WildFly" параметры сетевого интерфейса могут переопределятся стартовым скриптом SystemV или Systemd.

Применение изменений в конфигурации требует перезапуска сервиса:

# systemctl restart wildfly

Обработка в "Nginx" некорректных ответов web-приложений.

Продолжая тему связки "WildFly" и фронтального web-сервера, приведу пример правил вынужденной корректировки ответов Java-приложений клиентам. Иногда - и не так уж редко - некомпетентные разработчики прикладных web-приложений не знают, как наладить корректную обработку параметров описания соединения "X-Forwarded-Host", "X-Forwarded-For", "X-Real-IP" и "X-Forwarded-Proto", поступающих от серверов проксирования и отвечают на запросы "как есть", полагая, что соседние компоненты работают на тех же FQDN и портах, что и они сами.

Приходится перехватывать ответы клиентам с указанием неправильных параметров источника и подменять их желаемыми:

# vi /etc/nginx/sites-available/wildfly.example.net.conf

....
# Блок описания web-сервиса приёма, терминирования SSL/TLS запросов и проксирования их нижележащему WildFly
server {
  listen      443 ssl http2;
  server_name wildfly.example.net;
  ....

  # Отправляем все запросы на обработку в WildFly
  location / {
    proxy_pass http://localhost:8080;
    ....
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    ....

    # Перехват некорректных ответов компонентов web-приложения и подстановка требуемых
    proxy_redirect ~*^http://(.+):8080(/.+)$ https://$1$2;
    #
    proxy_redirect ~*^http://(.+):80(/.+)$ https://$1$2;
    #
    proxy_redirect ~*^http://(.+)$ https://$1;
    #
    proxy_redirect ~*^https://(.+):80(/.+)$ https://$1$2;
  }
}

Развёртывание Java-приложения в контейнере "WildFly".

В современном сервере приложений "WildFly" встроен и по умолчанию уже активирован функционал автоматической установки модулей и приложений. Достаточно разместить WAR/EAR-дистрибутив в директории "/opt/wildfly/standalone/deployments/", и он будет автоматически развёрнут в соответствие с предустановленными настройками.

Легко отслеживать успешность установки web-приложения, по специальным маркерным файлам, имя которых соответствует WAR/JAR-дистрибутиву, а расширение принимает вид вроде ".deployed" или ".failed", в зависимости от исхода развёртывания. Внутри маркерного файла сохраняется журнал событий установки - так легко выяснить, в чём проблема, буде она имеется.


Заметки и комментарии к публикации:


Оставьте свой комментарий ( выразите мнение относительно публикации, поделитесь дополнительными сведениями или укажите на ошибку )