Привет, Хабр!
Выступление Себастьяна Дашнера на java meetup в московском офисе IBM (нашел запись похожего выступления) подтолкнуло меня начать свое знакомство с легковесными серверами приложений, в частности, с OpenLiberty. И тогда я задумался:
- Какие преимущества дают легковесные сервера приложений?
- Как меняется специфика работы при их использовании?
- Зачем упаковывать сервер приложений в контейнер?
Отвечая на эти вопросы, я заметил, что информации по этой теме в открытом доступе мало, потому решил собрать и систематизировать её здесь.
Результаты выкладываю под катом.
Какие преимущества дают легковесные сервера приложений?
Раньше корпоративные серверы приложений Java EE (такие как JBoss AS, Oracle WebLogic, IBM WebSphere AS) считались тяжеловесной и громоздкой конструкцией, особенно, если мы говорим о времени запуска и развертывания. Но облачные технологии захватывают все большую часть индустрии и требования к серверам приложений меняются.
И вот, на смену полнофункциональным, корпоративным серверам приложений, приходят быстрые, модульные, небольшие сервера приложений, сфокусированные на выполнение определенной задачи: Thorntail, Payara Micro – младшие братья WildFly и Payara; Meecrowave – легковесный JAX-RS+CDI+JSON сервер, KumuluzEE – сервер, позволяющий расширять Java EE с помощью Node.js, Go и прочие.
В этот список входит и OpenLiberty — open source сервер приложений (распространяется по EPL-1.0) поддерживающий самые последние стандарты Java EE/Microprofile, на базе которого работает WebSphere Liberty.
Вкратце об особенностях EPL-1.0 (Eclipse Public License Version 1.0)
EPL 1.0 базируется на CPL и не совместима с GPL, разрешает соблюдать другие лицензии и патенты, которые используются в работе, и предоставляет право лицензировать продукт под любой другой лицензией, копия лицензии должна быть включена во все копии программы.
Дополнения к основному продукту могут лицензироваться отдельно и даже на коммерческой лицензии. Однако изменения и дополнения, которые представляют собой производные работы, должны быть лицензированы под такой же самой лицензией EPL, которая требует сделать исходный код открытым.
Связывание программного проекта с кодом, защищённым лицензией EPL (например, использование этого кода в качестве библиотеки), в общем случае, не делает этот проект производной работой и не накладывает соответствующих обязательств.
Участник, который включает Программу в коммерческое предложение, должен сделать это так, чтобы избежать потенциальной ответственности за других Участников. (явно отказаться от любых гарантий и ответственности от лица всех авторов)
Например: Участник может включить Программу в коммерческое предложение, продукт X. Тогда этот участник является Коммерческим участником. Если этот Коммерческий участник затем делает утверждения о скорости работы или предлагает гарантии по продукту X, эти утверждения и предложения являются персональной ответственностью Коммерческого участника. В рамках данного раздела Коммерческий участник должен защищать других Участников от претензий, относящихся к производительности и гарантиям, и если суд потребует любого другого Участника оплатить какой-либо результирующий ущерб, Коммерческий участник должен оплатить эти убытки.
Дополнения к основному продукту могут лицензироваться отдельно и даже на коммерческой лицензии. Однако изменения и дополнения, которые представляют собой производные работы, должны быть лицензированы под такой же самой лицензией EPL, которая требует сделать исходный код открытым.
Связывание программного проекта с кодом, защищённым лицензией EPL (например, использование этого кода в качестве библиотеки), в общем случае, не делает этот проект производной работой и не накладывает соответствующих обязательств.
Участник, который включает Программу в коммерческое предложение, должен сделать это так, чтобы избежать потенциальной ответственности за других Участников. (явно отказаться от любых гарантий и ответственности от лица всех авторов)
Например: Участник может включить Программу в коммерческое предложение, продукт X. Тогда этот участник является Коммерческим участником. Если этот Коммерческий участник затем делает утверждения о скорости работы или предлагает гарантии по продукту X, эти утверждения и предложения являются персональной ответственностью Коммерческого участника. В рамках данного раздела Коммерческий участник должен защищать других Участников от претензий, относящихся к производительности и гарантиям, и если суд потребует любого другого Участника оплатить какой-либо результирующий ущерб, Коммерческий участник должен оплатить эти убытки.
Давайте посмотрим, какие преимущества мы можем получить, развернув приложение в контейнере с OpenLiberty. (У вас должна быть установлена любая версия java, дальнейшие действия необходимо выполнять, находясь в директории wlp)
Скорость:
Скорость — важнейший показатель для облачного приложения, ведь для того, чтобы облачное приложение быстро масштабировалось, управлялось и справлялось с растущей нагрузкой, оно должно запускаться за считанные секунды. Легковесный сервер приложений способен сделать это. Чтобы проверить — скачаем сервер OpenLiberty и выполним bin/server run (полный список команд):
$ bin/server run
Запуск defaultServer (Open Liberty 19.0.0.6/wlp-1.0.29.cl190620190617-1530) в Java HotSpot(TM) 64-Bit Server VM версии 1.8.0_212-b10 (ru_US)
[AUDIT ] CWWKE0001I: Сервер defaultServer запущен.
[AUDIT ] CWWKZ0058I: Выполняется мониторинг dropins для приложений.
[AUDIT ] CWWKF0012I: На сервере установлены следующие комплекты: [el-3.0, jsp-2.3, servlet-3.1].
[AUDIT ] CWWKF0011I: Сервер defaultServer готов к работе для разумной планеты. Сервер defaultServer запущен за 1,709 сек.
Модульность и гибкость
Большинству приложений не нужна Java EE целиком, а требуется выделенный набор стандартов, чаще всего используемый в enterprise-приложениях. Благодаря OSGI, мы можем выбрать нужный нам набор стандартов Java EE и/или MicroProfile, игнорируя все остальное.
Например, объявим JAX-RS из Java EE и mpHealth из Microprofile, добавив пару строк в featureManager блок usr/servers/serverName/server.xml
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">
<!-- Enable features -->
<featureManager>
<feature>jsp-2.3</feature>
<feature>mpHealth-1.0</feature>
<feature>jaxrs-2.1</feature>
</featureManager>
<!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
<httpEndpoint id="defaultHttpEndpoint"
httpPort="9080"
httpsPort="9443" />
<!-- Automatically expand WAR files and EAR files -->
<applicationManager autoExpand="true"/>
</server>
Динамическое обновление
В процессе разработки программный код и конфигурация постоянно изменяются.
Сервер приложений настроен для мониторинга изменений, и при необходимости переконфигурирует и развернет приложение на лету. Вот например, реакция на последние изменения:
[AUDIT ] CWWKG0016I: Начато обновление конфигурации сервера.
[AUDIT ] CWWKG0017I: Конфигурация сервера успешно обновлена за 0,475 сек.
[AUDIT ] CWWKF0012I: На сервере установлены следующие комплекты: [cdi-2.0, jaxrs-2.1, jaxrsClient-2.1, jndi-1.0, json-1.0, jsonp-1.1, mpHealth-1.0, servlet-4.0].
[AUDIT ] CWWKF0013I: С сервера удалены следующие комплекты: [servlet-3.1].
[AUDIT ] CWWKF0008I: Обновление комплектов выполнено за 0,476 сек.
Размер и сборка образа
Размеры серверов приложений заметно уменьшились и теперь позволяют разворачивать
каждое приложение на отдельном сервере приложений для упаковки их в контейнеры, давая широчайшую гибкость. Более того, т.к. образ состоит из слоев, при повторной сборке и распространении артефактов, копируется только слой приложения, ОС, среда выполнения и сервер приложений кэшируются.
В dockerhub есть готовые образы, содержащие предварительно настроенный сервер OpenLiberty. Воспользуемся одним из них и создадим Dockerfile:
FROM open-liberty
COPY usr/servers/defaultServer /opt/ol/wlp/usr/servers/defaultServer
ENTRYPOINT ["/opt/ol/wlp/bin/server", "run"]
CMD ["defaultServer"]
Соберём образ:
docker build -t app .
Запустим его как контейнер:
docker run -d --name app -p 9080:9080 app
Проверим полученный результат http://localhost:9080/health/
Для остановки контейнера:
docker stop app
Дальнейших сценариев использования контейнера множество, и в целом, это повод для отдельных статей, поэтому вернемся к вопросам.
Как меняется специфика работы?
Комплектация
Образ контейнера должен собираться только один раз, а затем выполняется во всех средах. Поэтому рекомендуется собирать каждое приложение вместе c сервером приложений. Это упрощает жизненный цикл и развертывание приложений и отлично вписывается в современный мир контейнерных технологий.
Сборка
Сейчас уже не нужно упаковывать разные технические блоки в отдельные архивы. Вся бизнес-логика вместе с веб-сервисами и сквозным функционалом упаковывается в один war-файл. Это значительно упрощает установку проекта, а также процедуру сборки. Больше не приходится упаковывать приложение в виде нескольких уровней иерархии, чтобы затем снова распаковать его в один экземпляр сервера.
Запуск
И сервер приложений, и само приложение добавляются в образ в процессе сборки. Потенциальная конфигурация источников данных, накопителей или модулей сервера также задается в процессе сборки, добавлением специализированных файлов конфигурации. Всеми различиями в конфигурации следует управлять не внутри приложения, а извне. Поэтому приложение следует не развертывать в уже запущенном контейнере, а добавлять в него на этапе сборки образа в каталог автоматического развертывания, чтобы запускать его в момент запуска контейнера.
Расширение функционала
Контейнеры, системы развёртывания и масштабирования, сервисы мониторинга и сервисные сетки дали нам возможность настраивать обнаружение, мониторинг, управление, аутентификацию, масштабирование, трассировку, устойчивость поверх приложения, прозрачно перенося большое количество логики на другой уровень, облегчая приложение и упрощая его разработку.
Зачем упаковывать сервер приложений в контейнер?
Прежде всего, упаковывая сервер приложений в контейнер, вы даете возможность каждой команде независимо настроить свой сервер приложений и сфокусироваться на реализации функций, экономя время разработчиков на производство ручных операций, связующего кода, и различные согласования.
Бонусом вы получаете возможность полноценно пользоваться преимуществами контейнеров и всех инструментов, построенных вокруг них. Приложением становится легко управлять и масштабировать, обновлять, а также автоматизировать сборку и развертывания артефактов с нулевым временем простоя.
Больше практики вы найдете здесь
На сайте проекта помимо документации есть большое количество обучающих руководств: как создавать web-приложения с maven/gradle, упаковывать и разворачивать приложения, разворачивать и настраивать микросервисы в kubernetes, управлять трафиком с помощью istio, выполнять развертывание в IBM Cloud, у других популярных облачных провайдеров и многом другом.
Себастьян Дашнер (Java & OSS энтузиаст, Java Champion, IBMer, JCP member, Jakarta EE committer) в своем блоге публикует полезные статьи о том, как пользоваться OpenLiberty, например, мониторинг Open Liberty с помощью Prometheus и Grafana, а его книга Architecting Modern Java EE Applications использовалась при подготовки этой статьи.
Liberty-maven-plugin (альтернатива для gradle) может заметно упростить вашу работу. Кстати, в руководствах есть хорошие примеры его использования.
Вы также можете внести свой вклад в проект.
Себастьян Дашнер (Java & OSS энтузиаст, Java Champion, IBMer, JCP member, Jakarta EE committer) в своем блоге публикует полезные статьи о том, как пользоваться OpenLiberty, например, мониторинг Open Liberty с помощью Prometheus и Grafana, а его книга Architecting Modern Java EE Applications использовалась при подготовки этой статьи.
Liberty-maven-plugin (альтернатива для gradle) может заметно упростить вашу работу. Кстати, в руководствах есть хорошие примеры его использования.
Вы также можете внести свой вклад в проект.