Деплой Elixir при помощи Docker

Для разработки приложений обычно используются различные технологии, такие как Ruby/Rails, Elixir/Phoenix, Java, Python или даже PHP. И как бы нам ни хотелось, использовать единый инструмент для деплоя всего, это невозможно.

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

Как работать с Docker

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

Поэтому гораздо выгоднее разделить этот процесс на две части, то есть отделить разработку приложения (компиляцию, подготовку релиза) от деплоя и эксплуатации.

Далее рассматривается такой процесс для Elixir, хотя и для остальных языков применяются те же самые принципы. Например, запущенный контейнер с приложением на JavaScript содержит только откомпилированный код без каких-либо npm зависимостей.

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

Как было сказано прежде, построение Docker-образа осуществляется в два этапа:

  1. Разработка

    1. Установить Erlang

    2. Установить Elixir

    3. Запустить mix deps.get

    4. Запустить mix release

    5. Сохранить дистрибутив myapp.tar.gz

  2. Подготовка релиза

    1. Установить Erlang

    2. Извлечь файлы из дистрибутива myapp.tar.gz в контейнер

    3. Готово!

Так как этот процесс одинаков для всех Elixir-приложений, был разработан простой модуль mix_docker, который выполняет все вышеперечисленные действия при помощи парочки mix-команд.

Немного о mix_docker

Утилита mix_docker предоставляет несколько mix-команд для простой и удобной упаковки Elixir-приложений в Docker-образы. Он построен на базе пакета distillery от Пола Сконфелдера и легковесного Docker-образа alpine-erlang.

Ниже перечислены шесть этапов создания Docker-образа.

1. Добавить mix_docker в mix.exs:

def deps do
  [{:mix_docker, "~> 0.2.2"}]
end

2. Дать образу имя в config/config.exs:

config :mix_docker, image: "teamon/demo"

3. Инициализировать настройки приложения:

mix docker.init

После этого запустится distillery init и создастся файл rel/config.exs, и вам даже не придётся ничего менять – стандартные значения для Docker доступны из коробки.

4. Подготовить релиз

mix docker.build

Создастся образ teamon/demo:build, содержащий в себе пакет demo.tar.gz.

5. Создание минимального образа приложения:

mix docker.release

Архив demo.tar.gz распакуется в минимальный Docker-образ, готовый к запуску в продакшн. Такие образы, как правило, весят меньше, чем изначальные.

6. Опубликовать образ на Docker Hub

mix docker.publish

После этого образ получит название согласно текущей версии приложения в mix.exs, количеству git-коммитов и sha-хешу, например 0.1.0.253-158c4a45c1. Полное имя образа будет таким: teamon/demo:0.1.0.253-158c4a45c1.

Существует также команда mix docker.shipit, выполняющая сразу и сборку, и подготовку релиза, и публикацию образа.

Настройка загруженных в Docker приложений

Так как релизы в продакшн не содержат Mix, то наиболее простой способ подобрать рабочие настройки – использовать переменные среды. Стандартные образы Docker, созданные с помощью mix_docker, содержат конфигурацию REPLACE_OS_VARS=true, поэтому нужно будет «подготовить» файл config/prod.exs следующим образом:

config :demo, Demo.Endpoint,
  server: true,
  # use {:system, var} if library supports it
  http: [port: {:system, "PORT"}],
  # use ${VAR} syntax to replace config on startup
  url: [ host: "${APP_DOMAIN}" ]

config :demo, Demo.Mailer,
  adapter: Bamboo.MailgunAdapter,
  api_key: "${MAILGUN_API_KEY}"

Подробнее об этом можно почитать в документации к пакету distillery.

Не забудьте перестроить образ после изменения конфигураций!

Запуск приложения

Теперь можно смело запускать приложение, как простой Docker-контейнер.

docker run -e PORT=4000 teamon/demo:0.1.0.253-158c4a45c1 foreground

Использование удалённого терминала

Так как Docker-контейнеры самодостаточны, то, чтобы подключиться к работающей ноде, используя remote_console, нужно выполнить команду exec с текущим контейнером:

docker exec -it CID /opt/app/bin/demo remote_console

Вот и всё!

Одно из явных преимуществ использования переменных среды для настройки приложений во время выполнения – это возможность использования одного и того же образа как на стадии подготовки, так и на стадии внедрения (изменяются только домены, API-ключи и т. д.), что очень удобно во время деплой в продакшн.

В следующей части рассказывается о том, как осуществить деплой этих же Docker-контейнеров с помощью Rancher и соединить несколько экземпляров в один Erlang-кластер.

© 2020 / Россия Любые мысли и вопросы пишите на elixir@wunsh.ru.