Quarkus — новый взгляд на Cloud Native Java

Автор оригинала: Javier Ramos
  • Перевод
Привет, Хабр!

В наступившем новом году мы планируем всерьез развивать темы контейнеров, Cloud-Native Java и Kubernetes. Логичным продолжением этих тем на русском языке будет рассказ о фреймворке Quarkus, уже рассмотренном в хорошей статье на Хабре. Сегодняшняя статья посвящена не столько устройству "субатомной сверхбыстрой Java", сколько тем перспективам, которые Quarkus привносит в Enterprise.

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

Введение


Чем плотнее я занимаюсь DevOps, контейнерами и бессерверными технологиями, тем чаще обнаруживаю, что пишу мой контейнеризованный код в легких контейнерах или FaaS на Python или JavaScript. Java просто слишком тяжел для начальной загрузки, чтобы использовать его в бессерверном фреймворке. Что касается работы с микросервисами, JavaScript или Python обеспечивают более быструю загрузку и более компактные контейнеры, благодаря чему оказываются эффективнее Java.



Python и JavaScript – самые лучшие языки для создания облачно-ориентированных микросервисов

Языку Java больше 20 лет, и во времена его зарождения мир был совершенно иным, нежели сейчас. С появлением JVM были решены огромные проблемы – мы получили возможность единожды написать код и запускать его на множестве платформ и операционных систем. Контейнеры позволяют упаковывать приложения, библиотеки и ресурсы операционной системы в отдельные емкости, и каждый такой контейнер может работать где угодно. Портируемость, которую обеспечивает JVM, теперь не так актуальна. В свое время мы были готовы нести дополнительные издержки для обеспечения портируемости, но теперь эти времена прошли. Теперь требуется быстрая работа с минимальными задержками и реактивные приложения, которые всегда будут доступны. Контейнеры и инструменты для оркестрации контейнеров, например, Kubernetes, обеспечивают такие возможности независимо от языка программирования.

Компании, переходящие к использованию микросервисных архитектур, берут имеющиеся у них Spring-сервисы, написанные Java, связывают их в увесистые jar-архивы, добавляют JDK и запускают в контейнере, работающем на основе Linux. Такое решение работает, но вам приходится управляться с тяжелыми контейнерами размером по 500МБ, которые приводятся в состояние доступности за 10-30 секунд каждый; это серьезная проблема. После миграции многие компании медленно переходят на использование Python, оставляя сервисы серверной части на Java, а, в конце концов, останавливаются на FaaS.

Бессерверные технологии и FaaS сегодня очень популярны, поскольку позволяют сосредоточиться на написании функций, не беспокоясь при этом об инфраструктуре. Как бы то ни было, все они работают в контейнерах, но облачный провайдер управляет их жизненным циклом. Самое приятное, что, спустя определенное время, провайдер полностью останавливает контейнер и возобновляет его работу только после следующего вызова, то есть, вы оплачиваете только время фактической работы. Первый вызов функции может продлиться несколько больше обычного, это и есть знаменитый холодный старт. Дело в том, что контейнеру необходима первичная загрузка. При использовании Python или JavaScript это не такая большая проблема, но в случае с Java первичная загрузка может занимать 10-15 секунд, а это уже приговор и одна из причин снижения популярности Java. Сейчас нам нужен код, способный запуститься, выполнить задачу, а затем остановиться. Мы не хотим иметь дел со множеством потоков или долгоиграющими процессами, нам нужны короткоживущие процессы, которые могут загружаться очень быстро.

Знакомство с Quarkus


Если вы читаете технические блоги или следите за новостями, то, возможно, полагаете, что бессерверная парадигма захватывает мир, и все воспринимают ее с крайним энтузиазмом. Сейчас стартап может писать функции и предоставлять их в облаке как услугу – благодаря использованию JavaScript – а также масштабировать их для поддержки миллионов пользователей, без необходимости управлять при этом инфраструктурой. Правда, существует еще и реальный мир за пределами Кремниевой долины: финансовые институты, государственное управление, розничная торговля и множество других отраслей, обслуживаемых при помощи миллионов строк на Java, переписывать которые слишком накладно. Поэтому приходится принять как данность тот факт, что в этих отраслях остается и дальше пользоваться тяжеловесными контейнерами.



GraalVM и, в частности, Substrate VM, сегодня открывают двери для славного и долгого будущего языка Java. GraalVM – это универсальная виртуальная машина для выполнения приложений, написанных на JavaScript, Python, Ruby, R и языках для JVM, в частности, Java, Scala или Kotlin. Самое классное, что GraalVM позволяет заранее (в AOT-режиме) компилировать программы в нативный исполняемый файл. Это означает, что вы можете компилировать ваш код Java непосредственно в машинно-специфичный код. Получаемая в результате программа не работает на Java HotSpot VM, но использует все необходимые компоненты, в частности, управление памятью, планирование потоков с иной реализации виртуальной машины, которая называется Substrate VM. Substrate VM написана на Java, и ее код компилируется в нативный исполняемый файл. Получаемая в результате программа быстрее запускается и, соответственно, дает более низкие издержки в использовании памяти по сравнению с Java VM. Это отлично, но, вероятно, вы думаете: заблаговременная компиляция? Она же противоречит базовой идее, ради которой создавалась JVM, то есть, использованию единожды написанного кода везде! Это же безумие!!! Однако, подумайте сами: теперь у нас есть контейнеры, а им не нужна JVM. Обычные контейнерные приложения, создаваемые при помощи Spring boot, имеют лишний уровень абстрагирования, который совершенно не нужен в мире, где есть Kubernetes. У вас есть приложение Java, работающее на JVM внутри контейнера, этот контейнер остается неизменным, поскольку в наши дни готовый продукт – это контейнер, а не приложение. Теперь мы упаковываем контейнеры, а не WAR-файлы. Поэтому, все издержки, связанные с использованием приложения на JVM внутри контейнера становятся бесполезными, и AOT становится весьма логичным решением, если вы собираетесь упаковывать ваши приложения в контейнеры.

Правда, AOT-компиляция серьезно ограничивает динамические возможности Java (загрузка классов во время исполнения, рефлексия, прокси, т.д.). На практике это означает, что 90% экосистемы Java без изменений работать не будет. Соответственно, экосистема Java должна адаптироваться. Есть и хорошие новости: большинство из этого можно сделать во время сборки!

В этом и заключается сила Quarkus. Он использует GraalVM и предоставляет экосистему, поддерживающую AOT-компиляцию во время сборки; таким образом, при помощи Java можно создавать нативные двоичные файлы. Благодаря Quarkus, GraalVM поступает в распоряжение Java-разработчиков.

Приступаем к работе с Quarkus


Как было объяснено выше, Quarkus обеспечивает заблаговременную компиляцию для Java-приложений, и таким образом получается экосистема сверхзвуковой субатомной Java; Quarkus отличается сверхскоростной загрузкой – и Java возвращается в игру на поле облачно-ориентированной разработки. Меня годами так не воодушевляла какая-либо новая технология – и я в этом не одинок.

Почитайте руководство для начинающих – и убедитесь сами. По-прежнему есть множество компаний, использующих Java+JPA внутри контейнера, но в такой конфигурации загрузка может занимать 15 секунд, а в случае Quarkus – 0,005!



Статистика Quarkus

Вы используете ту же IDE и тот же инструментарий, к которым привыкли в мире Spring Boot. Для сборки вашего проекта используете Maven или Gradle. Проект можно запускать непосредственно в IDE и поверх нее, вам доступна горячая live-перезагрузка при любых изменениях, и перезапускать приложение при этом не требуется. Quarkus – это не Spring, поэтому, если вы используете Spring Boot, то придется выполнить миграцию Spring-специфичного кода. К счастью, в Quarkus предусмотрен уровень совместимости для внедрения зависимостей Spring, что сильно упрощает работу. Фреймворк Quarkus соответствует стандартам, что означает легкость в портировании и поддержке его кода.

Процесс разработки Quarkus


Quarkus может использоваться в режиме разработки, этим он напоминает Spring Boot. С ним вы также можете упаковывать ваш проект в толстые jar. Это очень удобно для тестирования и отладки вашего кода, поскольку поддерживается live-перезагрузка; но для выхода в продакшен вам потребуется заблаговременная компиляция. Весь этот процесс показан на следующей схеме:



  • Сначала собираете приложение в вашей любимой IDE, а затем можете запустить его в режиме разработчика при помощи: “mvnw compile quarkus:dev”, как поступили бы с приложением Spring Boot. Также можете упаковать его в толстый jar.
  • Как только закончите предыдущий этап, и результат вас устроит – вы готовы к созданию двоичного файла Java, просто запустите: “mvnw package -Pnative”. На это потребуется некоторое время, поскольку в ходе заблаговременной компиляции будет создаваться нативный код! Когда этот шаг будет завершен, у вас в распоряжении окажется сверхмалый и сверхбыстрый исполняемый файл, но работать он сможет лишь на вашей платформе/OS, то есть, он не портируется! Но и это нормально, поскольку мы можем поместить его в контейнер – и таким образом обеспечить портируемость. Вот как это делается: ./mvnw package -Pnative -Dnative-image.docker-build=true4 – и мы вынимаем исполняемый файл из контейнера Docker, то есть, выполняем нативную сборку внутри контейнера и создаем двоичный файл. Этот прием может не сработать у вас на ноутбуке, если его операционная система отличается от целевой платформы, указанной в файле DockerFile, генерируемом Quarkus в ходе создания проекта.
  • Затем, после того как у вас будет двоичный файл, просто создаем образ на основе файла docker. docker build -f src/main/docker/Dockerfile.native -t quarkus-quickstart/quickstart .
  • Наконец, приложение можно запустить в Docker или Kubernetes: docker run -i --rm -p 8080:8080 quarkus-quickstart/quickstart

Возможности Quarkus


В Quarkus гораздо больше возможностей, чем в нативном коде Java.

  • Унификация императивных и реактивных возможностей: позволяет комбинировать привычный императивный код с неблокирующим кодом, написанном в реактивном стиле.
  • Разработчику приятно: унифицированная конфигурация, Zero config, live-перезагрузка в мгновение ока, оптимизированный обтекаемый код для 80% распространенных случаев и гибкий код для оставшихся 20% случаев, генерация нативных исполняемых файлов без суеты, live-кодинг.
  • Поразительно быстрая загрузка, невероятно малая резидентная область памяти (да, речь не только о размере кучи!), что обеспечивает почти мгновенное вертикальное масштабирование и очень плотное использование памяти при оркестрации контейнеров на таких платформах как Kubernetes. См. подробнее.
  • Quarkus предлагает целостный, приятный в использовании full-stack фреймоврк, в несущие структуры которого внедрены первосортные библиотеки, которые вы знаете и любите. Подробнее.
  • Поддерживаются библиотеки Hibernate, JPA, REST, JWT, т.д.
  • Поддерживаются конфигурации, развернутые в Kubernetes и OpenShift
  • Открытая трассировка (open tracing) с использованием Jaeger
  • Поддержка Kotlin
  • Обмен сообщениями при помощи Kafka, Camel…
  • И многое другое, познакомьтесь со списком расширений!

Экосистема Quarkus


Короче говоря, теперь можно запускать традиционные транзакционные сервисы JPA/JTA в супербыстрых легковесных контейнерах – как в облаке, так и на территории предприятия.

Пример Quarkus


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



Простейший способ создать новый проект Quarkus – открыть окно командной строки и выполнить в нем следующую команду:

mvn io.quarkus:quarkus-maven-plugin:0.12.0:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=getting-started \
    -DclassName="org.acme.quickstart.GreetingResource" \
    -Dpath="/hello"

Таким образом генерируется проект Maven с GreetingResuce, предоставляющий конечную точку /hello. Также генерируются докерные образы Dockerfile для нативных файлов и jvm (традиционные образы в виде толстых jar). Код получается очень чистым и простым:

@Path("/hello")
public class GreetingResource {    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}

Для запуска приложения используйте: ./mvnw compile quarkus:dev
Приложение упаковывается при помощи ./mvnw package. В результате получается 2 jar-файла:

  • getting-started-1.0-SNAPSHOT.jar – содержит только классы и ресурсы проектов. Это обычный артефакт, получающийся в результате сборки Maven;
  • getting-started-1.0-SNAPSHOT-runner.jar – это исполняемый jar. Учтите, что это не «uber-jar», здесь есть и зависимости, они копируются в каталог target/lib.

Запустить приложение можно с помощью: java -jar target/getting-started-1.0-SNAPSHOT-runner.jar

Затем нужно скачать и установить GraalVM и задать переменную окружения GRAALVM_HOME.

Теперь можно создать нативный исполняемый файл при помощи: ./mvnw package -Pnative -Dnative-image.docker-build=true.

Вот как создается образ Docker: docker build -f src/main/docker/Dockerfile.native -t quarkus-quickstart/quickstart .

Теперь его можно запустить при помощи любого движка оркестрации контейнеров, в случае, если вы используете minishift:

kubectl run quarkus-quickstart --image=quarkus-quickstart/quickstart:latest --port=8080 --image-pull-policy=IfNotPresent

kubectl expose deployment quarkus-quickstart --type=NodePort

Вот и все!; теперь у вас есть контейнер с REST-сервисом на Java, запускающийся за 0.004 секунд!

Вывод


Теперь понятно, почему меня так восхищает фреймворк Quarkus, поддерживаемый Red Hat. Я действительно считаю, что он изменит технологический ландшафт Java и обеспечит большим традиционным предприятиям реальную возможность миграции в облако.

Kubernetes + Knative + Quarkus меняют правила игры в облачно-ориентированной разработке и порадуют любого Java-разработчика.

В этом репозитории – множество интересных примеров!
Издательский дом «Питер»
Компания

Комментарии 4

    +3
    Интересно… Если делают «модно-быстро-молодежно», почему maven везде, а не gradle…
      –1
      В маленьких проектах gradle иногда выглядит как оверхэд.
      +1
      немного не понял, поэтому спрошу:
      у нас есть сервис написанный на spring boot. Могу я его не изменяя код скомпилировать «под» «Quarkus»?
        +1
        особенно если этот сервис использует спринговую интеграцию с БД (или с пятью разными БД), с Kafka, и прочими RabbitMQ и LDAP. И при этом использует Spring Security, Spring Batch и реактивного черта в ступе?

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

      Самое читаемое