упаковка java приложения в docker

Упаковка jvm приложения в docker образ

упаковка java приложения в docker. a1d6abe0e88d459cafac028f9c38a7a9. упаковка java приложения в docker фото. упаковка java приложения в docker-a1d6abe0e88d459cafac028f9c38a7a9. картинка упаковка java приложения в docker. картинка a1d6abe0e88d459cafac028f9c38a7a9.

Мы же решим практическую задачу по упаковке jvm приложения и получим контейнер с миниатюрным Linux, JDK и нашим приложением, который опубликуем на hub.docker.com и сможем запускать где угодно.

Исходное приложение

Если ваше приложение для jvm состоит из более чем одного файла и образ нужно генерировать регулярно/автоматически, то можно интегрировать процесс сборки в maven с помощью плагина com.spotify:docker-maven-plugin. Если же вы используете Gradle, то se.transmode.gradle:gradle-docker вам поможет сделать сборку docker образа. Пример для maven опубликовал на github.

В публикации используем jar из центрального maven репозитария. Это своя сборка груви, которая примечательна упакованным aether provide для разрешения зависимостей Grape и расширенной поддержкой протоколов для java.net.URL. Для пытливых умов:

Для меня это удобный инструмент, который часто использую. Как пример — загрузка jvm агента в запущенную JVM, инсталятор для crate.io, пример парсера сайта…

Теперь groovy-grape-aether доступен и в docker образе suhorukov/docker-groovy. В подобный образ вы может упаковать ваше приложение для jvm.

Dockerfile

Описывает на основе какого существующего образа и с помощью каких команд сформировать новый образ.

Воспользуемся образом frolvlad/alpine-oraclejdk8, с благодарностью frol за его труд! Этот образ на основе Alpine Linux image + glibc и Oracle JDK 8u102.14. Достаточно компактный, по сравнению с образами на основе на debian/ubuntu. Как альтернатива возможен популярный образ anapsix/alpine-java.

ENTRYPOINT определяет какая команда с параметрами будет запущена при старте контейнера. Для команды возможно указать с помощью CMD параметры по-умолчанию.

Собираем образ локально

Запускаем в той же директории, где находится наш Dockerfile команду

Публикация в hub.docker.com

Этот раздел можете пропустить, если у вас не opensource проект и вам не нужно делиться образом со всем миром.

Публикуем Dockerfile на github

В своей учетной записи на github выбираем New Repository и помещаем туда подготовленный Dockerfile. В этом примере используется репозитарий docker-groovy.

Настраиваем сборку в hub.docker.com

Создаем бесплатный аккаунт на главной странице hub.docker, заполнив форму и нажав «Sign Up». Обязательно активируем его с помощью письма в email.

В настройках аккаунта заходим во вкладку «Linked Accounts & Services» и настраиваем «Link Github» на ваш github аккаунт( другой вариант Bitbucket аккаунт).

В меню вверху страници выбираем Create->Create Automated Build, затем жмем«Create Auto-build Github», выбираем репозитарий и указываем где в репозитарии Dockerfile. При следующем push в репозитарий на github сборка автоматичеки запустится. Можно запустить и сборку вручную.

В результате всех вышеописанных действий получился такой проект на hub.docker.com.

Загрузка и использование контейнера

Перед использованием, получим образ из hub.docker.

И запустим контейнер с параметром-скриптом gitblit.groovy.

В случае, если для приложения нужен доступ к внешним для контейнера ресурсам в файловой системе (директории хост-машины, NFS, распределенной файловой системы) то нужно указать точки монтирования при создании образа в секции VOLUME в Dockerfile. Если же нужно распределенное выполнение и орекстрация контейнеров — Kubernetes / Mesos / Swarm / fabric8.io / Rancher более подходящие для этого технологии которые работают с docker.

Источник

Как работать с Docker: упаковка Spring Boot приложения в контейнер

Немного разбираемся с теорией и проверяем на практике.

упаковка java приложения в docker. 720a5b2ec12ff21ce19af43f9722994f. упаковка java приложения в docker фото. упаковка java приложения в docker-720a5b2ec12ff21ce19af43f9722994f. картинка упаковка java приложения в docker. картинка 720a5b2ec12ff21ce19af43f9722994f.

упаковка java приложения в docker. b0bfcbbb4124b188a05792d1e0878a78. упаковка java приложения в docker фото. упаковка java приложения в docker-b0bfcbbb4124b188a05792d1e0878a78. картинка упаковка java приложения в docker. картинка b0bfcbbb4124b188a05792d1e0878a78.

Эта статья посвящена основам Docker и раскрывает азы работы с контейнерами. Мы изучим базовые определения и самые необходимые команды и даже разработаем и развернём простейшее Java-приложение.

Что такое Docker

Docker — инструмент, предназначенный для быстрой разработки, доставки и развёртывания приложений. Он позволяет упаковать приложение вместе со всеми его зависимостями в так называемый контейнер, а затем запустить его в любой среде.

Идея контейнеризации состоит в том, что на одной машине может разворачиваться множество таких контейнеров с приложениями. Для каждого из них в операционной системе выделяется изолированная область — осуществляется виртуализация на уровне ОС.

Важный момент: все контейнеры запускаются одинаковым способом вне зависимости от того, что находится внутри. Это напоминает контейнеры для морских перевозок — с виду они одинаковы, но внутри могут храниться совершенно разные грузы.

упаковка java приложения в docker. 22002909122020 ddeaa1241fdda660b487287475d3cbbd726bc5fa. упаковка java приложения в docker фото. упаковка java приложения в docker-22002909122020 ddeaa1241fdda660b487287475d3cbbd726bc5fa. картинка упаковка java приложения в docker. картинка 22002909122020 ddeaa1241fdda660b487287475d3cbbd726bc5fa.

Разрабатывает приложения на Java, воспитывает двух котов: Котлин и Монго.

Основные понятия

Образ — некий шаблон, на основе которого создаются контейнеры. Содержит всё необходимое для запуска приложения. Сюда относятся код, системные утилиты, библиотеки, настройки и так далее. Образ можно представить в виде набора слоёв, которые накладываются друг на друга. Каждый последующий добавляет, изменяет или удаляет файлы предыдущего слоя.

DockerfIle — текстовый файл с набором инструкций по созданию образа, каждая из которых добавляет к образу новый слой.

Контейнер — конкретный экземпляр приложения, созданный на основе образа. Причём из одного образа можно создать сколько угодно контейнеров. Технически контейнер создаётся путём добавления к образу нового слоя, содержащего результаты работы приложения.

Реестр — хранилище образов (как GitHub для кода приложений). Образы можно скачивать из реестра и создавать на их основе контейнеры. Также в реестр можно загружать новые или изменённые образы для дальнейшего использования.

Пример использования

Давайте разработаем простое Spring Boot приложение, создадим на его основе образ и развернём контейнер на локальной машине. Это делается в три простых шага:

Источник

Создание оптимизированных образов Docker для приложения Spring Boot

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

В этой статье рассматриваются различные способы контейнеризации приложения Spring Boot:

создание образа Docker с помощью файла Docker,

создание образа OCI из исходного кода с помощью Cloud-Native Buildpack,

и оптимизация образа во время выполнения путем разделения частей JAR на разные уровни с помощью многоуровневых инструментов.

Пример кода

Терминология контейнеров

Мы начнем с терминологии контейнеров, используемой в статье:

Образ контейнера (Container image): файл определенного формата. Мы конвертируем наше приложение в образ контейнера, запустив инструмент сборки.

Контейнер: исполняемый экземпляр образа контейнера.

Движок контейнера (Container engine): процесс-демон, отвечающий за запуск контейнера.

Хост контейнера (Container host): хост-компьютер, на котором работает механизм контейнера.

Реестр контейнеров (Container registry): общее расположение, используемое для публикации и распространения образа контейнера.

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

Версия 2.3 Spring Boot предоставляет плагины для создания образов OCI.

Построение образа контейнера традиционным способом

Создавать образы Docker для приложений Spring Boot очень легко, добавив несколько инструкций в файл Docker.

Сначала мы создаем исполняемый файл JAR и, как часть инструкций файла Docker, копируем исполняемый файл JAR поверх базового образа JRE после применения необходимых настроек.

Создание файла Docker

Затем мы помещаем это приложение в контейнер, добавляя Dockerfile :

Сборка приложения

Сначала нужно создать приложение с помощью Maven или Gradle. Здесь мы используем Maven:

Это создает исполняемый JAR-файл приложения. Нам нужно преобразовать этот исполняемый JAR в образ Docker для работы в движке Docker.

Создание образа контейнера

Затем мы помещаем этот исполняемый файл JAR в образ Docker, выполнив команду docker build из корневого каталога проекта, содержащего файл Docker, созданный ранее:

Мы можем увидеть наш образ в списке с помощью команды:

Просмотр слоев внутри образа контейнера

Давайте посмотрим на стопку слоев внутри образа. Мы будем использовать инструмент dive, чтобы просмотреть эти слои:

Вот часть результатов выполнения команды Dive:

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

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

Создание образа контейнера с помощью Buildpack

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

Преимущество облачных сборочных пакетов

Одним из основных преимуществ использования Buildpack для создания образов является то, что изменениями конфигурации образа можно управлять централизованно (builder) и распространять на все приложения, использующие builder.

Сборочные пакеты были тесно связаны с платформой. Cloud-Native Buildpacks обеспечивают стандартизацию между платформами, поддерживая формат образа OCI, который гарантирует, что образ может запускаться движком Docker.

Использование плагина Spring Boot

Плагин Spring Boot создает образы OCI из исходного кода с помощью Buildpack. Образы создаются с использованием bootBuildImage задачи (Gradle) или spring-boot:build-image цели (Maven) и локальной установки Docker.

Мы можем настроить имя образа, необходимого для отправки в реестр Docker, указав имя в image tag :

Давайте воспользуемся Maven для выполнения build-image цели по созданию приложения и созданию образа контейнера. Сейчас мы не используем никаких файлов Docker.

Результат будет примерно таким:

Из выходных данных мы видим, что paketo Cloud-Native buildpack используется для создания работающего образа OCI. Как и раньше, мы можем увидеть образ, указанный как образ Docker, выполнив команду:

Создание образа контейнера с помощью Jib

Настраиваем jib-maven-plugin в pom.xml:

Далее мы запускаем плагин Jib с помощью команды Maven, чтобы построить приложение и создать образ контейнера. Как и раньше, здесь мы не используем никаких файлов Docker:

После выполнения указанной выше команды Maven мы получаем следующий вывод:

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

Мотивации и методы создания оптимизированных образов

У нас есть две основные причины для оптимизации:

Производительность: в системе оркестровки контейнеров образ контейнера извлекается из реестра образов на хост, на котором запущен механизм контейнера. Этот процесс называется планированием. Извлечение образов большого размера из реестра приводит к длительному времени планирования в системах оркестровки контейнеров и длительному времени сборки в конвейерах CI.

Безопасность: образа большого размера также имеют большую область для уязвимостей.

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

Spring Boot использует «толстый JAR» в качестве формата упаковки по умолчанию. Когда мы просматриваем толстый JAR, мы видим, что приложение составляет очень маленькую часть всего JAR. Это часть, которая меняется чаще всего. Оставшаяся часть состоит из зависимостей Spring Framework.

Формула оптимизации сосредоточена вокруг изоляции приложения на отдельном уровне от зависимостей Spring Framework.

Слой зависимостей, формирующий основную часть толстого JAR-файла, загружается только один раз и кэшируется в хост-системе.

Только тонкий слой приложения вытягивается во время обновлений приложения и планирования контейнеров, как показано на этой диаграмме:

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

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

Создание оптимизированного образа контейнера для приложения Spring Boot с помощью Buildpack

Spring Boot 2.3 поддерживает многоуровневость путем извлечения частей толстого JAR-файла в отдельные слои. Функция наслоения по умолчанию отключена, и ее необходимо явно включить с помощью плагина Spring Boot Maven:

Мы будем использовать эту конфигурацию для создания нашего образа контейнера сначала с помощью Buildpack, а затем с помощью Docker в следующих разделах.

Давайте запустим build-image цель Maven для создания образа контейнера:

Если мы запустим Dive, чтобы увидеть слои в результирующем образе, мы увидим, что уровень приложения (обведен красным) намного меньше в диапазоне килобайт по сравнению с тем, что мы получили с использованием толстого формата JAR:

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

Создание оптимизированного образа контейнера для приложения Spring Boot с помощью Docker

Вместо использования плагина Maven или Gradle мы также можем создать многоуровневый образ JAR Docker с файлом Docker.

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

Содержимое полученного JAR после сборки с помощью Maven с включенной функцией наслоения будет выглядеть следующим образом:

В выходных данных отображается дополнительный JAR с именем spring-boot-jarmode-layertools и layersfle.idx файл. Этот дополнительный JAR-файл предоставляет возможность многоуровневой обработки, как описано в следующем разделе.

Извлечение зависимостей на отдельных слоях

Выполнение этой команды дает вывод, содержащий доступные параметры команды:

Мы видим список зависимостей, которые можно добавить как слои.

любая зависимость, версия которой не содержит SNAPSHOT

Источник

Java и Docker: это должен знать каждый

Постановка проблемы

В демонстрационных целях я создал демон docker в виртуальной машине с 1 Гб ОЗУ, используя такую команду:

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

Похожий результат получается даже в кластере Kubernetes / OpenShift. Я запустил группу контейнеров Kubernetes с ограничением памяти, используя такую команду:

При этом кластеру было назначено 15 Гб памяти. В итоге общий объём памяти, о котором сообщила система, составил 14 Гб.

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

Исследование кластера с 15 Гб памяти

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

Для того, чтобы воспроизвести ситуацию, в которой система останавливает процесс после превышения заданного лимита памяти, можно запустить WildFly Application Server в контейнере с ограничением памяти в 50 Мб, воспользовавшись такой командой:

Теперь, в процессе работы контейнера, можно выполнить команду docker stats для того, чтобы проверить ограничения.

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

Данные о контейнере

Через несколько секунд исполнение контейнера WildFly будет прервано, появится сообщение:

Выполним такую команду:

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

Анализ причины остановки контейнера

Влияние неверной работы с памятью на Java-приложения

Попробуем это сделать:

Конечная точка ответит примерно следующим образом:

Всё это может навести нас, по меньшей мере, на два вопроса:

Увеличение объёма памяти как пример неверного решения проблемы

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

Предположим, мы предоставили демону не 1 Гб памяти, а 8 Гб. Для его создания подойдёт такая команда:

Следуя той же идее, ослабим ограничение контейнера, дав ему не 150, а 800 Мб памяти:

Обратите внимание на то, что команда curl http://`docker-machine ip docker8192`:8080/api/memory в таких условиях даже не сможет выполниться, так как вычисленный параметр MaxHeapSize для JVM в окружении с 8 Гб памяти будет равен 2092957696 байт (примерно 2 Гб). Проверить это можно такой командой:

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

Проверка параметра MaxHeapSize

Приложение попытается выделить более 1.6 Гб памяти, что больше, чем лимит контейнера (800 Мб RAM и столько же в swap-файле), в результате процесс будет остановлен.

Верное решение проблемы

Небольшое изменение в Dockerfile позволяет нам задавать переменную окружения, которая определяет дополнительные параметры для JVM. Взгляните на следующую строку:

Теперь можно использовать переменную окружения JAVA_OPTIONS для того, чтобы сообщать системе о размере кучи JVM. Этому приложению, похоже, хватит 300 Мб. Позже можно взглянуть в логи и найти там значение 314572800 байт (300 МиБ).

В Kubernetes переменную среды можно задать, воспользовавшись ключом –env=упаковка java приложения в docker :

Улучшаем верное решение проблемы

Что если размер кучи можно было бы рассчитать автоматически, основываясь на ограничениях контейнера?

Это вполне достижимо, если использовать базовый образ Docker, подготовленный сообществом Fabric8. Образ fabric8/java-jboss-openjdk8-jdk задействует скрипт, который выясняет ограничения контейнера и использует 50% доступной памяти как верхнюю границу. Обратите внимание на то, что вместо 50% можно использовать другое значение. Кроме того, этот образ позволяет включать и отключать отладку, диагностику, и многое другое. Взглянем на то, как выглядит Dockerfile для приложения Spring Boot:

Теперь всё будет работать так, как нужно. Независимо от ограничений памяти контейнера, наше Java-приложение всегда будет настраивать размер кучи в соответствии с параметрами контейнера, не основываясь на параметрах демона.

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

упаковка java приложения в docker. image loader. упаковка java приложения в docker фото. упаковка java приложения в docker-image loader. картинка упаковка java приложения в docker. картинка image loader.

Использование разработок Fabric8

Итоги

JVM до сих пор не имеет средств, позволяющих определить, что она выполняется в контейнеризированной среде и учесть ограничения некоторых ресурсов, таких, как память и процессор. Поэтому нельзя позволять механизму JVM ergonomics самостоятельно задавать максимальный размер кучи.

Один из способов решения этой проблемы — использование образа Fabric8 Base, который позволяет системе, основываясь на параметрах контейнера, настраивать размер кучи автоматически. Этот параметр можно задать и самостоятельно, но автоматизированный подход удобнее.

В JDK9 включена экспериментальная поддержка JVM ограничений памяти cgroups в контейнерах (в Docker, например). Тут можно найти подробности.

Надо отметить, что здесь мы говорили о JVM и об особенностях использования памяти. Процессор — это отдельная тема, вполне возможно, мы ещё её обсудим.

Уважаемые читатели! Сталкивались ли вы с проблемами при работе с Java-приложениями в контейнерах Linux? Если сталкивались, расскажите пожалуйста о том, как вы с ними справлялись.

Источник

Альтернативная версия упаковки Java приложения в Docker

Прочитав статью об упаковке JVM приложения в Docker, я остался в смятении. Я, как и любой разработчик, хочу чтоб мои волосы развевались на ветру контейнер собирался за одну команду, сразу, без лишних телодвижений. Чтобы конфигурация проекта и сборки была собрана в одном файле, и чтобы не было зависимостей от внешних систем.

упаковка java приложения в docker. 8437c1b7455d6b1088c518315a0585c0. упаковка java приложения в docker фото. упаковка java приложения в docker-8437c1b7455d6b1088c518315a0585c0. картинка упаковка java приложения в docker. картинка 8437c1b7455d6b1088c518315a0585c0.

Далее последует альтернативный способ сборки Java-приложений в Docker контейнер используя Maven.

Для сборки нам понадобится Java проект, Maven и немного терпения, чтоб все это завести.

В это раз, мы будем использовать Maven-плагин от fabric8io.

Предположим, что у нас микро-сервис, который упаковывается в jar-файл, содержащий main метод, который и надо запустить для старта приложения. Добавляем плагин в build секцию:

И, в общем то, все. Разберем поподробнее, что у нас там написано.

В начале идёт типичное определение плагина с версией. Далее идёт определение в какие фазы луны сборки мы хотим собирать и пушить наш контейнер. В моем случае сборка контейнера (build) будет происходить во время упаковки проекта (phase: package).

Пуш (push) контейнера в репозиторий будет происходить во время фазы деплой (deploy).

Maven имеет скотскую привычку запускать различные плагины одной фазы в прядке их упоминания в effective pom. Построить effective pom можно следующей командой:
Если Вам никакими ухищрениями не удается получить правильный порядок, то перенесите построение контейнера в следующую фазу: install, deploy.
Например в моем случае перенесение в фазу deploy выглядит так:

Определяем имя образа и репозиторий:

Если репозиторий не указан, то будет использоваться репозиторий Docker-а. Документация для дальнейшего чтения.

from — базовый образ
tags — теги с которыми будет собран образ
ports — открытые порты
cmd — строка запуска, так же можно указать entryPoint, о все доступных опциях можно почитать в документации.

Это типичная строка запуска Java приложения, завернутая в shell форму Docker-а, в представлении Maven-а.

assembly — самая интересная часть плагина. Она определяет, что будет упаковано в образ контейнера и как. Есть несколько вариантов как можно описать упаковку, я выбрал inline, но есть и другие варианты.

В примере, я хочу взять все зависимости проекта и сложить в папку «jars»:

А так же, артефакт проекта, собственно наше приложение, туда же, а конфигурационный файл в корень контейнера.

Подробнее о возможностях ассемблирования в документации, на этот раз Maven-а.

И все! В результате:

» Сам плагин может ещё много чего: возможности
» Пример проекта выложен на github-е

Источник

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *