mix release
Собирает автономный релиз для текущего проекта:
MIX_ENV=prod mix release MIX_ENV=prod mix release NAME
После сборки релиза его можно упаковать и развернуть на целевой системе, при условии, что целевая система работает на той же дистрибутиве и версии операционной системы (ОС), что и машина, на которой выполняется команда mix release.
Релиз можно настроить в вашем файле mix.exs по ключу :releases внутри def project.
def project do
[
releases: [
demo: [
include_executables_for: [:unix],
applications: [runtime_tools: :permanent]
],
...
]
]
end
Вы можете указать несколько релизов, где ключ — имя релиза, а значение — список ключевых слов с конфигурацией релиза. Выпуск определенного имени выполняется так:
MIX_ENV=prod mix release demo
Если заданного имени не существует, генерируется ошибка.
Если вызов mix release без имени и есть несколько имён, то будет сгенерирована ошибка, если вы не установите default_release: NAME в корне конфигурации проекта.
Если вызов mix release выполняется без указания имени, собирается релиз, использующий имя приложения и значения по умолчанию.
Зачем нужны релизы?
Релизы позволяют разработчикам предварительно скомпилировать и упаковать весь свой код и среду выполнения в единый блок. Преимущества релизов:
Предварительная загрузка кода. VM имеет два механизма загрузки кода: интерактивный и встроенный. По умолчанию он работает в интерактивном режиме, динамически загружая модули по мере их использования в первый раз. В первый раз, когда ваше приложение вызывает
Enum.map/2, VM найдёт модульEnumи загрузит его. Есть недостаток. Когда вы запускаете новый сервер в рабочей среде, ему может потребоваться загрузить множество других модулей, что приведет к аномальному увеличению времени отклика на первые запросы. При использовании Erlang/OTP версии ранее 23 система всегда работает во встроенном режиме. При использовании Erlang/OTP 23+ она работает в интерактивном режиме во время настройки, а затем переключается на встроенный режим, гарантируя, что ваша система готова обрабатывать запросы после загрузки.Настройка и настройка. Релизы предоставляют разработчикам тонкую настройку конфигурации системы и флагов VM, используемых для запуска системы.
Автономность. Релизу не требуется включать исходный код в файлы производства. Весь код предварительно скомпилирован и упакован. Релизы даже не требуют Erlang или Elixir на серверах, так как по умолчанию включают Erlang VM и его среду выполнения. Кроме того, стандартные библиотеки Erlang и Elixir сокращены до включения только тех частей, которые вы фактически используете.
Несколько релизов. Вы можете собрать разные релизы с различной конфигурацией на приложение или даже с разными приложениями.
Сценарии управления. Релизы поставляются со скриптами для запуска, перезапуска, удалённого подключения к работающей системе, выполнения вызовов RPC, запуска в режиме демона, установки в виде службы Windows и др.
Запуск релиза
После сборки релиза можно запустить его, вызвав bin/RELEASE_NAME start внутри релиза. В рабочей среде вы бы сделали:
MIX_ENV=prod mix release _build/prod/rel/my_app/bin/my_app start
bin/my_app start запустит систему, подключённую к текущему стандартному вводу/выводу, где по умолчанию также записываются журналы. Это предпочтительный способ запуска системы. Многие инструменты, такие как systemd, платформы как сервис, такие как Heroku, и многие платформы контейнеров, такие как Docker, способны обрабатывать стандартный ввод/вывод и перенаправлять содержимое журналов в другое место. Эти инструменты и платформы также заботятся о перезапуске системы в случае сбоя.
Вы также можете выполнить одноразовые команды, запустить релиз в режиме демона на системах Unix-подобных системах или установить его как службу в Windows. Мы рассмотрим эти варианты далее. Вы также можете перечислить все доступные команды, вызвав bin/RELEASE_NAME.
Одноразовые команды (eval и rpc)
Если вы хотите вызвать определённые модули и функции в своём релизе, вы можете сделать это двумя способами: с помощью eval или rpc.
bin/RELEASE_NAME eval "IO.puts(:hello)" bin/RELEASE_NAME rpc "IO.puts(:hello)"
Команда eval запускает свой экземпляр VM, но без запуска каких-либо приложений в релизе и без запуска распределения. Например, если вам нужно выполнить некоторую подготовительную работу перед запуском фактической системы, например, миграцию базы данных, eval может подойти. Просто имейте в виду, что любое приложение, которое вы можете использовать во время eval, должно быть явно загружено и/или запущено.
Вы можете запустить приложение, вызвав Application.ensure_all_started/1. Однако, если по какой-либо причине вы не можете запустить приложение, возможно, потому что оно будет запускать другие сервисы, которых вам не нужно, вы должны, по крайней мере, загрузить приложение, вызвав Application.load/1. Если вы не загружаете приложение, любые попытки прочитать его среду или конфигурацию могут завершиться ошибкой. Обратите внимание, что если вы запускаете приложение, оно автоматически загружается перед запуском.
Другой способ выполнить команды — с помощью rpc, который подключится к текущей работающей системе и даст ей указание выполнить заданное выражение. Это означает, что вам нужно гарантировать, что система уже запущена, и быть осторожным с инструкциями, которые вы выполняете. Вы также можете использовать remote для подключения удалённой сессии IEx к системе.
Вспомогательный модуль
При работе с вашей системой вы, возможно, довольно часто будете выполнять какой-то код в качестве одноразовой команды. Вы можете рассмотреть возможность создания модуля для группировки этих задач:
# lib/my_app/release_tasks.ex
defmodule MyApp.ReleaseTasks do
def eval_purge_stale_data() do
# Eval commands needs to start the app before
# Or Application.load(:my_app) if you can't start it
Application.ensure_all_started(:my_app)
# Code that purges stale data
...
end
def rpc_print_connected_users() do
# Code that print users connected to the current running system
...
end
end
В приведённом выше примере мы добавили префиксы к именам функций, соответствующим именам команд, используемых для их выполнения, но это необязательно.
И для их выполнения:
bin/RELEASE_NAME eval "MyApp.ReleaseTasks.eval_purge_stale_data()" bin/RELEASE_NAME rpc "MyApp.ReleaseTasks.rpc_print_connected_users()"
Режим демона (Unix-подобные системы)
Вы можете запустить релиз в режиме демона с помощью команды:
bin/RELEASE_NAME daemon_iex
В режиме демона система запускается в фоновом режиме с помощью run_erl. Вы также можете включить heart в режиме демона, чтобы система автоматически перезапускалась в случае сбоев. См. сгенерированный файл releases/RELEASE_VSN/env.sh.
Дэмон будет записывать весь свой стандартный вывод в каталог «tmp/log/» в корне релиза. Вы можете следить за файлом журнала, выполнив tail -f tmp/log/erlang.log.1 или аналогично. После того, как файлы станут слишком большими, суффикс индекса будет увеличен. Разработчик также может подключиться к стандартному вводу демона, вызвав «to_erl tmp/pipe/» из корня релиза. Однако имейте в виду, что подключение к системе должно осуществляться с особой осторожностью, поскольку обычные команды завершения работы Elixir-системы, такие как дважды нажатие Ctrl+C или Ctrl+\, фактически остановят демона. Поэтому использование bin/RELEASE_NAME remote предпочтительнее, даже в режиме демона.
Вы можете настроить каталог tmp, используемый как для ведения журнала, так и для передачи данных в режиме демона, установив переменную среды RELEASE_TMP. См. раздел «Настройка».
Режим служб (Windows)
Хотя демоны недоступны в Windows, с помощью erlsrv можно установить выпущенную систему в качестве службы в Windows. Это можно сделать, выполнив:
bin/RELEASE_NAME install
После установки службу необходимо явно управлять с помощью исполняемого файла erlsrv, который включён в каталог erts-VSN/bin. Служба не запускается автоматически после установки.
Например, если у вас есть релиз с именем demo, вы можете установить службу, а затем запустить её из корня релиза следующим образом:
bin/demo install erts-VSN/bin/erlsrv.exs start demo_demo
Имя службы — demo_demo, поскольку имя формируется путём конкатенации имени узла и имени релиза. Поскольку Elixir автоматически использует одно и то же имя для обоих, служба будет обращаться как demo_demo.
Команда install должна выполняться от имени администратора.
Команды bin/RELEASE_NAME
Следующие команды поддерживаются bin/RELEASE_NAME:
start Starts the system start_iex Starts the system with IEx attached daemon Starts the system as a daemon (Unix-like only) daemon_iex Starts the system as a daemon with IEx attached (Unix-like only) install Installs this system as a Windows service (Windows only) eval "EXPR" Executes the given expression on a new, non-booted system rpc "EXPR" Executes the given expression remotely on the running system remote Connects to the running system via a remote shell restart Restarts the running system via a remote command stop Stops the running system via a remote command pid Prints the operating system PID of the running system via a remote command version Prints the release name and version to be booted
Развёртывания
Требования
Релиз создаётся на хосте — машине, содержащей Erlang, Elixir и все необходимые зависимости для компиляции приложения. Затем релиз развёртывается на целевой системе, которая может быть той же самой машиной, что и хост, но обычно отделена, и часто существует множество целевых систем (как несколько экземпляров, так и развёртывание релиза в разнородных средах).
Для прямого развёртывания с хоста на отдельную целевую систему без кросс-компиляции между хостом и целевой системой должны совпадать следующие параметры:
- Архитектура целевой системы (например, x86_64 или ARM)
- Производитель + операционная система целевой системы (например, Windows, Linux или Darwin/macOS)
- ABI целевой системы (например, musl или gnu)
Это часто представлено в виде тройки целевой системы, например, x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl, x86_64-apple-darwin.
Итак, для прямого развёртывания с хоста на отдельную целевую систему система выполнения Erlang (ERTS) и все зависимости (NIF), должны быть скомпилированы для той же тройки целевой системы. Если вы создаёте на MacBook (x86_64-apple-darwin) и пытаетесь развернуть на типичной машине Ubuntu (x86_64-unknown-linux-gnu) релиз не будет работать. Вместо этого вы должны создать релиз на хосте x86_64-unknown-linux-gnu. Как мы увидим, это можно сделать несколькими способами, например, создать релиз на самой целевой системе или с помощью виртуальных машин или контейнеров, обычно как часть вашей системы доставки релиза.
В дополнение к соответствию тройки целевой системы также важно, чтобы целевая система имела все системные пакеты, которые потребуется приложению во время выполнения. Общим является необходимость OpenSSL при создании приложения, использующего :crypto или :ssl, который динамически связан с ERTS. Другим распространённым источником подобных зависимостей являются зависимости, содержащие NIF (функции, реализованные нативно), которые могут ожидать динамической связи с библиотеками, которые они используют.
Конечно, некоторые операционные системы и менеджеры пакетов могут отличаться между версиями, поэтому, если вашей целью является полная совместимость между хостом и целевой системой, лучше убедиться, что операционная система и менеджер пакетов имеют одинаковые версии на хосте и на целевой системе. Это может быть даже требованием в некоторых системах, особенно в системах с менеджерами пакетов, которые пытаются создавать полностью воспроизводимые среды (Nix, Guix).
Аналогично, при создании автономного пакета и выпуска для Windows, обратите внимание на зависимость системы выполнения Erlang от некоторых библиотек Microsoft (Visual C++ Redistributable Packages для Visual Studio 2013). Эти библиотеки устанавливаются (если их нет) при установке Erlang, но они не входят в стандартную среду Windows. Развертывание автономного выпуска на компьютере без этих библиотек приведет к ошибке при попытке запустить выпуск. Один из способов решения этой проблемы — загрузить и установить эти библиотеки Microsoft при первом развертывании выпуска (версия установщика Erlang 10.6 поставляется с «Microsoft Visual C++ 2013 Redistributable - 12.0.30501»).
В качестве альтернативы, вы также можете объединить скомпилированные объектные файлы в релизе, при условии, что они были скомпилированы для той же целевой платформы. Если вы это сделаете, вам необходимо обновить переменную окружения LD_LIBRARY_PATH с путями, содержащими объединённые объекты, на системах типа Unix, или переменную окружения PATH на системах Windows.
В настоящее время нет официального способа кросс-компиляции выпуска из одной целевой тройки в другую из-за сложности этого процесса.
Техники
Существует несколько способов гарантировать, что выпуск создаётся на хосте с теми же свойствами, что и целевая платформа. Простой вариант — извлечь исходный код, скомпилировать код и собрать выпуск на самой целевой платформе. Это будет примерно так:
git clone remote://path/to/my_app.git my_app_source cd my_app_source mix deps.get --only prod MIX_ENV=prod mix release _build/prod/rel/my_app/bin/my_app start
Если вы предпочитаете, вы также можете скомпилировать выпуск в отдельную директорию, чтобы вы могли удалить весь исходный код после сборки выпуска:
git clone remote://path/to/my_app.git my_app_source cd my_app_source mix deps.get --only prod MIX_ENV=prod mix release --path ../my_app_release cd ../my_app_release rm -rf ../my_app_source bin/my_app start
Однако этот вариант может быть дорогостоящим, если у вас есть несколько узлов производства или если процесс сборки выпуска является длительным, поскольку каждый узел должен индивидуально собрать выпуск.
Вы можете автоматизировать этот процесс несколькими способами. Один вариант — сделать его частью вашей конвейерной системы непрерывной интеграции (CI) / непрерывного развертывания (CD). Когда у вас есть конвейер CI/CD, машины в вашем конвейере CI/CD обычно работают на той же целевой тройке, что и ваши серверы производства (если это не так, то они должны). В этом случае вы можете собрать выпуск в конце вашего конвейера CI/CD, вызвав MIX_ENV=prod mix release и передать артефакт в хранилище S3 или любое другое сетевое хранилище. Для выполнения развертывания ваши производственные машины могут извлечь развертывание из сетевого хранилища и запустить bin/my_app start.
Другой механизм автоматизации развертываний — использование образов, таких как Amazon Machine Images, или платформ контейнеров, таких как Docker. Например, вы можете использовать Docker для запуска локальной системы с той же целевой тройкой, что и ваши серверы производства. Внутри контейнера вы можете вызвать MIX_ENV=prod mix release и создать полный образ и/или контейнер с операционной системой, всеми зависимостями, а также выпусками.
Другими словами, существует множество способов развертывания систем, и выпуска можно автоматизировать и включить во все из них, если вы помните, что нужно создавать систему на той же целевой тройке.
После развертывания системы её можно остановить, отправив SIGINT/SIGTERM в систему, что обычно делают большинство контейнеров, платформ и инструментов, или явным вызовом bin/RELEASE_NAME stop. После получения запроса на остановку каждое приложение и соответствующие деревья надзора будут останавливаться по одному, в обратном порядке, в котором они были запущены.
Настройка
Существует несколько способов, с помощью которых разработчики могут настроить сгенерированные артефакты внутри выпуска.
Параметры
Следующие параметры можно установить в вашем mix.exs в каждом определении выпуска:
-
:applications- список ключевых слов, который настраивает и добавляет новые приложения в выпуск. Ключ — имя приложения, а значение — одно из:-
:permanent- приложение запускается, и узел завершает работу, если приложение завершается по любой причине -
:transient- приложение запускается, и узел завершается, если приложение завершается аварийно -
:temporary- приложение запускается, и узел не завершается, если приложение завершается -
:load- приложение только загружается -
:none- приложение входит в состав выпуска, но не загружается и не запускается. Все приложения по умолчанию:permanent. По умолчанию:applicationsвключает текущее приложение и все приложения, от которых зависит текущее приложение, рекурсивно. Вы можете включить новые приложения или изменить режим существующих, перечислив их здесь. Порядок приложений, указанных в:applications, сохраняется по возможности, с исключительным перечислением:kernel,:stdlib,:sasl, и:elixirперед указанным списком приложений. Выпуски, собранные из проекта-зонтика, требуют явного указания этой настройки.
-
:strip_beams- управляет тем, должны ли файлы BEAM содержать отладочную информацию, фрагменты документации и другую некритическую метаданные. По умолчаниюtrue. Может быть установлено вfalseдля отключения очистки. Также принимает[keep: ["Docs", "Dbgi"]]для сохранения определенных фрагментов, которые обычно удаляются.-
:cookie- строка, представляющая куки Erlang Distribution. Если этот параметр не задан, случайный куки записывается в файлreleases/COOKIEпри первой сборке выпуска. При запуске мы в первую очередь попробуем извлечь куки из переменной средыRELEASE_COOKIE, а затем прочитаем файлreleases/COOKIE.Если вы устанавливаете этот параметр вручную, мы рекомендуем использовать куки в виде длинной случайной строки, например:
Base.url_encode64(:crypto.strong_rand_bytes(40)). Мы также рекомендуем ограничить символы в куки подмножеством, возвращаемымBase.url_encode64/1. :validate_compile_env- по умолчанию выпуск будет сопоставлять все конфигурации выполнения с любой конфигурацией, помеченной во время компиляции в вашем приложении или его зависимостях через функциюApplication.compile_env/3. Если есть несоответствие между ними, это означает, что ваша система неправильно настроена и не может загрузиться. Вы можете отключить эту проверку, установив этот параметр в false.:path- путь, по которому должен быть установлен выпуск. По умолчанию"_build/MIX_ENV/rel/RELEASE_NAME".:version- версия выпуска в виде строки или{:from_app, app_name}. По умолчанию используется текущая версия приложения. Формат{:from_app, app_name}можно использовать для лёгкой ссылки на версию приложения из другого приложения. Это особенно полезно в приложениях-зонтиках.:quiet- логический параметр, который управляет тем, будут ли выпуски записывать шаги в стандартный вывод. По умолчаниюfalse.-
:include_erts- логический, строковый параметр или анонимная функция с нулевой арностью. Если логический параметр, указывает, будет ли система выполнения Erlang (ERTS), включающая виртуальную машину Erlang, включена в выпуск. Значение по умолчаниюtrue, что также является рекомендуемым значением. Если строковый параметр, представляет путь к существующей установке ERTS. Если анонимная функция с нулевой арностью, это функция, которая возвращает любой из вышеперечисленных параметров (логический или строковый).Вы также можете установить этот параметр в
false, если хотите использовать версию ERTS, установленную на целевой платформе. Обратите внимание, что версия ERTS на целевой платформе должна быть точно такой же, как версия ERTS, используемая при сборке выпуска. Установка значения вfalseтакже отключает горячие исправления кода. Поэтому,:include_ertsследует устанавливать вfalseс осторожностью и только если вы собираете выпуск на том же сервере, на котором он работает. -
:include_executables_for- список атомов, определяющих, для каких операционных систем должны быть сгенерированы исполняемые файлы. По умолчанию он установлен в[:unix, :windows]. Вы можете настроить их следующим образом:releases: [ demo: [ include_executables_for: [:unix] # Or [:windows] or [] ] ] :rel_templates_path- путь к файлам шаблонов, которые копируются в выпуск, такие как «vm.args.eex», «env.sh.eex» (или «env.bat.eex») и «overlays». По умолчанию «rel» в корне проекта.:overlays- список каталогов с дополнительными файлами, которые копируются как есть в выпуск. Каталог «overlays» по адресу:rel_templates_pathвсегда включается в этот список по умолчанию (обычно в «rel/overlays»). Дополнительная информация в разделе «Наложения».:steps- список шагов, которые необходимо выполнить при сборке выпуска. Дополнительная информация в разделе «Шаги».
Обратите внимание, что каждое определение выпуска может быть задано как анонимная функция. Это полезно, если некоторые атрибуты выпуска дорогостоящие для вычисления:
releases: [
demo: fn ->
[version: @version <> "+" <> git_ref()]
end
]
Помимо перечисленных выше параметров, можно настроить создаваемый выпуск с помощью пользовательских файлов, настраивая шаги сборки или выполняя пользовательские параметры и команды при запуске. Мы подробно рассмотрим оба подхода ниже.
Наложения
Зачастую необходимо скопировать дополнительные файлы в корень выпуска после его сборки. Это легко сделать, поместив такие файлы в каталог rel/overlays. Любой файл в нём копируется как есть в корень выпуска. Например, если у вас есть файл «rel/overlays/Dockerfile», «Dockerfile» будет скопирован как есть в корень выпуска.
Если вы хотите указать дополнительные каталоги наложений, вы можете сделать это с помощью параметра :overlays. Если вам нужно скопировать файлы динамически, см. раздел «Шаги».
Шаги
Можно добавить один или несколько шагов до и после сборки выпуска. Это можно сделать с помощью параметра :steps.
releases: [
demo: [
steps: [&set_configs/1, :assemble, ©_extra_files/1]
]
]
Параметр :steps должен быть списком, и он всегда должен включать атом :assemble, который выполняет основную работу по сборке выпуска. Вы можете передать анонимные функции до и после :assemble для настройки конвейера сборки выпуска. Эти анонимные функции будут получать структуру Mix.Release и должны возвращать ту же или обновлённую структуру Mix.Release. Также возможно создать архив tarball выпуска, передав шаг :tar в любой момент после :assemble. Если выпуск :path не настроен, архив tarball создаётся в _build/MIX_ENV/RELEASE_NAME-RELEASE_VSN.tar.gz. В противном случае он создаётся внутри настроенного :path.
См. Mix.Release для получения дополнительной информации о структуре и доступных для изменения полях. Обратите внимание, что поле :steps само по себе может быть изменено и обновляется каждый раз при вызове шага. Поэтому, если вам нужно выполнить команду до и после сборки релиза, вам нужно только объявить первые шаги в вашей конвейерной линии, а затем встроить последний шаг в структуру релиза. Поле шагов также можно использовать для проверки, был ли шаг установлен до или после сборки релиза.
vm.args и env.sh (env.bat)
Разработчики могут захотеть настроить флаги виртуальной машины и переменные окружения, предоставляемые при запуске релиза. Это обычно делается путем настройки двух файлов внутри вашего релиза: releases/RELEASE_VSN/vm.args и releases/RELEASE_VSN/env.sh (или env.bat в Windows).
Однако вместо изменения этих файлов после сборки релиза, самый простой способ настроить эти файлы — запустить mix release.init. Задача Mix скопирует настраиваемые rel/vm.args.eex, rel/env.sh.eex, и rel/env.bat.eex файлы в корень вашего проекта. Вы можете изменить эти файлы, и они будут оцениваться каждый раз, когда вы выполняете новый релиз. Эти файлы — обычные EEx-шаблоны, и они имеют единственное присваивание, называемое @release, со структурой Mix.Release.
Файл vm.args может содержать любые флаги виртуальной машины, которые принимаются командой erl.
Файлы env.sh и env.bat используются для установки переменных окружения. Там вы можете установить переменные, такие как RELEASE_NODE, RELEASE_COOKIE, и RELEASE_TMP, чтобы настроить имя узла, куки и временную директорию соответственно. Всякий раз, когда вызывается env.sh или env.bat, переменные RELEASE_ROOT, RELEASE_NAME, RELEASE_VSN, и RELEASE_COMMAND уже будут установлены, поэтому вы можете полагаться на них. См. раздел о переменных окружения для получения дополнительной информации.
Кроме того, в то время как vm.args является статическим, вы можете использовать env.sh и env.bat для динамической настройки параметров виртуальной машины. Например, если вы хотите убедиться, что Erlang Distribution прослушивает только определенный порт, известный во время выполнения, вы можете установить следующее:
case $RELEASE_COMMAND in
start*|daemon*)
ELIXIR_ERL_OPTIONS="-kernel inet_dist_listen_min $BEAM_PORT inet_dist_listen_max $BEAM_PORT"
export ELIXIR_ERL_OPTIONS
;;
*)
;;
esac
Обратите внимание, что мы устанавливаем порт только для команд start/daemon. Если вы также ограничите порт для других команд, таких как rpc, то вы не сможете установить удалённое соединение, так как порт уже будет занят узлом.
В Windows ваш файл env.bat будет выглядеть так:
IF NOT %RELEASE_COMMAND:start=%==%RELEASE_COMMAND% ( set ELIXIR_ERL_OPTIONS="-kernel inet_dist_listen_min %BEAM_PORT% inet_dist_listen_max %BEAM_PORT%" )
Конфигурация приложения
Релизы предоставляют два механизма для настройки приложений OTP: на этапе сборки и на этапе выполнения.
Конфигурация на этапе сборки
Всякий раз, когда вы вызываете команду mix, Mix загружает конфигурацию из файла config/config.exs, если он существует. Обычно файл config/config.exs сам импортирует другие конфигурации на основе текущей MIX_ENV, такие как config/dev.exs, config/test.exs, и config/prod.exs. Мы говорим, что эта конфигурация — конфигурация на этапе сборки, поскольку она оценивается каждый раз при компиляции кода или при сборке релиза.
Другими словами, если ваша конфигурация делает что-то вроде:
config :my_app, :secret_key, System.fetch_env!("MY_APP_SECRET_KEY")
Ключ :secret_key в :my_app будет вычислен на хост-машине при сборке релиза. Установка MY_APP_SECRET_KEY непосредственно перед запуском релиза не повлияет на результат.
К счастью, релизы также предоставляют конфигурацию на этапе выполнения, о которой мы поговорим далее.
Конфигурация на этапе выполнения
Чтобы включить конфигурацию на этапе выполнения в вашем релизе, вам нужно создать файл с именем config/runtime.exs:
import Config
config :my_app, :secret_key, System.fetch_env!("MY_APP_SECRET_KEY")
Этот файл будет выполнен при запуске вашего проекта Mix или вашего релиза.
Ваш файл config/runtime.exs должен следовать трём важным правилам:
- Он ДОЛЖЕН
import Configв верхней части вместо устаревшегоuse Mix.Config - Он НЕ ДОЛЖЕН импортировать другие файлы конфигурации через
import_config - Он НЕ ДОЛЖЕН обращаться к
Mixникоим образом, посколькуMix— инструмент сборки, и он недоступен внутри релизов
Если файл config/runtime.exs существует, он будет скопирован в ваш релиз и выполнен в начале процесса загрузки, когда запущены только Elixir и основные приложения Erlang. После загрузки конфигурации система Erlang будет перезапущена (в рамках того же процесса операционной системы), и новая конфигурация вступит в силу.
Вы можете изменить путь к файлу конфигурации на этапе выполнения, установив :runtime_config_path в каждой конфигурации релиза. Этот путь разрешается на этапе сборки, так как указанный файл конфигурации всегда копируется внутрь релиза:
releases: [
demo: [
runtime_config_path: ...
]
]
Наконец, для корректной работы конфигурации на этапе выполнения (а также любого другого «поставщика конфигурации», как определено далее) необходимо сохранить вычисленную конфигурацию на диске. Вычисленный файл конфигурации будет записан в директорию «tmp» внутри релиза каждый раз при запуске системы. Вы можете настроить директорию «tmp», установив переменную окружения RELEASE_TMP, как явно, так и в файле releases/RELEASE_VSN/env.sh (или env.bat в Windows).
Поставщики конфигурации
Релизы также поддерживают пользовательские механизмы, называемые поставщиками конфигурации, для загрузки любого типа конфигурации на этапе выполнения в систему во время запуска. Например, если вам нужно получить доступ к хранилищу или загрузить конфигурацию из файла JSON, это можно сделать с помощью поставщиков конфигурации. Конфигурация на этапе выполнения, описанная в предыдущем разделе, обрабатывается поставщиком Config.Reader. См. модуль Config.Provider для получения дополнительной информации и примеров.
Следующие параметры могут быть установлены в вашем ключе «releases» в mix.exs для управления работой поставщиков конфигурации:
:reboot_system_after_config— каждый раз, когда ваша система конфигурируется, она перезапускается, чтобы позволить новой конфигурации вступить в силу. Вы можете установить это значение вfalse, чтобы отключить перезагрузку для приложений, чувствительных к времени загрузки, но помните, что в этом случае вы не сможете настроить системные приложения, такие как:kernelи:stdlib. По умолчаниюtrue, если используется устаревшееconfig/releases.exs,falseв противном случае.:prune_runtime_sys_config_after_boot— если:reboot_system_after_configустановлено, каждый раз при запуске системы релиз запишет файл конфигурации в вашу временную директорию. Эти файлы конфигурации обычно небольшие. Но если вы обеспокоены дисковым пространством или у вас есть другие ограничения, вы можете попросить систему удалить эти файлы конфигурации после запуска. Недостаток в том, что вы больше не сможете перезапустить систему внутренне (ни черезSystem.restart/0, ни черезbin/RELEASE_NAME restart). Если вам нужен перезапуск, вам нужно будет завершить процесс операционной системы и запустить новый.:start_distribution_during_config— если:reboot_system_after_configустановлено, релизы запускают функции дистрибуции Erlang VM только после оценки файлов конфигурации. Вы можете установить его вtrue, если вам нужна дистрибуция во время конфигурации. По умолчаниюfalseначиная с Erlang/OTP 22+.:config_providers— список кортежей с пользовательскими поставщиками конфигурации. СмотритеConfig.Providerдля получения дополнительной информации. По умолчанию[].
Резюме по настройке и конфигурации
В целом, доступны следующие файлы для настройки и конфигурации работающей системы:
config/config.exs(иconfig/prod.exs) — обеспечивает конфигурацию приложения на этапе сборки, выполняемую при сборке релизаconfig/runtime.exs— предоставляет конфигурацию приложения на этапе выполнения. Она выполняется каждый раз при запуске вашего проекта Mix или релиза и дополнительно расширяется с помощью поставщиков конфигурации. Если вы хотите определить, находитесь ли вы внутри релиза, вы можете проверить переменные окружения, специфичные для релиза, такие какRELEASE_NODEилиRELEASE_MODErel/vm.args.eex— файл-шаблон, который копируется в каждый релиз и предоставляет статическую конфигурацию виртуальной машины Erlang и других флагов выполненияrel/env.sh.eexиrel/env.bat.eex— файлы-шаблоны, которые копируются в каждый релиз и выполняются при каждой команде для установки переменных окружения, включая специфические для виртуальной машины и общие переменные окружения
Структура каталогов
Релиз организован следующим образом:
bin/
RELEASE_NAME
erts-ERTS_VSN/
lib/
APP_NAME-APP_VSN/
ebin/
include/
priv/
releases/
RELEASE_VSN/
consolidated/
elixir
elixir.bat
env.bat
env.sh
iex
iex.bat
runtime.exs
start.boot
start.script
start_clean.boot
start_clean.script
sys.config
vm.args
COOKIE
start_erl.data
tmp/
Переменные окружения
Система устанавливает различные переменные окружения. Следующие переменные устанавливаются на ранней стадии и могут быть прочитаны только env.sh и env.bat:
RELEASE_ROOT- указывает на корень выпуска. Если система включает ERTS, то это то же самое, что:code.root_dir/0. Эта переменная всегда вычисляется и не может быть установлена в пользовательское значениеRELEASE_COMMAND- команда, данная выпуску, например,"start","remote","eval", и так далее. Обычно к ней обращаются внутриenv.shиenv.batдля установки различных переменных среды в разных условиях. Однако обратите внимание, чтоRELEASE_COMMANDне была проверена к моменту вызоваenv.shиenv.bat, поэтому она может быть пустой или содержать недопустимые значения. Эта переменная всегда вычисляется и не может быть установлена в пользовательское значениеRELEASE_NAME- имя выпуска. Его можно установить в пользовательское значение при вызове выпускаRELEASE_VSN- версия выпуска, в противном случае используется последняя версия. Его можно установить в пользовательское значение при вызове выпуска. Пользовательское значение должно быть существующей версией выпуска в каталогеreleases/
Следующие переменные можно установить перед вызовом выпуска или внутри env.sh и env.bat:
RELEASE_COOKIE- куки выпуска. По умолчанию использует значение вreleases/COOKIE. Его можно установить в пользовательское значениеRELEASE_NODE- имя узла выпуска в форматеname@host. Его можно установить в пользовательское значение. Часть имени должна состоять только из букв, цифр, символов подчеркивания и дефисовRELEASE_SYS_CONFIG- местоположение файла sys.config. Его можно установить в пользовательский путь, и он не должен включать расширение.configRELEASE_VM_ARGS- местоположение файла vm.args. Его можно установить в пользовательский путьRELEASE_TMP- каталог в выпуске для записи временных файлов. Его можно установить в пользовательский каталог. По умолчанию$RELEASE_ROOT/tmpRELEASE_MODE- запуск выпуска в режиме embedded или interactive. По умолчанию «embedded». Применяется только к командам start/daemon/installRELEASE_DISTRIBUTION- способ запуска дистрибутива. Может бытьname(длинные имена),sname(короткие имена) илиnone(дистрибутив не запускается автоматически). По умолчаниюsname, что позволяет получить доступ только внутри текущей системы.nameпозволяет внешние подключения. Если используетсяname, и вы не работаете на Erlang/OTP 22 или более поздних версиях, необходимо установитьRELEASE_NODEвRELEASE_NAME@127.0.0.1с IP-адресом или известным хостомRELEASE_BOOT_SCRIPT- имя скрипта загрузки для запуска выпуска. Этот скрипт используется при выполнении команд, таких какstartиdaemon. Ожидается, что скрипт загрузки будет находиться по путиreleases/RELEASE_VSN/RELEASE_BOOT_SCRIPT.boot. По умолчаниюstartRELEASE_BOOT_SCRIPT_CLEAN- имя скрипта загрузки, используемого при чистом запуске выпуска без вашего приложения или его зависимостей. Этот скрипт используется командами, такими какeval,rpc, иremote. Ожидается, что скрипт загрузки будет находиться по путиreleases/RELEASE_VSN/RELEASE_BOOT_SCRIPT_CLEAN.boot. По умолчаниюstart_clean
Проекты-зонтики
Выпуски хорошо интегрированы с проектами-зонтиками, позволяя вам выпускать один или несколько подмножеств ваших дочерних проектов-зонтиков. Единственное различие между выполнением выпуска в проекте-зонтике по сравнению с обычным приложением заключается в том, что проекты-зонтики требуют от вас явного указания вашего выпуска и точки входа для каждого выпуска. Например, представьте такие приложения-зонтики:
my_app_umbrella/
apps/
my_app_core/
my_app_event_processing/
my_app_web/
где как my_app_event_processing , так и my_app_web зависят от my_app_core, но не зависят друг от друга.
Внутри вашего проекта-зонтика вы можете определить несколько выпусков:
releases: [
web_and_event_processing: [
applications: [
my_app_event_processing: :permanent,
my_app_web: :permanent
]
],
web_only: [
applications: [my_app_web: :permanent]
],
event_processing_only: [
applications: [my_app_event_processing: :permanent]
]
]
Обратите внимание, что вам не нужно определять все приложения в :applications, только точки входа. Также помните, что рекомендуемый режим для всех приложений в системе — :permanent.
Наконец, имейте в виду, что не обязательно собирать выпуск из корня проекта-зонтика. Вы также можете собрать выпуск из каждого дочернего приложения индивидуально. Однако сборка из корня позволяет включить в один выпуск два приложения, которые не зависят друг от друга.
Горячие обновления кода
Erlang и Elixir иногда известны возможностью обновления узла, работающего в режиме производства, без остановки этого узла. Однако эта функция не поддерживается из коробки выпусками Elixir.
Причина, по которой мы не предоставляем горячие обновления кода, заключается в том, что они очень сложны для выполнения на практике, так как требуют тщательного кодирования ваших процессов и приложений, а также обширного тестирования. Учитывая, что большинство команд могут использовать другие методы, не зависящие от языка, для обновления своих систем, такие как развертывания «синий/зелёный», развертывания «канарейки», пошаговые развертывания и другие, горячие обновления редко являются жизнеспособным вариантом. Давайте разберёмся, почему.
При горячем обновлении кода вы хотите обновить узел с версии A на версию B. Для этого первым шагом является написание рецептов для каждого приложения, которое изменилось между этими двумя выпусками, точно описывая, как приложение изменилось между версиями; эти рецепты называются файлами .appup. Хотя некоторые шаги по созданию файлов .appup можно автоматизировать, не все из них. Кроме того, каждый процесс в приложении необходимо явно запрограммировать с учётом горячих обновлений кода. Посмотрим пример. Представьте, что ваше приложение имеет процесс счётчика как GenServer:
defmodule Counter do
use GenServer
def start_link(_) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
def bump do
GenServer.call(__MODULE__, :bump)
end
## Callbacks
def init(:ok) do
{:ok, 0}
end
def handle_call(:bump, counter) do
{:reply, :ok, counter + 1}
end
end
Вы добавляете этот процесс в вашу структуру управления и выпускаете версию 0.1.0 вашей системы. Теперь представим, что в версии 0.2.0 вы внесли два изменения: вместо bump/0, которая всегда увеличивает счётчик на единицу, вы вводите bump/1, которая передает точное значение для увеличения счётчика. Вы также изменяете состояние, поскольку хотите сохранить максимальное значение увеличения:
defmodule Counter do
use GenServer
def start_link(_) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
def bump(by) do
GenServer.call(__MODULE__, {:bump, by})
end
## Callbacks
def init(:ok) do
{:ok, {0, 0}}
end
def handle_call({:bump, by}, {counter, max}) do
{:reply, :ok, {counter + by, max(max, by)}}
end
end
Если вы выполните горячее обновление кода в таком приложении, оно аварийно завершит работу, потому что в исходной версии состояние было просто счётчиком, а в новой версии состояние является кортежем. Кроме того, вы изменили формат сообщения call с :bump на {:bump, by}, и процесс может временно смешать и старые, и новые сообщения, поэтому нам нужно обработать оба типа. Окончательная версия будет:
defmodule Counter do
use GenServer
def start_link(_) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
def bump(by) do
GenServer.call(__MODULE__, {:bump, by})
end
## Callbacks
def init(:ok) do
{:ok, {0, 0}}
end
def handle_call(:bump, {counter, max}) do
{:reply, :ok, {counter + 1, max(max, 1)}}
end
def handle_call({:bump, by}, {counter, max}) do
{:reply, :ok, {counter + by, max(max, by)}}
end
def code_change(_, counter, _) do
{:ok, {counter, 0}}
end
end
Теперь вы можете перечислить этот процесс в файле .appup и выполнить горячее обновление кода. Это лишь один из многих шагов, необходимых для выполнения горячих обновлений кода, и его необходимо учитывать для каждого процесса и приложения, обновляемого в системе. .appup руководство предоставляет хорошую ссылку и больше примеров.
После создания файлов .appup следующим шагом является создание файла .relup со всеми инструкциями, необходимыми для обновления самого выпуска. Документация Erlang предоставляет главу о Создание и обновление целевой системы. Learn You Some Erlang содержит главу о горячих обновлениях кода.
В целом, существует множество шагов, сложностей и предположений, связанных с горячими обновлениями кода, что в конечном счёте и является причиной, по которой они не предоставляются в Elixir из коробки. Тем не менее, горячие обновления кода всё ещё могут быть реализованы командами, которые хотят реализовать эти шаги поверх mix release в своих проектах или в качестве отдельных библиотек.
Опции командной строки
-
--force- принудительно перекомпилировать -
--no-archives-check- не проверять архив -
--no-deps-check- не проверять зависимости -
--no-elixir-version-check- не проверять версию Elixir -
--no-compile- не компилировать перед сборкой выпуска -
--overwrite- если существующая версия выпуска есть, перезаписать её -
--path- путь к выпуску -
--quiet- не писать информацию о прогрессе в стандартный вывод -
--version- версия выпуска
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/mix/1.11.2/Mix.Tasks.Release.html