Как создать native image с помощью GraalVM

Про GraalVM я уже рассказывал в предыдущей статье. Из набора технологий GraalVM интересным была технология предварительной компиляции Ahead-of-Time compilation (AOT compilation), которая была на время экспериментально добавлена в обычный Java JDK. В Java 17 эта функция была выпилена. Ходят слухи, что в Java 21 она снова появится.

В GraalVM функция предкомпиляции позволяет так называемые нативные приложения (native images). То есть все ваше Java-приложение вместе со всеми потрохами Java SDK заворачивается в исполняемый файл и этот файл можно запускать, на любой другой машине даже если там нет Java JDK. Разумеется, exe-файл получается внушительный, но 1) GraalVM весьма умно при компиляции понимает, что надо в него заворачивать, а что не понадобится 2) когда это нас стали пугать exe файлы размером в 11 мегабайт?

Давно хотел поиграться с этой функцией, чтобы хотя бы в общих чертах понять сам процесс. Я решил попробовать собрать простейшее тестовое Spring-приложение в native-image не меняя в нем ничего, чтобы разобраться, какие танцы с бубном понадобятся, чтобы все заработало само собой просто из коробки.

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

Что нам понадобится

  • сам GraalVM SDK для вашей операционной системы, который можно скачать с официального сайта
  • Microsoft Visual Studio. Так как мы планируем создавать нативное приложение под Windows, нам нужен весь tool-chain для этого. А он доступен бесплатно в составе Microsoft Visual Studio.
  • Spring Boot CLI, если вы предпочитаете быстренько создать простое Spring-приложение
  • Micronaut, если есть желание поиграться с новеньким конкурентом Spring
  • Maven, если вы предпочитаете работать с ним а не с Gradle

Доступ ко всем утилитам должен присутствовать в PATH, а путь к папке bin там, где установлен GraalVM, должен быть прописан в JAVA_HOME. Для удобства я создал переменную GRAALVM. В окне компиляции я даю команду

set JAVA_HOME=%GRAALVM% 

и для данной сессии в данном окне путь JAVA_HOME указывает на папку, где установлен GraalVM. Это очень удобно, так как я не хочу менять JAVA_HOME для всех приложений (он у меня выставлен на Java JDK 20), если мне нужно только поиграться с GraalVM.

GraalVM разные!

Дистрибутивы GraalVM представлены в двух редакциях: CE (Community Edition) и EE (Enterprise Edition). CE (Community Edition) распространяется свободно и бесплатно, но набор функционала там меньше. EE (Enterprise Edition) распространяется по лицензии, но для любительских игр эта лицензия — чистая формальность.

Обращаю ваше внимание, что и внутри каждой редакций есть отличия. Дистрибутив под Windows не имеет поддержи некоторых языков (python, R), они есть только в Linux-версии. То есть поиграться с многоязычными (polyglot) приложениями на GraalVM можно на любой ОС, если это не Windows.

Добавляем native-image компонент к GraalVM

Сам GraalVM SDK еще не содержит всех компонентов, которые нам нужны. Для их установки воспользуемся утилитой gu, что идет в комплекте GraalVM SDK. Команда

gu list

покажем нам список установленных компонентов. Команда

gu available

покажет нам список компонентов, которые мы можем установить. Среди них есть компонент под названием native-image. Чтобы его установить, надо набрать команду

gu install native-image

Утилита gu загрузит компонент из интернета и установит его в вашем GraalVM SDK.

Создаем native image из Spring-приложения

В командной строке в отдельной папочке пишем:

spring init -d=web demo --build maven

В результате spring-boot cli создаст простейшее веб-приложение со встроенным веб-сервером и сконфигурирует его для работы с Maven. Если параметр —build пропустить, проект по умолчанию будет создан под работу с Gradle.

Проверим как проект компилируется. В новой папке demo, где уже есть pom.xml файл, пишем:

mvn clean install

Если все настроено верно, мы в результате в под-папке target получим толстый JAR-файл, который можно запустить так:

java -jar demo.jar

Наше веб-приложение запустится и будет ждать запросов на порту :8080. Это порт у меня на рабочей машине занят, поэтому единственное изменение, которое я сделал в данном простейшем тестовом приложении, это поменял порт в src/main/resrources/application.properties:

server.port=8888

После пересборки и запуска веб-приложение стало ждать запросы на порту :8888. Закроем приложение и создадим из него native image, т.е. exe-файл.

Для этого нам надо открыть специальное командное окно. Когда вы устанавливали Microsoft Visual Studio, вызов этого окна прописался в вашем стартовом меню. Называется это окно «x64 Native Tools Command Prompt for VS 2022«:

Это все та же командная cmd-строка, но сконфигурированная так, что из нее видны все утилиты Windows SDK tool-chain для компиляции Windows-приложений. Запомните это! Из обычного cmd-окна ничего не заработает: будут вылетать ошибки, что не найдены библиотеки и файлы!

Переходим в этом окне в нашу папку, где находится Spring-проект. В окне даем команду, о которой я упоминал выше, чтобы переключиться на GraalVM SDK:

set JAVA_HOME=%GRAALVM%

После этого набираем:

mvn -Pnative native:compile

Если все было сконфигурировано верно, запустится процесс сборки JAR-файла, а затем процесс его преобразования в exe. Весь процесс занял примерно 2 минуты на моей не очень мощной машинке. В результате в папке target вы найдете файл demo.exe, который можно запустить и на экране вы увидите все те же строки Spring-приложения, только на этот раз ваше веб-приложение запустится в 100 раз быстрее — 99 миллисекунд вместо 2.5 секунды для JAR-файла! И это естественно, ведь это реальный машинный код!

Создаем native image из Micronaut-приложения

Порядок действий почти такой же. В отдельной папке создаем простейшее Micronaut-приложение:

mn create-app demo —build maven

В новой под-папке demo, где уже лежит pom.xml, набираем:

mvn clean install

и потом запускаем:

mvn mn:run

Приложение опять таки будет ждать запросов на порту :8080. Чтобы поменять порт, пропишите в файле src/main/resources/application.yml:

micronaut:
   server:
      port: 8888 

Для того чтобы собрать native-image этого простейшего приложения надо опять открыть окно «x64 Native Tools Command Prompt for VS 2022«. Перейти в нем в папку проекта. Дать команду:

set JAVA_HOME=%GRAALVM%

а потом запустить сборку:

mvn package -Dpackaging=native-image

В результате вы опять получите exe-файл demo.exe, который можно запустить как обычное Windows-приложение.

А на Linux?

На Linux native-image собирается чуть проще. Эксперимент я проводил с помощью Vagrant (и VirtualBox). Файл проекта можете найти в моем репозитории. На Linux вам вместо Microsoft Visual Studio понадобится gcc (Vagrant-скрипт установит его автоматически), а в остальном — все то же самое.

Выводы

Надеюсь, эта статья вам поможет сделать первые шаги в исследовании возможностей GraalVM и его функции предварительной компиляции.

Для чего это все может понадобиться? Native image функционал может понадобиться для создания микросервисов, которые размещаются в облаке в кластерах Kubernetes, где не имеет смысла выкатывать целый паровоз JDK только для того, чтобы запустить маленький микросервис. При использовании native-image экономится дисковое пространство, память, ускоряется запуск и работа самого микросервиса. Разумеется, есть нюансы. Но сама технология — очень интересна, а ее разработчики достойны всяческих похвал!

Реклама

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s