Привет, Хабр!
В наступившем новом году мы планируем всерьез развивать темы контейнеров, 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. Сейчас нам нужен код, способный запуститься, выполнить задачу, а затем остановиться. Мы не хотим иметь дел со множеством потоков или долгоиграющими процессами, нам нужны короткоживущие процессы, которые могут загружаться очень быстро.
Если вы читаете технические блоги или следите за новостями, то, возможно, полагаете, что бессерверная парадигма захватывает мир, и все воспринимают ее с крайним энтузиазмом. Сейчас стартап может писать функции и предоставлять их в облаке как услугу – благодаря использованию 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 обеспечивает заблаговременную компиляцию для 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 может использоваться в режиме разработки, этим он напоминает Spring Boot. С ним вы также можете упаковывать ваш проект в толстые jar. Это очень удобно для тестирования и отладки вашего кода, поскольку поддерживается live-перезагрузка; но для выхода в продакшен вам потребуется заблаговременная компиляция. Весь этот процесс показан на следующей схеме:
В Quarkus гораздо больше возможностей, чем в нативном коде Java.
Короче говоря, теперь можно запускать традиционные транзакционные сервисы JPA/JTA в супербыстрых легковесных контейнерах – как в облаке, так и на территории предприятия.
В этом разделе давайте в упрощенном виде рассмотрим руководство для начинающих, чтобы составить впечатление о мощи Quarkus.
Простейший способ создать новый проект Quarkus – открыть окно командной строки и выполнить в нем следующую команду:
Таким образом генерируется проект Maven с GreetingResuce, предоставляющий конечную точку /hello. Также генерируются докерные образы Dockerfile для нативных файлов и jvm (традиционные образы в виде толстых jar). Код получается очень чистым и простым:
Для запуска приложения используйте:
Приложение упаковывается при помощи ./mvnw package. В результате получается 2 jar-файла:
Запустить приложение можно с помощью: java -jar target/getting-started-1.0-SNAPSHOT-runner.jar
Затем нужно скачать и установить GraalVM и задать переменную окружения
Теперь можно создать нативный исполняемый файл при помощи:
Вот как создается образ Docker:
Теперь его можно запустить при помощи любого движка оркестрации контейнеров, в случае, если вы используете minishift:
Вот и все!; теперь у вас есть контейнер с REST-сервисом на Java, запускающийся за 0.004 секунд!
Теперь понятно, почему меня так восхищает фреймворк Quarkus, поддерживаемый Red Hat. Я действительно считаю, что он изменит технологический ландшафт Java и обеспечит большим традиционным предприятиям реальную возможность миграции в облако.
Kubernetes + Knative + Quarkus меняют правила игры в облачно-ориентированной разработке и порадуют любого Java-разработчика.
В этом репозитории – множество интересных примеров!
В наступившем новом году мы планируем всерьез развивать темы контейнеров, 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=true
4 – и мы вынимаем исполняемый файл из контейнера 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-разработчика.
В этом репозитории – множество интересных примеров!