EliseeAlex.me

Экспортируем scala-проект в docker при помощи sbt-native

Как настроить sbt-native, чтобы он собрал jar со всеми зависимостями и положил его в docker

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

Для сборки проектов на scala я использую sbt. Он позволяет загружать внешние зависимости к проектам и настраивать скрипты для сборки приложения, запуска и тестирования. Это как maven или gradle для java или webpack для js.

В sbt есть модуль sbt native packeger, который позволяет собирать jar-ники и класть их в Docker. Документацию в проекте читать достаточно сложно, поэтому расскажу, на что нужно обратить внимание, чтобы всё получилось.

Я отталкиваюсь от того, что у вас уже есть готовый SBT-проект и объект с методом main, который вы хотите запускать при старте приложения.

Сборка проекта

Для того, чтобы создать образ для операционной системы или для docker нужно сначала собрать приложение, которое будет лежать в этом образе. Для этого, нужно настроить sbt-native, чтобы он собирал приложение со всеми зависимостями и проверить, что всё работает правильно.

Я настраивал эту сборку для создания бота Lise. Поэтому буду показывать сборку на его примере.

Сперва нужно добавить плагин в файл /project/plugins.sbt:

addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.1.1")

Теперь нужно включить плагин в вашем файле build.sbt, если на него ругается ваш ide — ничего страшного, так бывает в мире scala:

enablePlugins(JavaAppPackaging)

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

mainClass in Compile := Some("press.lis.lise.Bot")

После этого, если вызвать в командной строке sbt stage в папке ./target/universal/stage/bin/ появится скрипт для запуска приложения локально (там скрипты для командной строки linux/mac и windows).

Сборка приложения для разных платформ

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

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

Для сборки нужно включить подходящий плагин, для jar это JDKPackagerPlugin. Его нужно указать в build.sbt:

enablePlugins(JDKPackagerPlugin)

После этого будут доступны новые параметры для сборки через sbt. К примеру, если вызвать в командной строке:

sbt jdkPackager:packageBin

В папке ./target/universal/jdkpackager появятся все файлы, необходимы для запуска приложения (включая внешние зависимости).

## Упаковка приложения в образ Docker

Для доставки приложения на сервер я предпочитаю Docker. На нём не нужно устанавливать ничего лишнего и настраивать среду для scala, можно скачать образ, где всё настроено и просто добавить в него приложение.

Для подключения Docker нужно в build.sbt разрешить ещё один плагин:

enablePlugins(DockerPlugin)

Для его выполнения нужен локально установленный Docker, если у вас его нет — с установкой проблем не должно быть. После этого можно загрузить образ в локальный docker:

sbt docker:publishLocal

И запустить его:

docker run -it lise-bot:1.0-SNAPSHOT

Аргумент среды можно указывать прямо при запуске приложения, к примеру я запускаю docker так:

sudo docker run --name=lise-bot --net=host lise-bot:1.0-SNAPSHOT -Dbot.token=XXX -Ddb.default.user=YYY -Ddb.default.password=ZZZ

--name=lise-bot — это название, которое отображается в утилитах докера, по которому можно остановить работу образа.

--net=host — обозначает, что локальный компьютер будет доступен по адресу localhost. На маке и windows это не работало, когда я писал эту статью.

Экспорт артефакта на сервер

Осталось настроить экспорт артефакта. Я загружаю образ на сервер через репозиторий docker hub.

Для этого нужно создать аккаунт в docker hub и войти в этот аккаунт используя:

docker login

Дальше нужно в build.sbt указать имя репозитория, в котором будет доступен образ и имя создателя. Для меня это выглядело так:

maintainer in Docker := "eliseealex"

packageName in Docker := "eliseealex/lise-bot"

Приложение будет автоматически загружаться на Docker Hub если вызвать, в первый может быть долго:

sbt docker:publish

На сервере нужно скачать этот образ, указав версию такую же как в build.sbt (версия желательно указывать, в докере стандартное версионирование работает странно):

sudo docker pull eliseealex/lise-bot:1.0-SNAPSHOT

Мы загрузили образ, теперь можно запустить само приложение. Я запускаю его скрытым в консоли (используя &), но можно вместо этого использовать утилиту screen:

sudo docker run --name=lise-bot --net=host eliseealex/lise-bot:1.0-SNAPSHOT -Dbot.token=XXX -Ddb.default.user=YYY -Ddb.default.password=ZZZ >> ~/lise-bot/bot.log &

Для обновления, я обычно обновляю образ, останавливаю текущую программу и запускаю её заново. Полная процедура обновления выглядит так:

sudo docker pull eliseealex/lise-bot:1.0-SNAPSHOT
sudo docker rm -f lise-bot
sudo docker run --name=lise-bot --net=host eliseealex/lise-bot:1.0-SNAPSHOT -Dbot.token=XXX -Ddb.default.user=YYY -Ddb.default.password=ZZZ >> ~/lise-bot/bot.log &