Корпоративный мир Java живёт по своим правилам. Здесь не принято собирать приложение в один исполняемый файл и запускать его через простой systemd-юнит. Здесь работают тяжёлые серверы приложений, которые управляют жизненным циклом развёрнутых модулей, занимаются пулингом соединений с базами, хранением сессий, балансировкой потоков и десятком других задач, о которых разработчик может вообще не задумываться. WildFly - один из таких серверов, и за его внешней простотой скрывается мощная платформа корпоративного уровня.

Этот продукт развивается под крылом RedHat и распространяется как свободное программное обеспечение. История проекта тянется ещё с тех времён, когда он назывался JBoss AS - имя, знакомое каждому Java-разработчику со стажем. В 2014 году произошёл ребрендинг, появилось название WildFly, но архитектурная преемственность сохранилась. Получился кросс-платформенный сервер приложений, заточенный под Java, лёгкий по меркам своего класса, гибкий благодаря модульной структуре и оснащённый удобной веб-консолью администратора.

Главная особенность WildFly - подключаемые подсистемы. Если приложению не нужен почтовый клиент или поддержка JMS, соответствующие модули можно отключить. Если же на сервере крутится сложный enterprise-проект, подключаются все необходимые компоненты. Такой подход даёт ощутимую экономию ресурсов на простых сценариях и не ограничивает возможности на сложных. Ниже разобран полный путь установки на Rocky Linux 8 - те же шаги работают на AlmaLinux 8 и CentOS 8 без изменений.

Подготовительные требования и базовое окружение для развёртывания Java-инфраструктуры

Чтобы пройти этот путь без сюрпризов, нужен сервер на Rocky Linux 8, AlmaLinux 8 или CentOS 8. Эти три дистрибутива - идейные наследники RHEL, и поведение системных утилит у них практически идентично. Доступ к серверу должен быть с правами root или через пользователя с возможностью повышения привилегий через sudo.

Ресурсы для WildFly зависят от планируемой нагрузки и сложности приложений. Минимальная конфигурация для тестов - 2 ядра CPU и 2 ГБ оперативной памяти. Для боевых проектов лучше начинать от 4 ядер и 8 ГБ - JVM любит память, и при недостатке начинаются мучения с GC-паузами. Дисковое пространство - 10 ГБ под систему и сам сервер плюс место под развёртываемые приложения.

В этом руководстве команды приведены без префикса sudo, как в оригинале - подразумевается работа из-под root. При работе под обычным пользователем перед каждой командой нужно ставить sudo.

Установка OpenJDK 11 и проверка корректности работы виртуальной машины Java

WildFly написан на Java и не запустится без JVM в системе. Поддерживаются разные дистрибутивы Java - официальный Oracle JDK, OpenJDK, Amazon Corretto, Azul Zulu и другие. Для большинства задач хватит OpenJDK из стандартного репозитория - бесплатно, обновляется централизованно вместе с системой и не требует регистрации на сторонних сайтах.

Ставим OpenJDK 11 версии для разработчиков (с devel-пакетом):

dnf install java-11-openjdk-devel -y

Пакет devel включает не только саму JVM для запуска приложений, но и инструменты для работы с Java-классами - jar, javac, javadoc и прочие. Для запуска WildFly хватило бы и обычного java-11-openjdk без devel, но имея под рукой полный набор инструментов, проще диагностировать проблемы и работать с архивами развёртываний.

Проверяем результат установки:

java --version

Ожидаемый вывод выглядит примерно так:

openjdk 11.0.8 2020-07-14 LTS
OpenJDK Runtime Environment 18.9 (build 11.0.8+10-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.8+10-LTS, mixed mode, sharing)

Цифры конкретной версии будут отличаться - они зависят от того, когда был последний раз обновлён пакет в репозитории. Главное - что Java определилась, версия 11, и режим работы 64-Bit Server VM. Серверный режим JVM оптимизирован для длительно работающих приложений с упором на пиковую производительность, в отличие от клиентского, который оптимизирует время старта.

Почему именно 11 версия? WildFly поддерживает разные версии Java, но 11 - это LTS (Long Term Support) релиз с долгосрочной поддержкой и хорошей совместимостью. Можно поставить и более свежие 17 или 21 - они тоже LTS и работают отлично, но в этом руководстве сохранён выбор оригинала, чтобы не отклоняться от проверенного сценария.

Загрузка дистрибутива WildFly и распаковка в стандартное место установки

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

Создаём группу и пользователя:

groupadd --system wildfly
useradd -s /sbin/nologin --system -d /opt/wildfly -g wildfly wildfly

Флаг --system при создании группы и пользователя помещает их в системный диапазон UID/GID (обычно ниже 1000). Параметр -s /sbin/nologin задаёт оболочку, которая запрещает интерактивный вход - попытка зайти под пользователем wildfly через ssh или su завершится отказом. Опция -d /opt/wildfly указывает домашний каталог (он же станет местом установки сервера), -g wildfly связывает пользователя с одноимённой группой.

Скачиваем дистрибутив с официального сайта:

wget https://download.jboss.org/wildfly/20.0.1.Final/wildfly-20.0.1.Final.tar.gz

Версия 20.0.1.Final - та, которая была актуальна на момент написания оригинальной статьи. На сайте проекта могут быть более свежие выпуски - перед использованием стоит зайти на главную и подставить актуальный номер. Принципы установки от версии к версии меняются редко, основная схема остаётся прежней.

Распаковываем архив:

tar -xvzf /root/wildfly-20.0.1.Final.tar.gz

Флаги -xvzf расшифровываются как extract (извлечь), verbose (подробный вывод), gunzip (через gzip-распаковку), file (имя архива указано следующим параметром). После выполнения в текущем каталоге появится папка wildfly-20.0.1.Final со всем содержимым сервера.

Перемещаем её в стандартное место:

mv wildfly-20.0.1.Final /opt/wildfly

Каталог /opt традиционно используется для пакетов, которые ставятся не из репозиториев дистрибутива. Это давнее соглашение Filesystem Hierarchy Standard, и придерживаться его - хороший тон. К тому же мы заранее указали /opt/wildfly как домашний каталог пользователя wildfly, так что всё логично сходится.

Готовим каталог для конфигурационных файлов сервера:

mkdir /etc/wildfly

В отличие от файлов программы, конфигурация по конвенции должна жить в /etc. Это отделяет настройки от собственно программного обеспечения, упрощает резервное копирование (бэкапить /etc куда проще, чем разбираться с разрозненными конфигами в /opt) и помогает при обновлениях - новая версия дистрибутива не затрёт старую конфигурацию, если она лежит в отдельной директории.

Настройка systemd юнита для автоматического управления службой сервера приложений

Сами разработчики WildFly предусмотрели заготовки для systemd-интеграции, и эти заготовки лежат внутри дистрибутива. Копируем их в нужные места:

cp /opt/wildfly/docs/contrib/scripts/systemd/wildfly.conf /etc/wildfly/
cp /opt/wildfly/docs/contrib/scripts/systemd/wildfly.service /etc/systemd/system/
cp /opt/wildfly/docs/contrib/scripts/systemd/launch.sh /opt/wildfly/bin/

Первая команда переносит файл с переменными окружения в /etc/wildfly. Здесь можно прописывать параметры запуска - режим работы (standalone или domain), путь к JVM, имя конфигурационного XML-файла и адрес для прослушивания. Вторая команда копирует сам systemd-юнит в стандартное место. Третья переносит скрипт запуска, который запускает WildFly с правильными параметрами.

Раздаём корректные права:

chmod +x /opt/wildfly/bin/launch.sh
chown -R wildfly:wildfly /opt/wildfly
chmod -R +x /opt/wildfly/

Первая команда делает скрипт запуска исполняемым - без этого systemd просто откажется его запускать. Вторая рекурсивно меняет владельца всего каталога WildFly на нашего системного пользователя. Третья выставляет права на исполнение для всего содержимого - это нужно, поскольку внутри дистрибутива есть множество вложенных скриптов и утилит.

Перечитываем конфигурацию systemd:

systemctl daemon-reload

Эта команда обязательна после любых изменений в юнит-файлах. Без неё systemd работает по старому кешированному набору, и новые правки игнорируются.

Запускаем службу и включаем автозапуск при загрузке системы:

systemctl start wildfly
systemctl enable wildfly

Первая команда поднимает сервер прямо сейчас, вторая прописывает запуск при перезагрузке. Без enable после рестарта системы WildFly останется в покое.

Проверяем результат:

systemctl status wildfly

В выводе должно быть active (running) зелёным шрифтом:

? wildfly.service - The WildFly Application Server
   Loaded: loaded (/etc/systemd/system/wildfly.service; disabled; vendor preset: disabled)
   Active: active (running) since Sun 2020-09-13 05:57:22 EDT; 16s ago
 Main PID: 31834 (launch.sh)
    Tasks: 123 (limit: 12527)
   Memory: 304.1M
   CGroup: /system.slice/wildfly.service
           ??31834 /bin/bash /opt/wildfly/bin/launch.sh standalone standalone.xml 0.0.0.0
           ??31835 /bin/sh /opt/wildfly/bin/standalone.sh -c standalone.xml -b 0.0.0.0
           ??31925 java -D[Standalone] -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=tru>

Sep 13 05:57:22 centos8 systemd[1]: Started The WildFly Application Server.

В дереве процессов видна красивая иерархия. Сначала systemd запускает launch.sh, тот вызывает standalone.sh, а уже последний поднимает собственно JVM с длинной командной строкой параметров. Параметры -Xms64m и -Xmx512m задают начальный и максимальный размер кучи - именно их обычно подкручивают первыми, когда нужно увеличить производительность под нагрузку.

Проверяем, что сервер слушает свой основной порт:

ss -tunelp | grep 8080

Ожидаемый вывод:

tcp    LISTEN   0        128               0.0.0.0:8080           0.0.0.0:*      users:(("java",pid=31925,fd=478)) uid:989 ino:59014 sk:9

И панель администратора:

ss -tunelp | grep 9990

Ожидаемый вывод:

tcp    LISTEN   0        50              127.0.0.1:9990           0.0.0.0:*      users:(("java",pid=31925,fd=138)) uid:989 ino:59017 sk:7

Заметна важная деталь. Порт 8080 слушает 0.0.0.0 - то есть все сетевые интерфейсы, включая внешний. А вот 9990 (порт админки) привязан к 127.0.0.1, то есть доступен только с локального хоста. Это дефолтная защитная мера - чтобы случайно открытая в интернет админка не стала подарком злоумышленникам. Дальше мы это поведение поменяем, но осознанно.

Создание административного пользователя для входа в консоль управления

Доступ к веб-консоли защищён паролем, и пользователя нужно создать вручную через специальный скрипт. WildFly не использует базу данных для хранения учёток - всё хранится в обычных property-файлах, что и проще, и надёжнее для административных учётных записей.

Запускаем интерактивный скрипт:

/opt/wildfly/bin/add-user.sh

Скрипт начнёт задавать вопросы. Первый - какой тип пользователя создаём:

What type of user do you wish to add? 
 a) Management User (mgmt-users.properties) 
 b) Application User (application-users.properties)
(a): a

Вариантов два. Management User - администратор сервера, имеющий доступ к консоли управления. Application User - учётка для самих приложений, которые могут использовать встроенный механизм аутентификации WildFly. Для нашей задачи нужен первый вариант, набираем a и нажимаем Enter.

Дальше спрашивают имя пользователя:

Enter the details of the new user to add.
Using realm 'ManagementRealm' as discovered from the existing property files.
Username : wildflyadmin

Вводим желаемое имя - в примере это wildflyadmin, но можно выбрать любое осмысленное.

Затем спрашивают пароль с показом рекомендаций:

Password recommendations are listed below. To modify these restrictions edit the add-user.properties configuration file.
 - The password should be different from the username
 - The password should not be one of the following restricted values {root, admin, administrator}
 - The password should contain at least 8 characters, 1 alphabetic character(s), 1 digit(s), 1 non-alphanumeric symbol(s)
Password : 
Re-enter Password : 

Требования разумные - пароль должен отличаться от имени пользователя, не входить в список банальных значений (root, admin, administrator) и содержать минимум 8 символов с обязательным буквенно-цифрово-спецсимвольным разнообразием. На боевом сервере имеет смысл использовать пароль длиннее и сложнее минимальных требований - 16-20 символов с генерацией через pwgen или openssl rand.

Дальше идёт вопрос про группы:

What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]: 

Для базового администратора группы не нужны, оставляем пустым и нажимаем Enter.

Подтверждение создания:

About to add user 'wildflyadmin' for realm 'ManagementRealm'
Is this correct yes/no? yes
Added user 'wildflyadmin' to file '/opt/wildfly/standalone/configuration/mgmt-users.properties'
Added user 'wildflyadmin' to file '/opt/wildfly/domain/configuration/mgmt-users.properties'
Added user 'wildflyadmin' with groups  to file '/opt/wildfly/standalone/configuration/mgmt-groups.properties'
Added user 'wildflyadmin' with groups  to file '/opt/wildfly/domain/configuration/mgmt-groups.properties'
Is this new user going to be used for one AS process to connect to another AS process? 
e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls.
yes/no? yes
To represent the user add the following to the server-identities definition 

Последний вопрос касается использования учётки для межсерверного взаимодействия в распределённой конфигурации. Если планируется кластер из нескольких серверов WildFly, отвечаем yes - тогда скрипт сгенерирует специальный токен для секретного обмена. Для одиночной установки можно ответить no.

Настройка SELinux и брандмауэра для корректной работы веб-доступа

Rocky Linux наследует от RedHat жёсткую политику безопасности, и SELinux включён по умолчанию. Это вторая защитная стена после стандартных Linux-прав, которая может неожиданно блокировать вполне легитимные действия. Чтобы WildFly работал без сюрпризов, нужно явно прописать правила для его файлов.

Применяем настройки SELinux:

semanage fcontext -a -t bin_t "/opt/wildfly/bin(/.*)?"
restorecon -Rv /opt/wildfly/bin/
setsebool -P httpd_can_network_connect 1

Первая команда добавляет контекст bin_t для всех файлов в /opt/wildfly/bin - это говорит SELinux, что внутри лежат исполняемые файлы, которые можно запускать. Регулярное выражение в кавычках охватывает сам каталог и все вложенные. Вторая команда применяет новые правила к уже существующим файлам через restorecon с флагом -Rv (рекурсивно с подробным выводом). Третья команда поднимает булевый флаг httpd_can_network_connect, разрешающий веб-серверу делать сетевые подключения - это пригодится Nginx для обращения к локальному WildFly на порту 8080.

Открываем нужные порты в firewalld:

firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --permanent --add-port=9990/tcp
firewall-cmd --permanent --add-port=80/tcp

Порт 8080 - основной для приложений WildFly. Порт 9990 - для админ-консоли. Порт 80 - для будущего Nginx, который мы поставим следующим шагом. Флаг --permanent делает правила постоянными (без него правила исчезли бы после перезагрузки firewalld).

Применяем изменения:

firewall-cmd --reload

После reload новые правила вступают в силу, и трафик до WildFly начинает доходить.

Открытие административной консоли для удалённого доступа через правку скрипта запуска

В дефолтной конфигурации админ-консоль слушает только 127.0.0.1, что отрезает любые удалённые подключения. Для разработки на одном сервере это удобно, но для нашего сценария (когда консоль нужна с рабочей машины администратора) придётся менять.

Открываем скрипт запуска:

nano /opt/wildfly/bin/launch.sh

Находим строку:

    $WILDFLY_HOME/bin/standalone.sh -c $2 -b $3

Меняем её на следующую:

    $WILDFLY_HOME/bin/standalone.sh -c $2 -b $3 -bmanagement=0.0.0.0

Добавленный параметр -bmanagement=0.0.0.0 заставляет management-интерфейс слушать все сетевые интерфейсы, а не только localhost. Параметр -b $3 (для основного интерфейса приложений) уже был в строке, и при запуске через launch.sh туда передаётся 0.0.0.0 - именно поэтому 8080 изначально доступен снаружи.

Сохраняем файл и перезапускаем сервис:

systemctl restart wildfly

Открываем браузер и переходим по адресу http://your-server-ip:9990. Появится диалог авторизации, в который вводим имя пользователя wildflyadmin (или какое выбрали при создании) и пароль. После входа открывается консоль управления с разделами для развёртывания приложений, мониторинга, настройки подсистем и многого другого.

Стоит держать в голове, что открытие админки во внешний интернет - не самая безопасная практика. Для боевого окружения лучше оставить -bmanagement привязанным к конкретному внутреннему интерфейсу или к VPN-сети, через которую заходят администраторы. Альтернатива - доступ через SSH-туннель: ssh -L 9990:localhost:9990 user@server, после чего админка открывается на локальном хосте администратора.

Настройка Nginx как обратного прокси для удобного доступа к приложениям

Заставлять пользователей помнить порт 8080 и набирать его в браузере - не очень дружелюбно. Гораздо удобнее открыть приложения на стандартном 80-м порту, что в перспективе откроет дорогу и к 443 с TLS-сертификатом. Для этой цели идеально подходит Nginx в роли обратного прокси.

Ставим Nginx:

dnf install nginx -y

Создаём отдельный файл конфигурации для нашего сайта:

nano /etc/nginx/conf.d/wildfly.conf

Содержимое:

upstream wildfly {
  server 127.0.0.1:8080 weight=100 max_fails=5 fail_timeout=5;
}

server {
  listen          80;
  server_name     your-server-ip;

  location / {
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://wildfly/;
  }
}

Разберём по частям. Блок upstream объявляет группу бэкендов с именем wildfly. В нашем случае бэкенд один - локальный WildFly на 8080 порту. Параметр weight=100 задаёт вес для балансировки (актуально только при нескольких бэкендах), max_fails=5 разрешает до пяти неудачных попыток подряд перед признанием бэкенда нерабочим, fail_timeout=5 задаёт период проверки в секундах.

Блок server описывает виртуальный хост на 80-м порту. Директива server_name стоит подменить на реальное доменное имя или IP - именно по этому имени Nginx будет матчить входящие запросы. Если сервер в инфраструктуре один и других вирт-хостов нет, можно оставить и так, но при добавлении новых сайтов это станет важным.

Блок location / ловит все запросы и проксирует их в нашу группу бэкендов wildfly. Заголовки X-Forwarded-Host, X-Forwarded-Server и X-Forwarded-For передают в WildFly информацию об исходном запросе - какой домен запрашивал пользователь и с какого IP. Без этих заголовков приложение видело бы все запросы как идущие от localhost (поскольку Nginx стоит рядом), что ломает логирование и любые проверки по IP.

Проверяем синтаксис:

nginx -t

Ожидаемый вывод:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Запускаем Nginx и включаем автозапуск:

systemctl start nginx
systemctl enable nginx

Проверяем статус:

systemctl status nginx

Вывод должен показать active (running):

? nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/nginx.service.d
           ??php-fpm.conf
   Active: active (running) since Sun 2020-09-13 06:03:37 EDT; 5s ago
  Process: 1775 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
  Process: 1773 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
  Process: 1771 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
 Main PID: 1776 (nginx)
    Tasks: 3 (limit: 12527)
   Memory: 5.4M
   CGroup: /system.slice/nginx.service
           ??1776 nginx: master process /usr/sbin/nginx
           ??1777 nginx: worker process
           ??1778 nginx: worker process

Sep 13 06:03:37 centos8 systemd[1]: Stopped The nginx HTTP and reverse proxy server.
Sep 13 06:03:37 centos8 systemd[1]: Starting The nginx HTTP and reverse proxy server...
Sep 13 06:03:37 centos8 nginx[1773]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
Sep 13 06:03:37 centos8 nginx[1773]: nginx: configuration file /etc/nginx/nginx.conf test is successful
Sep 13 06:03:37 centos8 systemd[1]: Started The nginx HTTP and reverse proxy server.

Теперь можно зайти в браузере на http://your-server-ip без указания порта - и откроется приветственная страница WildFly. Все запросы идут через Nginx, который незаметно для пользователя пробрасывает их на 8080 и возвращает ответ.

Распространённые подводные камни и практические советы для эксплуатации в боевых условиях

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

Первая частая проблема - выделение памяти JVM. Стандартные параметры -Xms64m -Xmx512m рассчитаны на минимальный сценарий. Любое серьёзное приложение быстро упрётся в потолок, и WildFly начнёт сыпать OutOfMemoryError. Лекарство - править файл /etc/wildfly/wildfly.conf или launch.sh, прописывая адекватные размеры. На сервере с 8 ГБ памяти разумно выделить JVM 4-6 ГБ, оставив остальное системе и кешам. Настройка делается через JAVA_OPTS с параметрами -Xms и -Xmx.

Вторая популярная грабля - забытое HTTPS-шифрование. WildFly из коробки работает по HTTP, и пароли учёток приложений летят по сети открытым текстом. Решение - либо настроить TLS прямо в WildFly через keystore, либо проще и универсальнее повесить TLS на Nginx через Let's Encrypt и certbot. Второй вариант обычно предпочтительнее, поскольку обновление сертификатов автоматизируется в одном месте, а не дублируется в нескольких сервисах.

Третий момент касается развёртывания приложений. WildFly умеет два режима - hot deployment (автоматическое подхватывание war-файлов из каталога deployments) и управляемый деплой через консоль или CLI. Hot deployment удобен для разработки, но в продакшене лучше использовать управляемый - он даёт контроль над процессом, возможность отката и не зависит от случайных перемещений файлов. CLI-инструмент /opt/wildfly/bin/jboss-cli.sh умеет всё, что умеет веб-консоль, и легко скриптуется для автоматизации.

Четвёртая тонкость - мониторинг JVM. Без наблюдения за heap, потоками и GC-паузами серьёзная эксплуатация невозможна. WildFly экспортирует метрики через стандартные интерфейсы JMX, которые можно собирать через Prometheus с помощью JMX Exporter, через Zabbix или любой другой стек мониторинга. Настраивать это нужно сразу, пока проект не разросся до критичного размера.

Пятая граблина - SELinux. Несмотря на наши настройки, сложные сценарии могут потребовать дополнительных правил - например, если WildFly должен писать логи в нестандартное место или подключаться к удалённой базе данных через специфичный порт. Проблемы SELinux диагностируются через ausearch -m avc -ts recent, и это первое место, куда стоит заглядывать при необъяснимых отказах в доступе.

Где такая инсталляция пригодится в реальной жизни? Сценариев масса. Корпоративные Java-приложения с запутанной бизнес-логикой и кучей интеграций. Внутренние порталы крупных компаний, где требуется полная enterprise-функциональность - JTA-транзакции, EJB, JMS-очереди, CDI. Унаследованные системы, написанные ещё на Java EE и не переписанные под Spring Boot или Quarkus. Среды разработки и тестирования, где нужен полнофункциональный сервер приложений для проверки совместимости.

Освоение WildFly даёт инженеру не просто навык установки одного сервера, а целое погружение в мир enterprise Java. Понимание модульной архитектуры, опыт работы с JVM-tuning, знание принципов работы серверов приложений - всё это переносимо на другие продукты экосистемы. Тот, кто разобрался с WildFly, без труда осваивает Apache Tomcat, IBM WebSphere, Oracle WebLogic или GlassFish. И хотя современная мода тяготеет к более лёгким решениям вроде Spring Boot и микросервисов, классические серверы приложений никуда не уходят. В банках, страховых компаниях, государственных структурах продолжают работать огромные приложения, которым WildFly даёт надёжную почву под ногами. Этот инструмент стоит знать и держать в арсенале как минимум по той причине, что встретиться с ним в реальной работе может каждый, кто связан с серьёзной Java-инфраструктурой.