Ускоряем запуск Spring Boot-приложений в контейнере
Знаете ли вы, что Spring Boot-приложение в контейнере может запускаться за миллисекунды? При этом без ущерба для производительности, памяти, паритета окружений разработки-продакшена, без ограничений возможностей языка Java, и почти без изменения кода приложения. Но как? С помощью Liberty 23.0.0.10-beta…
Liberty InstantOn
Функциональность InstantOn в Open Liberty использует IBM Semeru JDK и технологию Linux Checkpoint/Restore in Userspace (CRIU), позволяющую во время выполнения делать снимки состояния процесса (снапшоты, контрольные точки). Впоследствии состояние очень быстро восстанавливается и работа возобновляется. Восстановить приложение можно несколько раз, поскольку Open Liberty и Semeru JDK сохраняют уникальность каждого восстановленного процесса в контейнере. После восстановления состояния процессу нет необходимости выполнять инициализацию при запуске, что экономит до 90% времени старта (зависит от вашего приложения). Использование InstantOn требует незначительных изменений вашего Java-приложения.
Подробнее о Liberty InstantOn см. в статье How to package your cloud-native Java application for rapid startup и в разделе Faster startup for containerized applications with Open Liberty InstantOn документации Open Liberty.
Поддержка checkpoint/restore в Spring Boot
В Spring Framework 6.1 будет реализована интеграция с JVM checkpoint/restore с помощью проекта org.crac, что позволит ускорить запуск приложений. В Liberty InstantOn версии 23.0.0.10-beta для получения реализации org.crac API нужно добавить функциональный модуль (feature) crac-1.3. Это позволит развертывать Spring-приложения, включая Spring Boot, с помощью Liberty InstantOn.
Production-ready образы контейнеров Liberty
Образы контейнеров для всех новых релизов Liberty создаются на основе Universal Base Image и загружаются в репозиторий IBM Container Registry. Образ Liberty UBI с версии Liberty 23.0.0.6 включает необходимые компоненты для создания контрольных точек приложения с помощью Liberty InstantOn. А с версии 23.0.0.10-beta — также и для Spring Boot 3.2.
Образы Liberty упрощают разработку InstantOn-приложений, готовых к развертыванию в продакшене. Важное преимущество Liberty InstantOn — возможность создавать контрольные точки процесса приложения внутри контейнера без необходимости запуска приложения в контейнере под root, что важно с точки зрения безопасности. Это также позволяет развертывать ваши InstantOn-образы в облачных сервисах Kubernetes, таких как AWS EKS и Azure EKS.
Пример приложения на Spring Boot 3.2.0 с использованием Liberty InstantOn
В этом примере используются материалы туториала Containerizing, packaging, and running a Spring Boot application. Самый простой способ начать работу — клонировать ветку cracSpringBoot из github-репозитория с примером:
git clone --branch cracSpringBoot https://github.com/openliberty/guide-spring-boot.git
cd guide-spring-boot/finish
В ветке cracSpringBoot
используются более новые версии Open Liberty и Spring Boot, поддерживающие org.crac
для Spring Boot-приложений. Понадобится Java 17 или более поздняя версия.
Для сборки приложения запустим mvnw
в папке finish
.
./mvnw package
Далее будем контейнеризовать наше приложение. Мы обратим внимание только на изменения в контейнеризации, связанные с Liberty InstantOn. Подробнее о лучших практиках контейнеризации Spring Boot-приложений с Open Liberty см. в руководстве Containerizing, packaging, and running a Spring Boot application.
Для создания образа приложения, использующего InstantOn, необходимо либо запустить привилегированный контейнер, либо предоставить инструменту сборки образов необходимые Linux capabilities, позволяющие создавать контрольные точки.
Включение сrac-1.3 в Liberty
В Liberty мы можем выбрать только необходимые нам компоненты. Для использования org.crac
в Liberty, необходимо в конфигурацию Liberty добавить crac-1.3
. В данном случае можно скопировать файл src/main/liberty/config/crac.xml
в образ контейнера с помощью следующей команды в Dockerfile:
COPY src/main/liberty/config/crac.xml /config/configDropins/defaults
Включаем crac-1.3 в файле crac.xml конфигурации Liberty:
<?xml version="1.0" encoding="UTF-8"?>
<server description="Enable the org.crac API">
<featureManager>
<feature>crac-1.3</feature>
</featureManager>
</server>
Сборка образа с помощью Podman
Podman позволяет при сборке образа указать Linux capabilities, необходимые для создания контрольной точки приложения, что позволит запустить checkpoint.sh с помощью команды RUN
в Dockerfile. Это последняя инструкция в Dockerfile.podman
:
...
RUN configure.sh
RUN checkpoint.sh afterAppStart
Для сборки образа используем следующую команду с указанием необходимых capabilities:
sudo podman build \
-f Dockerfile.podman \
-t springboot \
--cap-add=CHECKPOINT_RESTORE \
--cap-add=SYS_PTRACE\
--cap-add=SETPCAP \
--security-opt seccomp=unconfined .
Также вы можете запустить скрипт scripts/build-instanton-podman.sh
из примеров.
Последний этап сборки образа — запуск checkpoint.sh с параметром afterAppStart
. В результате контрольная точка создается после запуска приложения. О других вариантах создания контрольной точки смотрите в документации.
При запуске приложения вы увидите следующий результат:
[AUDIT ] CWWKZ0001I: Application thin-guide-spring-boot-0.1.0 started in 3.880 seconds.
[AUDIT ] CWWKC0451I: A server checkpoint "afterAppStart" was requested. When the checkpoint completes, the server stops.
2023-09-06T21:06:18.763Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Stopping Spring-managed lifecycle beans before JVM checkpoint
2023-09-06T21:06:18.767Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Stopping beans in phase 2147483647
2023-09-06T21:06:18.768Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Bean 'applicationTaskExecutor' completed its stop procedure
2023-09-06T21:06:18.769Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Stopping beans in phase 2147482623
2023-09-06T21:06:18.771Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Bean 'webServerGracefulShutdown' completed its stop procedure
2023-09-06T21:06:18.771Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Stopping beans in phase 2147481599
2023-09-06T21:06:18.796Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Bean 'webServerStartStop' completed its stop procedure
2023-09-06T21:06:18.796Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Stopping beans in phase -2147483647
2023-09-06T21:06:18.797Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Bean 'springBootLoggingLifecycle' completed its stop procedure
[2/2] COMMIT springboot
По логам видно, что приложение останавливалось для создания контрольной точки. Теперь у вас есть образ контейнера приложения под названием springboot
.
Сборка образа с помощью Docker
В настоящее время Docker не позволяет при сборке образа указать Linux capabilities
. По этой причине мы не можем запустить checkpoint.sh при выполнении docker build
. Вместо этого будем использовать трехэтапную сборку образа:
Соберем образ без слоя InstantOn.
Запустим из полученного образа контейнер и создадим контрольную точку приложения.
Сохраним контейнер с контрольной точкой в новый InstantOn-образ с помощью commit.
Выполните эти три этапа сборки, запустив скрипт scripts/build-instanton-docker.sh. Вывод при сборке через Docker похож на вывод Podman. В логе вы увидите сообщения от lifecycle-бинов.
Запуск приложения
И Podman, и Docker используют одинаковые параметры для запуска:
Файлы run-instanton-podman.sh и run-instanton-docker.sh:
[sudo podman or docker] run \
--rm \
-p 9080:9080 \
--cap-add=CHECKPOINT_RESTORE \
--cap-add=SETPCAP \
--security-opt seccomp=unconfined \
springboot
Для запуска можно использовать данную команду или один из скриптов: scripts/run-instanton-podman.sh
или scripts/run-instanton-docker.sh
.
При запуске вы увидите следующий вывод:
[AUDIT ] Launching defaultServer (Open Liberty 23.0.0.10-beta/wlp-1.0.81.cl230920230904-1158) on Eclipse OpenJ9 VM, version 17.0.7+7 (en_US)
2023-09-07T15:22:52.683Z INFO 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Restarting Spring-managed lifecycle beans after JVM restore
2023-09-07T15:22:52.684Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase -2147483647
2023-09-07T15:22:52.684Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Successfully started bean 'springBootLoggingLifecycle'
2023-09-07T15:22:52.685Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 2147481599
[AUDIT ] CWWKT0016I: Web application available (default_host): http://e93ebe585ce3:9080/
2023-09-07T15:22:52.759Z INFO 118 --- [ecutor-thread-1] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 106109 ms
2023-09-07T15:22:52.762Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Successfully started bean 'webServerStartStop'
2023-09-07T15:22:52.763Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 2147482623
2023-09-07T15:22:52.763Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Successfully started bean 'webServerGracefulShutdown'
2023-09-07T15:22:52.763Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 2147483647
2023-09-07T15:22:52.763Z DEBUG 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Successfully started bean 'applicationTaskExecutor'
2023-09-07T15:22:52.764Z INFO 118 --- [ecutor-thread-1] o.s.c.support.DefaultLifecycleProcessor : Spring-managed lifecycle restart completed in 80 ms
[AUDIT ] CWWKC0452I: The Liberty server process resumed operation from a checkpoint in 0.263 seconds.
[AUDIT ] CWWKZ0001I: Application thin-guide-spring-boot-0.1.0 started in 0.265 seconds.
[AUDIT ] CWWKF0012I: The server installed the following features: [crac-1.3, expressionLanguage-5.0, pages-3.1, servlet-6.0, springBoot-3.0, ssl-1.0, transportSecurity-1.0, websocket-2.1].
[AUDIT ] CWWKF0011I: The defaultServer server is ready to run a smarter planet. The defaultServer server started in 0.277 seconds.
Обратите внимание на последнее сообщение "... server started in 0.277 seconds
". Время запуска 0,277 секунды включает в себя как время, необходимое criu
для восстановления Java-процесса, так и время, необходимое Liberty для правильного восстановления runtime-состояния для безопасного запуска приложения после восстановления. В результате мы получили более чем десятикратное ускорение запуска по сравнению с исходными 5,5+ сек. без использования InstantOn.
Резюме
Open Liberty InstantOn представляет собой единую среду выполнения для развертывания облачных приложений с быстрым запуском. Liberty InstantOn можно использовать как с приложениями, использующими открытые стандарты, такими как Jakarta EE и MicroProfile, так и с приложениями на Spring, использующими последние версии Spring Boot и Spring Framework, в которых реализована поддержка org.crac
.
У Open Liberty есть следующие преимущества:
Доступ к полноценной платформе Java SE без каких-либо компромиссов. Для быстрого запуска нет необходимости изменять код приложения под окружение, как, например, при нативной компиляции.
Оптимизированный Liberty runtime обеспечивает высокую производительность и использует меньше памяти по сравнению с другими средами выполнения.
Расширенные возможности JIT-компиляции при развертывании в облаке (Semeru Cloud Compiler).
Запуск в среде выполнения, содержащей только необходимые компоненты, что позволяет уменьшить размер образа контейнера.
Использование production-ready образов Open Liberty позволяет придерживаться лучших практик по оптимизации и безопасности контейнеров, таких как отсутствие запуска приложения в контейнере от root и отказ от привилегированных контейнеров.
Образы InstantOn-приложений готовы к развертыванию в публичных облаках, таких как AWS EKS и Azure AKS. Быстрое время запуска, обеспечиваемое Liberty InstantOn, делает его идеальной платформой для создания бессерверных приложений, в том числе основанных на SpringBoot.
В заключение приглашаем всех желающих на открытое занятие «Разработка REST-клиентов на Spring», которое пройдет 14 ноября. Записаться можно на странице курса по Spring.