Spec-Zone.ru › Phoenix

Развертывание на Heroku

Исходный код

Главная цель этого руководства — запустить приложение Phoenix на Heroku.

Что нам понадобится

Для этого руководства нам понадобится только работающее приложение Phoenix. Если вам нужно простое приложение для развертывания, следуйте руководству «Начало работы».

Ограничения

Heroku — отличная платформа, и Elixir хорошо на ней работает. Однако вы можете столкнуться с ограничениями, если планируете использовать расширенные возможности Elixir и Phoenix, например:

  • Количество подключений ограничено.

    • Heroku ограничивает число одновременных подключений, а также длительность каждого подключения. Elixir часто используют для приложений реального времени, которым требуется множество одновременных постоянных подключений, а Phoenix способен обрабатывать более 2 миллионов подключений на одном сервере.
  • Распределенная кластеризация невозможна.

    • Heroku изолирует dyno друг от друга с помощью брандмауэров. Это означает, что таким возможностям, как распределенные каналы Phoenix и распределенные задачи, придется полагаться, например, на Redis вместо встроенного механизма распределения Elixir.
  • Состояние в памяти, например в Agents, GenServers и ETS, будет теряться каждые 24 часа.

    • Heroku перезапускает dyno каждые 24 часа, независимо от того, исправен ли узел.
  • Встроенный observer нельзя использовать с Heroku.

    • Heroku позволяет подключаться к dyno, но использовать observer для наблюдения за состоянием dyno не получится.

Если вы только начинаете работу или не планируете использовать перечисленные выше возможности, Heroku должно быть достаточно. Например, если вы переносите на Phoenix существующее приложение, работающее на Heroku, и сохраняете примерно тот же набор функций, Elixir будет работать не хуже, а возможно, даже лучше вашего текущего стека.

Если вам нужна платформа как услуга без этих ограничений, альтернативы перечислены на боковой панели и доступны в других местах. Если вы предпочитаете развертывание на облачной платформе, например EC2, Google Cloud и т. д., рассмотрите возможность использования mix release.

Этапы

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

  • Инициализировать репозиторий Git
  • Зарегистрироваться на Heroku
  • Установить Heroku Toolbelt
  • Создать приложение Heroku и настроить его
  • Подготовить проект к работе на Heroku
  • Развернуть приложение!
  • Полезные команды Heroku

Инициализация репозитория Git

Git — популярная децентрализованная система управления версиями, которая также используется для развертывания приложений на Heroku.

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

$ git init
$ git add .
$ git commit -m "Initial commit"

Здесь Heroku рассказывает подробнее о том, как использует Git: здесь.

Регистрация на Heroku

Зарегистрироваться на Heroku очень просто: перейдите на https://signup.heroku.com/ и заполните форму.

Бесплатный тариф предоставит один веб-dyno и один рабочий dyno, а также бесплатные экземпляры PostgreSQL и Redis.

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

Установка Heroku Toolbelt

После регистрации скачайте подходящую для вашей системы версию Heroku Toolbelt здесь.

Heroku CLI, входящий в состав Toolbelt, пригодится для создания приложений Heroku, просмотра списка запущенных dyno существующего приложения, вывода журналов в реальном времени и выполнения разовых команд (например, задач mix).

Создание и настройка приложения Heroku

Есть два способа развернуть приложение Phoenix на Heroku: использовать buildpack Heroku или стек контейнеров. Различие между этими подходами заключается в том, как мы указываем Heroku обрабатывать сборку. При использовании buildpack нужно настроить приложение на Heroku, чтобы оно использовало buildpack для Phoenix/Elixir. При использовании контейнеров у нас больше возможностей контролировать настройку приложения, а образ контейнера можно определить с помощью Dockerfile и heroku.yml. В этом разделе мы рассмотрим подход с buildpack. Для использования Dockerfile часто рекомендуют преобразовать приложение, чтобы использовать релизы; об этом мы расскажем далее.

Создание приложения

Buildpack — удобный способ упаковать поддержку фреймворка и/или среды выполнения. Для работы Phoenix на Heroku требуются два buildpack: первый добавляет базовую поддержку Elixir, а второй — команды для Phoenix.

Установив Toolbelt, создадим приложение Heroku. Для этого воспользуемся последней доступной версией buildpack для Elixir:

$ heroku create --buildpack hashnuke/elixir
Creating app... done, ⬢ mysterious-meadow-6277
Setting buildpack to hashnuke/elixir... done
https://mysterious-meadow-6277.herokuapp.com/ | https://git.heroku.com/mysterious-meadow-6277.git

Примечание: при первом выполнении команды Heroku может появиться запрос на вход. В этом случае введите адрес электронной почты и пароль, указанные при регистрации.

Примечание: имя приложения Heroku — это случайная строка после «Creating» в приведенном выше выводе (mysterious-meadow-6277). Имя будет уникальным, поэтому у вас будет другое значение, а не «mysterious-meadow-6277».

Примечание: URL-адрес в выводе ведет на наше приложение. Если открыть его в браузере сейчас, отобразится приветственная страница Heroku по умолчанию.

Примечание: если бы мы не инициализировали репозиторий Git до выполнения команды heroku create, удаленный репозиторий Heroku сейчас не был бы настроен должным образом. Это можно сделать вручную, выполнив: heroku git:remote -a [our-app-name].

В buildpack заданы версии Elixir и Erlang по умолчанию. Во избежание неожиданностей при развертывании лучше явно указать версии Elixir и Erlang, которые будут использоваться в production, чтобы они совпадали с версиями для разработки или на серверах непрерывной интеграции. Для этого создайте в корневом каталоге проекта файл конфигурации elixir_buildpack.config и укажите в нем нужные версии Elixir и Erlang:

# Elixir version
elixir_version=1.15.0

# Erlang version
# https://github.com/HashNuke/heroku-buildpack-elixir-otp-builds/blob/master/otp-versions
erlang_version=25.3

# Invoke assets.deploy defined in your mix.exs to deploy assets with esbuild
# Note we nuke the esbuild executable from the image
hook_post_compile="eval mix assets.deploy && rm -f _build/esbuild*"

Наконец, укажем buildpack, как запускать веб-сервер. Создайте файл Procfile в корневом каталоге проекта:

web: mix phx.server

Необязательно: Node, npm и Phoenix Static buildpack

По умолчанию Phoenix использует esbuild и управляет всеми ресурсами за вас. Однако, если вы используете node и npm, для их обработки потребуется установить Phoenix Static buildpack:

$ heroku buildpacks:add https://github.com/gigalixir/gigalixir-buildpack-phoenix-static.git
Buildpack added. Next release on mysterious-meadow-6277 will use:
  1. https://github.com/HashNuke/heroku-buildpack-elixir.git
  2. https://github.com/gigalixir/gigalixir-heroku-buildpack-phoenix-static.git

При использовании этого buildpack всю сборку ресурсов нужно делегировать npm. Поэтому необходимо удалить конфигурацию hook_post_compile из elixir_buildpack.config и перенести ее в скрипт развертывания вашего assets/package.json. Например:

{
  ...
  "scripts": {
    "deploy": "cd .. && mix assets.deploy && rm -f _build/esbuild*"
  }
  ...
}

В Phoenix Static buildpack задана версия Node.js по умолчанию. Во избежание неожиданностей при развертывании лучше явно указать версию Node.js для production, чтобы она совпадала с версией для разработки или на серверах непрерывной интеграции. Для этого создайте в корневом каталоге проекта файл конфигурации phoenix_static_buildpack.config и укажите в нем нужную версию Node.js:

# Node.js version
node_version=10.20.1

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

Наконец, учтите, что при использовании нескольких buildpack может возникнуть проблема с неправильным порядком выполнения (buildpack для Elixir должен выполняться перед Phoenix Static buildpack). В документации Heroku это объясняется подробнее, но нужно убедиться, что Phoenix Static buildpack указан последним.

Подготовка проекта к работе на Heroku

В каждом новом проекте Phoenix есть файл конфигурации config/runtime.exs, который загружает конфигурацию и секреты из переменных окружения. Это хорошо согласуется с рекомендациями Heroku (приложения на основе 12 факторов), поэтому нам остается настроить URL-адреса и SSL.

Сначала укажем Phoenix, что сайту нужно использовать только версию с SSL. Найдите конфигурацию endpoint в файле config/prod.exs:

config :scaffold, ScaffoldWeb.Endpoint,
  url: [port: 443, scheme: "https"],

... и добавьте force_ssl

config :scaffold, ScaffoldWeb.Endpoint,
  url: [port: 443, scheme: "https"],
  force_ssl: [rewrite_on: [:x_forwarded_proto]],

Здесь нужно задать force_ssl, потому что это конфигурация времени компиляции. Она не будет работать, если задать ее в runtime.exs.

Затем в файле config/runtime.exs:

... добавьте host

config :scaffold, ScaffoldWeb.Endpoint,
  url: [host: host, port: 443, scheme: "https"]

и раскомментируйте строку # ssl: true, в конфигурации репозитория. Она будет выглядеть так:

config :hello, Hello.Repo,
  ssl: true,
  url: database_url,
  pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")

Наконец, если вы планируете использовать веб-сокеты, нужно уменьшить время ожидания для транспорта веб-сокетов в lib/hello_web/endpoint.ex. Если веб-сокеты использовать не планируется, можно оставить значение false. Подробнее о доступных параметрах см. в документации.

defmodule HelloWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :hello

  socket "/socket", HelloWeb.UserSocket,
    websocket: [timeout: 45_000]

  ...
end

Также задайте хост в Heroku:

$ heroku config:set PHX_HOST="mysterious-meadow-6277.herokuapp.com"

Это гарантирует, что Phoenix закроет все бездействующие подключения до истечения 55-секундного времени ожидания Heroku.

Создание переменных окружения в Heroku

Переменная конфигурации DATABASE_URL создается автоматически при добавлении дополнения Heroku Postgres. Базу данных можно создать с помощью Heroku Toolbelt:

$ heroku addons:create heroku-postgresql:mini

Теперь зададим переменную конфигурации POOL_SIZE:

$ heroku config:set POOL_SIZE=18

Это значение должно быть немного меньше числа доступных подключений, чтобы несколько подключений оставались свободными для миграций и задач mix. Мини-база данных допускает 20 подключений, поэтому зададим значение 18. Если к базе данных будут подключаться дополнительные dyno, уменьшите POOL_SIZE, чтобы равномерно распределить подключения между dyno.

При последующем запуске задачи mix (после отправки проекта на Heroku) также нужно будет ограничить размер ее пула, например так:

$ heroku run "POOL_SIZE=2 mix hello.task"

Это не позволит Ecto пытаться открыть больше подключений, чем доступно.

Нам также нужно создать конфигурацию SECRET_KEY_BASE на основе случайной строки. Сначала воспользуйтесь mix phx.gen.secret, чтобы сгенерировать новый секрет:

$ mix phx.gen.secret
xvafzY4y01jYuzLm3ecJqo008dVnU3CN4f+MamNd1Zue4pXvfvUjbiXT8akaIF53

У вас будет другая случайная строка; не используйте значение из этого примера.

Теперь зададим ее в Heroku:

$ heroku config:set SECRET_KEY_BASE="xvafzY4y01jYuzLm3ecJqo008dVnU3CN4f+MamNd1Zue4pXvfvUjbiXT8akaIF53"
Setting config vars and restarting mysterious-meadow-6277... done, v3
SECRET_KEY_BASE: xvafzY4y01jYuzLm3ecJqo008dVnU3CN4f+MamNd1Zue4pXvfvUjbiXT8akaIF53

Развертывание!

Теперь проект готов к развертыванию на Heroku.

Зафиксируем все изменения:

$ git add elixir_buildpack.config
$ git commit -a -m "Use production config from Heroku ENV variables and decrease socket timeout"

И развернем приложение:

$ git push heroku main
Counting objects: 55, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (49/49), done.
Writing objects: 100% (55/55), 48.48 KiB | 0 bytes/s, done.
Total 55 (delta 1), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Multipack app detected
remote: -----> Fetching custom git buildpack... done
remote: -----> elixir app detected
remote: -----> Checking Erlang and Elixir versions
remote:        WARNING: elixir_buildpack.config wasn't found in the app
remote:        Using default config from Elixir buildpack
remote:        Will use the following versions:
remote:        * Stack cedar-14
remote:        * Erlang 17.5
remote:        * Elixir 1.0.4
remote:        Will export the following config vars:
remote:        * Config vars DATABASE_URL
remote:        * MIX_ENV=prod
remote: -----> Stack changed, will rebuild
remote: -----> Fetching Erlang 17.5
remote: -----> Installing Erlang 17.5 (changed)
remote:
remote: -----> Fetching Elixir v1.0.4
remote: -----> Installing Elixir v1.0.4 (changed)
remote: -----> Installing Hex
remote: 2015-07-07 00:04:00 URL:https://s3.amazonaws.com/s3.hex.pm/installs/1.0.0/hex.ez [262010/262010] ->
"/app/.mix/archives/hex.ez" [1]
remote: * creating /app/.mix/archives/hex.ez
remote: -----> Installing rebar
remote: * creating /app/.mix/rebar
remote: -----> Fetching app dependencies with mix
remote: Running dependency resolution
remote: Dependency resolution completed successfully
remote: [...]
remote: -----> Compiling
remote: [...]
remote: Generated phoenix_heroku app
remote: [...]
remote: Consolidated protocols written to _build/prod/consolidated
remote: -----> Creating .profile.d with env vars
remote: -----> Fetching custom git buildpack... done
remote: -----> Phoenix app detected
remote:
remote: -----> Loading configuration and environment
remote:        Loading config...
remote:        [...]
remote:        Will export the following config vars:
remote:        * Config vars DATABASE_URL
remote:        * MIX_ENV=prod
remote:
remote: -----> Compressing... done, 82.1MB
remote: -----> Launching... done, v5
remote:        https://mysterious-meadow-6277.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/mysterious-meadow-6277.git
 * [new branch]      master -> master

Если ввести heroku open в терминале, откроется браузер со страницей приветствия Phoenix. Если для доступа к базе данных используется Ecto, после первого развертывания также нужно выполнить миграции:

$ heroku run "POOL_SIZE=2 mix ecto.migrate"

Вот и все!

Развертывание на Heroku с помощью стека контейнеров

Создание приложения Heroku

Установите для приложения стек container — это позволит использовать Dockerfile для настройки приложения.

$ heroku create
Creating app... done, ⬢ mysterious-meadow-6277
$ heroku stack:set container

Добавьте новый файл heroku.yml в корневой каталог. В этом файле можно определить дополнения, используемые приложением, способ сборки образа и конфигурации, передаваемые образу. Подробнее о параметрах heroku.yml Heroku можно узнать здесь. Вот пример:

setup:
  addons:
    - plan: heroku-postgresql
      as: DATABASE
build:
  docker:
    web: Dockerfile
  config:
    MIX_ENV: prod
    SECRET_KEY_BASE: $SECRET_KEY_BASE
    DATABASE_URL: $DATABASE_URL

Настройка релизов и Dockerfile

Теперь в корневом каталоге проекта с приложением нужно создать файл Dockerfile. Для этого рекомендуется использовать релизы: с их помощью можно собрать контейнер, содержащий только те компоненты Erlang и Elixir, которые действительно используются. Следуйте документации по релизам. В конце руководства приведен пример файла Dockerfile, который можно использовать.

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

Полезные команды Heroku

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

$ heroku logs # use --tail if you want to tail them

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

$ heroku run "POOL_SIZE=2 iex -S mix"

На самом деле с помощью команды heroku run можно запускать любые команды, например упомянутую выше задачу миграции Ecto:

$ heroku run "POOL_SIZE=2 mix ecto.migrate"

Подключение к dyno

Heroku позволяет подключаться к dyno через оболочку IEx, в которой можно выполнять код Elixir, например запросы к базе данных.

  • Измените процесс web в Procfile, чтобы запускать именованный узел:

    web: elixir --sname server -S mix phx.server
  • Повторно разверните приложение на Heroku

  • Подключитесь к dyno с помощью heroku ps:exec (если в одном репозитории находится несколько приложений, укажите имя приложения или удаленного репозитория с помощью --app APP_NAME или --remote REMOTE_NAME)

  • Запустите сессию iex с помощью iex --sname console --remsh server

Теперь у вас есть сессия iex в вашем dyno!

Устранение неполадок

Ошибка компиляции

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

remote: == Compilation error on file lib/postgrex/connection.ex ==
remote: could not compile dependency :postgrex, "mix compile" failed. You can recompile this dependency with "mix deps.compile postgrex", update it with "mix deps.update postgrex" or clean it with "mix deps.clean postgrex"
remote: ** (CompileError) lib/postgrex/connection.ex:207: Postgrex.Connection.__struct__/0 is undefined, cannot expand struct Postgrex.Connection
remote:     (elixir) src/elixir_map.erl:58: :elixir_map.translate_struct/4
remote:     (stdlib) lists.erl:1353: :lists.mapfoldl/3
remote:     (stdlib) lists.erl:1354: :lists.mapfoldl/3
remote:
remote:
remote:  !     Push rejected, failed to compile elixir app
remote:
remote: Verifying deploy...
remote:
remote: !   Push rejected to mysterious-meadow-6277.
remote:
To https://git.heroku.com/mysterious-meadow-6277.git

Причина — устаревшие зависимости, которые не перекомпилируются должным образом. Чтобы решить эту проблему, можно заставить Heroku перекомпилировать все зависимости при каждом развертывании. Для этого создайте в корневом каталоге приложения файл elixir_buildpack.config. Он должен содержать следующую строку:

always_rebuild=true

Зафиксируйте этот файл в репозитории и попробуйте снова отправить проект на Heroku.

Ошибка ожидания подключения

Если при выполнении heroku run постоянно возникают тайм-ауты подключения, возможно, ваш интернет-провайдер заблокировал порт 5000:

heroku run "POOL_SIZE=2 mix myapp.task"
Running POOL_SIZE=2 mix myapp.task on mysterious-meadow-6277... !
ETIMEDOUT: connect ETIMEDOUT 50.19.103.36:5000

Это можно исправить, добавив параметр detached в команду запуска:

heroku run:detached "POOL_SIZE=2 mix ecto.migrate"
Running POOL_SIZE=2 mix ecto.migrate on mysterious-meadow-6277... done, run.8089 (Free)
← Предыдущая страница Развертывание на Gigalixir
Следующая страница → Краткая справка по маршрутизации

Пакет Hex Предпросмотр Hex (текущий файл) Скачать версию ePub

Создано с помощью ExDoc (v0.40.3) для языка программирования Elixir

© 2014 Chris McCord
Licensed under the MIT License.
https://phoenix.hexdocs.pm/heroku.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API