Heroku + Docker + Spring Boot

  • Tutorial
Далее речь пойдет о моем опыте запуска докеризованного Spring Boot приложения на бесплатных виртуальных машинах облачного сервиса Heroku. Одно из главных преимуществ этого провайдера в том, что он дает возможность создавать бесплатные виртуалки с ограничением по часам работы, причем для этого достаточно только регистрации. Даже платежные реквизиты подтверждать не требуется, хотя если их подтвердить можно получить дополнительные бонусы. Подробнее про их прайс можно прочитать здесь. С моей точки зрения их политика по части бесплатных ресурсов почти не имеет аналогов.

И так, после того, как вы создали на Heroku приложение есть несколько способов развернуть в нем ваш код

  • commit в heroku git репозиторий
  • привязка приложения к репозиторию на github
  • использование docker контейнера

Далее будем рассматривать последний из перечисленных способов. Чтобы продолжить, нам понадобится следующие приложения


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

Начинаем с того, что создаем Spring Boot приложение через Spring Initializr. В качестве зависимостей добавляем Spring Web Starter. Его должно быть достаточно.

В тот же пакет в котором находится основной класс приложения добавляем простейший класс REST контроллера, чтобы приложение как-то подавало признаки жизни.

@RestController
public class DemoController {
      
      @GetMapping("/check")
      public String check() {
           return "Application is alive";
      }
}

В файл application.properties добавляем

server.port=${PORT:8080}

Эта настройка является исключительно важной для запуска контейнера на Heroku. Дело в том, что во внутренней сети сервиса приложение запускается на некотором свободном на момент запуска порту, номер которого передается через переменную окружения PORT. Кроме того, приложение должно успеть подключиться к этому порту в течении первых 60 секунд после запуска, иначе оно будет остановлено.

В раздел plugins файл pom.xml добавляем dockerfile-plugin от Spotify, который поможет нам со сборкой docker-образа нашего приложения.

<plugin>
  <groupId>com.spotify</groupId>
  <artifactId>dockerfile-maven-plugin</artifactId>
  <version>1.4.6</version>
  <executions>
    <execution>
       <id>default</id>
       <goals>
         <goal>build</goal>
         <goal>push</goal>
       </goals>
    </execution>
  </executions>
  <configuration>
     <repository>registry.heroku.com/${project.artifactId}/web</repository>
     <tag>latest</tag>
     <buildArgs>
       <JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
     </buildArgs>
  </configuration>
</plugin>

Такая конфигурация плагина будет запускать сборку образа как часть цели install, а push образа в docker-репозиторий Heroku, как часть цели deploy. Кстати, чтобы можно было запустить maven deploy нужно отключить deploy собранного jar файла (нам его не нужно никуда выкладывать). Сделать это можно при помощи опции maven.deploy.skip в разделе properties файла pom.xml.

<properties>
  <java.version>1.8</java.version>
  <maven.deploy.skip>true</maven.deploy.skip>
</properties>

Далее создадим Dockerfile в корневой папке проекта (рядом с pom.xml)

FROM openjdk:8-jdk-alpine

ARG JAR_FILE

RUN mkdir -p /apps
COPY ./target/${JAR_FILE} /apps/app.jar
COPY ./entrypoint.sh /apps/entrypoint.sh

RUN chmod +x /apps/entrypoint.sh
ENTRYPOINT ["/apps/entrypoint.sh"]

Как видите мы передаем сюда имя и собранного jar файла, как аргумент сборки. Значение этого аргумента задается в настройках maven плагина.

Скрипт entrypoint.sh разместим также в корне проект и будет он очень простым.

#!/usr/bin/env sh

/usr/bin/java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Xmx256m -Xss512k -XX:MetaspaceSize=100m -jar /apps/app.jar

Обратите внимание на параметры JVM ограничивающие память и особенно на два первых параметра. Они включают специальный режим управления памятью, который необходим когда java приложение запускается внутри Docker контейнера. Доступная приложению память на бесплатных машинах Heroku ограничена 512Мб. Превышение этого лимита приведет к завершению приложения. Если вы хотите использовать Java 9+, то первые две опции нужно заменить на одну -XX:+UseContainerSupport. Подробнее можете почитать здесь.

После выполнения этих действий попробуйте запустить
mvnw clean install

Если все сделано правильно, то должна произойти сборка приложения и docker-образа для него. Проверить, создался ли образ можно командой
docker images

Теперь приступим к настройке облака и будем использовать для этого уже упомянутый Heroku CLI. Разумеется для выполнения этих действий вам нужно будет зарегистрировать аккаунт на Heroku.

Прежде всего нам нужно авторизоваться. Для этого запускаем команду
heroku login
и следуем дальнейшим инструкциям.

После этого нужно авторизоваться в Docker-репозитории heroku. Для этого запускаем команду
heroku container:login
без этого мы не сможем сделать push нашего docker-образа.

Далее создаем приложение при помощи команды
heroku apps:create <app-name>

Обратите внимание, что имя приложения должно совпадать с именем артефакта, которое указанно в pom.xml. Возможно тут вам придется потратить какое-то время на подбор имени приложения, которое еще не задано.

После того, как приложение создано, запускаем
mvnw clean deploy

и ждем пока приложение будет собранно и произойдет push docker-образа. Обращу ваше внимание, что push возможен только если имя образа соответствует шаблону registry.heroku.com/<app-name>/web и приложение с именем <app-name> было создано. Если посмотрите на настройки maven плагина, то увидите, что у нас все сделано именно так.

Последний шаг для того, чтобы образ был развернут и запущен это команда
heroku container:release web --app=<app-name>

После этого переходите по ссылке https://<app-name>.herokuapp.com/check и через некоторое время увидите текст, который будет выведен обработчиком контроллера.

Еще один способ открыть запущенное приложение в браузере это команда
heroku open --app=<app-name>

В случае если что-то не работает, логи можно посмотреть в Web интерфейсе Heroku или командой
heroku logs

На этом все! Надеюсь, что это руководство было вам полезно!

Несколько полезных ссылок


1. Документация по Heroku CLI devcenter.heroku.com/categories/command-line
2. Про особенности управления памятью в докеризованной Java devcenter.heroku.com/articles/java-memory-issues
3. devcenter.heroku.com/categories/deploying-with-docker
4. Пример Java приложения от Heroku. Тут другой способ развертывания, но посмотреть полезно github.com/heroku/java-getting-started
5. Руководство от Heroku по запуску Java приложения (не через Docker) devcenter.heroku.com/articles/getting-started-with-java?singlepage=true
  • +11
  • 2,6k
  • 3
Поделиться публикацией

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

    0
    Отличный мануал! А как быть, если приложение должно работать с БД? Как настроить доступ, чтобы БД не светить «в мир»?
      +2
      Благодарю! Базу данных там подключить очень просто. После создания приложения набираете команду
      heroku addons:create heroku-postgresql:hobby-dev --app appname
      и у вас готовый к работе Postgres. Строка соединения к нему доступна через переменную окружения DATABASE_URL. Есть небольшая проблема с тем, что формат этой строки соединения не совсем такой, как нужен для JDBC, но это решаемо.

      К слову, там огромное количество дополнений: Redis, RabbitMQ и др.
        +2
        Кстати, вот этот скрипт поможет адаптировать строку подключения для JDBC github.com/heroku/heroku-buildpack-jvm-common/blob/master/opt/jdbc.sh

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

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