В наши дни использование Docker стало обычным явлением. В этом руководстве мы узнаем, как научиться докеризовать наши HTTP-приложения Scala и Akka, без даже создания файла Docker.
В рамках этого руководства мы предполагаем, что Docker уже установлен на машине. Если это не так, следуйте официальной документации.
Чтобы автоматизировать создание Dockerfile для нашего проекта, мы будем использовать плагин sbt-native-packager.
Для этого руководства вы можете использовать любой проект Scala или Akka HTTP. Мы будем использовать следующий репозиторий, не стесняйтесь клонировать его и обязательно проверьте ветку 6.5-testing-directives
.
Добавление плагина
Во-первых, нам нужно добавить плагин в наш проект в project/plugins.sbt
файле. Если файл не существует, нам нужно создать его, а затем добавить следующую строку:
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.6")
Затем нам нужно включить плагин в нашем build.sbt
файле. Добавьте следующую строку вверху:
enablePlugins(JavaAppPackaging)
Включение этого плагина также позволяет нам создать исполняемый файл для нашего приложения. Запустите sbt stage
в корневом каталоге проекта.
Теперь мы можем выполнить наше приложение, запустив ./target/universal/stage/bin/akkahttp-quickstart
. Вы должны увидеть сообщение Success!
. Если вы отправите запрос GET на localhost:9000/todos
, вы получите пару задач.
Докеризация нашего приложения
Пришло время поиграть с Docker.
Начнем с создания Dockerfile для нашего приложения. Запустите sbt docker:stage
, затем запустите cat target/docker/stage/Dockerfile
, чтобы увидеть его содержимое:
FROM openjdk:latest
WORKDIR /opt/docker
ADD --chown=daemon:daemon opt /opt
USER daemon
ENTRYPOINT ["/opt/docker/bin/akkahttp-quickstart"]
CMD []
Все очень просто. В итоге он запускает двоичный файл, аналогичный тому, который мы сгенерировали и запустили ранее.
Мы можем создать образ Docker, используя этот файл Docker вручную, но есть более удобный способ сделать это. Давайте запустим sbt docker:publishLocal.
Как следует из названия, он опубликует Docker-образ нашего приложения в нашем локальном реестре.
Запустите docker images
, и вы должны увидеть следующую запись:
REPOSITORY TAG IMAGE ID CREATED SIZE
akkahttp-quickstart 0.1 d03732dd0854 42 seconds ago 774MB
Теперь мы можем запустить наше приложение с помощью Docker.
Запустите docker run akkahttp-quickstart:0.1
, вы снова должны увидеть сообщение Success!
.
Но на этот раз, если мы попытаемся запросить наше приложение, мы получим ошибку:
Давайте запустим docker ps
, чтобы получить некоторую информацию о нашем запущенном процессе Docker (вывод сокращен):
CONTAINER ID IMAGE PORTS NAMES
9746162d4723 akkahttp-quickstart:0.1 serene_agnesi
Как мы видим, нет открытых портов, поэтому нет возможности взаимодействовать с нашим приложением.
Приложения в Docker по умолчанию запускаются в их сети. Существует несколько способов разрешить обмен данными между процессами Docker и хост-компьютером. Самый простой способ - выставить порт.
Остановите запущенное приложение, нажав Ctrl-C
или запустив docker stop $CONTAINER_ID
.
На этот раз, когда мы запустим его, мы также выставим соответствующий порт:
docker run -p 9000:9000 akkahttp-quickstart:0.1
Теперь мы можем запросить наше докеризованное приложение:
Настройка нашей установки
Есть несколько вещей, которые мы могли бы сделать с текущими настройками, которые у нас есть:
- Что, если нам нужно другое имя изображения?
- Что, если мы захотим использовать другой порт?
- Можно ли сделать изображение светлее?
Давайте рассмотрим эти распространенные варианты использования.
Изменение имени изображения
Если мы заглянем в официальную документацию плагина, то увидим, что есть ряд параметров, которые мы можем изменить.
Прочтите его и посмотрите, что еще вы можете настроить.
Чтобы изменить имя изображения, мы можем изменить свойство packageName
в нашем build.sbt
файле, добавив следующую строку после свойства scalaVersion
:
packageName in Docker := "dockerised-akka-http"
Опубликуем изображение еще раз. Запустите sbt docker:publishLocal
. Мы можем проверить наличие нового образа, запустив docker images
:
REPOSITORY TAG IMAGE ID CREATED SIZE
akkahttp-quickstart 0.1 d03732dd0854 42 minutes ago 774MB
dockerised-akka-http 0.1 d03732dd0854 42 minutes ago 774MB
Теперь у нас есть два изображения, исходное и новое. Потрясающие!
Смена порта
Мы не можем изменить порт, который прослушивает наше приложение, не внося изменений в код. Порт жестко запрограммирован в нашем приложении. В идеале мы должны читать его из переменной окружения и, возможно, использовать ее по умолчанию.
Но это нормально. Поскольку наше приложение работает в другой сети, мы можем сопоставить другой порт с внутренним портом 9000.
Когда мы указываем флаг -p 9000:9000
, мы говорим, что порт 9000 на хост-машине будет отображаться на порт 9000 в сети нашего процесса. Попробуем это изменить.
Запустите docker run -p 5000:9000 dockerised-akka-http:0.1
, чтобы запустить наш новый образ с другим портом.
Мы можем запросить todos
, чтобы убедиться, что он работает должным образом:
Делаем наш образ светлее
В нашем последнем эксперименте мы постараемся сделать изображение светлее. На данный момент он использует более 700 МБ.
Во-первых, давайте увеличим версию, чтобы получить другой тег и сравнить их. Затем добавьте dockerBaseImage := "openjdk:8-jre-alpine"
выше, где мы меняем packageName
. Наш build.sbt
теперь выглядит так:
enablePlugins(JavaAppPackaging)
name := "akkahttp-quickstart" version := "0.2" scalaVersion := "2.12.6"
dockerBaseImage := "openjdk:8-jre-alpine" packageName in Docker := "dockerised-akka-http"
val akkaVersion = "2.5.13" val akkaHttpVersion = "10.1.3" val circeVersion = "0.9.3"
libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-testkit" % akkaVersion % Test, "com.typesafe.akka" %% "akka-stream" % akkaVersion, "com.typesafe.akka" %% "akka-stream-testkit" % akkaVersion % Test, "com.typesafe.akka" %% "akka-http" % akkaHttpVersion, "com.typesafe.akka" %% "akka-http-testkit" % akkaHttpVersion % Test, "io.circe" %% "circe-core" % circeVersion, "io.circe" %% "circe-generic" % circeVersion, "io.circe" %% "circe-parser" % circeVersion, "de.heikoseeberger" %% "akka-http-circe" % "1.21.0", "org.scalatest" %% "scalatest" % "3.0.5" % Test )
Мы используем другой тег базового образа openjdk
, чтобы указать, что мы хотим использовать alpine
, который является облегченным дистрибутивом Linux.
Опубликуйте изображение, запустив sbt docker:publishLocal
. Получите изображения с docker images
. Мы видим, что изображение стало светлее:
REPOSITORY TAG IMAGE ID CREATED SIZE
dockerised-akka-http 0.2 4688366e70bb 32 seconds ago 119MB
akkahttp-quickstart 0.1 d03732dd0854 2 hours ago 774MB
dockerised-akka-http 0.1 d03732dd0854 2 hours ago 774MB
Убедимся, что он по-прежнему работает.
Запустите docker run -p 5000:9000 dockerised-akka-http:0.2
, обращая внимание на номер тега. Не работает, мы получаем следующую ошибку:
env: can't execute 'bash': No such file or directory
Очевидно, нашему докеризованному приложению требуется bash для запуска. Читая документацию по плагину, мы можем сказать, что он генерирует bash-скрипт, который выполняет наше приложение.
Итак, давайте установим bash в наш образ и попробуем еще раз.
Добавьте следующие строки ниже, где мы меняем packageName
в нашем build.sbt
файле:
import com.typesafe.sbt.packager.docker._
dockerCommands ++= Seq(
Cmd("USER", "root"),
ExecCmd("RUN", "apk", "add", "--no-cache", "bash")
)
Мы добавляем несколько дополнительных команд в наш Dockefile. Мы меняем пользователя на root
для установки пакета, а затем устанавливаем bash.
Попробуем снова запустить приложение, docker run -p 5000:9000 dockerised-akka-http:0.2
. И теперь он работает, отлично!
Если мы снова проверим изображения, то изображение на основе alpine будет немного больше, например, 10 МБ. Это ничто по сравнению с примерно 770 МБ других.
Установка bash в alpine - не самое худшее в мире. Некоторые люди все равно добавляют его из-за своих предпочтений и для отладки.
Создание исполняемого файла, совместимого с Ash
Установка bash в наш образ - это своего рода обходной путь. Давайте воспользуемся дополнительным плагином для создания исполняемого файла, совместимого с Alpine. Спасибо Muki Seller за то, что сообщили нам об этом решении!
Согласно официальной документации, нам нужно включить дополнительный плагин AshScriptPlugin
.
Измените файл build.sbt
, чтобы включить оба плагина, и удалите предыдущий обходной путь:
enablePlugins(JavaAppPackaging, AshScriptPlugin)
name := "akkahttp-quickstart" version := "0.3" scalaVersion := "2.12.6"
dockerBaseImage := "openjdk:8-jre-alpine" packageName in Docker := "dockerised-akka-http"
val akkaVersion = "2.5.13" val akkaHttpVersion = "10.1.3" val circeVersion = "0.9.3"
libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-testkit" % akkaVersion % Test, "com.typesafe.akka" %% "akka-stream" % akkaVersion, "com.typesafe.akka" %% "akka-stream-testkit" % akkaVersion % Test, "com.typesafe.akka" %% "akka-http" % akkaHttpVersion, "com.typesafe.akka" %% "akka-http-testkit" % akkaHttpVersion % Test,
"io.circe" %% "circe-core" % circeVersion, "io.circe" %% "circe-generic" % circeVersion, "io.circe" %% "circe-parser" % circeVersion, "de.heikoseeberger" %% "akka-http-circe" % "1.21.0",
"org.scalatest" %% "scalatest" % "3.0.5" % Test )
Мы также увеличили версию, чтобы мы могли сравнивать и не переопределять предыдущую.
Запустите sbt docker:publishLocal
, а затем docker run dockerised-akka-http:0.3
.
Вы должны увидеть сообщение об успешном выполнении, и, если вы запросите задачи, вы также должны увидеть их. Большой!
Заключение
В этом руководстве мы докерили приложение Scala и Akka HTTP. Специально для этого приложения ничего не было сделано, а это значит, что установка будет работать в основном без изменений.
Затем мы рассмотрели, как реализовать некоторые распространенные варианты использования, настроив наш Dockerfile через плагин.
Нам даже удалось уменьшить размер изображения почти в семь раз!
Удивительно, не правда ли?
Если вам понравился этот учебник и вы хотите узнать, как создать API для приложения todo, ознакомьтесь с нашим новым бесплатным курсом! 😁👇🏽
Первоначально опубликовано на www.codemunity.io.