到底什么是发行版和目标系统?

发行版是启动 Erlang VM 和启动项目所需的一组应用程序。通过用于生成 .script.boot 的发行版资源文件(.rel)描述它。启动文件是二进制形式的脚本文件,Erlang 运行时系统(ERTS)使用其启动 Erlang 节点,有点像启动操作系统。甚至在命令行上运行 erl 也是使用启动脚本。

目标系统是能够在其它机器上(虚拟机或其它)启动的 Erlang 系统。ERTS 通常与目标系统捆绑在一起。

如欲了解更多信息,请参阅 Adopting Erlang 中关于发行版的章节


1. 开始

向项目的 rebar.config 添加 relx 部分:

运行 rebar3 release 将构建发行版,在 _build/<profile>/rel/<release name>/bin/<release name> 下面提供用于启动节点的脚本。

<release_name> 必须是原子,<release_vsn> 可以是下面的任意一个:

版本类型结果
string()使用字符串作为版本。比如 "0.1.0"
git | semver使用存储库上最新的 git 标签构造版本
{cmd, string()}使用在 Shell 中执行 string() 的内容的结果。比如使用文件 VERSION{cmd, "cat VERSION | tr -d '[:space:]'"}
{git, short | long}使用当前提交的简短的(8 个字符)或完整的 Git 引用
{file, File}使用文件的内容。比如,比使用 cmd 更好的使用 VERSION 文件的方式是:{file, "VERSION"}

每个 <app> 是原子的应用程序名称(比如,myapp),或元组。关于 <app> 的元组语法,请查看 App Syntax

可以向项目的 rebar.config 中的 relx 下添加多个 release 部分。

可以指定共享相同配置的不同发行版:

也可以通过使用 4 元组 release 定义的方式,指定具有独立配置的发行版:

可以使用 rebar3 release -n <release_name> 构建指定的发行版。


2. 构建配置

2.1. App 语法

在发行版中,每个 <app> 是原子的应用程序名称,或具有应用程序名称和额外选项的元组:

在这个例子中,发行版中包含 myapp,其版本是 "1.1.0",其启动类型是 transient,其包含的应用程序列表是 [otherapp]。这些选项直接对应于发行版资源文件(release resource file (.rel))。

2.2. 在发行版中包含源代码

默认情况下,发行版包含应用程序的源文件(如果存在的话)。

如果不希望包含源文件,那么将 include_src 设置为 false。

2.3. 应用程序排除

下面的指令从输出的发行版中移除指定的应用程序。

在发行版中,应用程序将被从任何应用程序的 .app 文件中移除,它们被列在 applications 下。

2.4. 模块排除

下面的指令从输出的发行版中移除应用程序模块。

模块将被从应用程序的 .app 文件的 module 列表中移除。

2.5. 模式

其它模式包括 prodminimal

模式扩展选项
dev[{dev_mode, true}, {include_src, true}, {debug_info, keep}, {include_erts, false}]
prod[{include_src, false}, {debug_info, strip}, {include_erts, true}, {dev_mode, false}]
minimal[{include_src, false}, {debug_info, strip}, {include_erts, false}, {dev_mode, false}]

在开发过程中,可能希望对应用程序的所有更改在发行版中立即可用。relx 提供多种模式,包括用于该特定使用场景的模式 dev。它创建符号链接,而非将组成发行版的应用程序拷贝到发行版结构中,因此编译、重新启动或加载更改的模块就已足够。

prod 模式增加常用于生产版本的选项:include_srcfalse,以便发行版中不包含源码;debug_info 被设置为 strip,使得 BEAM 文件更小,并且仅移除发行版中极可能不包含的工具使用的数据;include_erts 被设置为 true,在当前 Erlang 运行时进行捆绑,生成可以拷贝到兼容的目标系统,并且无需先安装 Erlang 即可运行的发行版。

Rebar3 Prod Profile

当在 rebar3 prod profile 中构建时,比如使用 rebar3 as prod release,将自动启用 relx prod 模式。

minimal 模式与 prod 的唯一不同是,它不包含 Erlang 运行时。

可以通过包含显式设置的方式,重写模式添加的选项。比如,如果想在 BEAM 模块中保留调试信息,那么可以使用如下配置:


3. 验证检查

3.1. 缺少函数

默认情况下,relx 将检查发行版包含的项目应用程序的模块中使用的外部函数是否存在。这意味着如果调用的函数未被包含在发行版中,将给出警告,即使它是 rebar.config 中的依赖或 OTP 中包含的应用程序。

这有助于避免一个常见错误,即忘记将应用程序添加到依赖它的应用程序的 .app.src 中。

如果出于某种原因,希望禁用该检查,那么在 relx 配置中将其设置为 false:

3.2. 过时的模块

选项 src_tests 在模块的源代码缺失或比目标代码更新时,将发出警告:

这对捕获依赖源文件的任何修改非常有用。因为 rebar3 release 将自动编译项目中应用程序的所有更改,依赖应该是唯一可能过时的模块。


4. 运行时配置

4.1. VM 配置

默认情况下,relx 提供设置节点名称和 Cookie 的基础 vm.args 文件。如欲获取选项及其用法的完整列表,请参阅 Erlang documentation

为提供自定义的 vm.argsvm.args.src,只需在项目根目录的顶级 config/ 目录中创建该文件。如果将其命名为 vm.argsvm.args.src 之外的名称,那么必须添加到 relx 配置:

4.2. 应用程序配置

通过 sys.configsys.config.src,在发布时,传递应用程序配置:

如果项目中存在文件 config/sys.config.srcconfig/sys.config,那么 relx 将自动地在发行版中包含其中之一(如果两者都存在,那么 .src 优先)。

为将指定文件设置为应用程序配置文件,可以设置 sys_configsys_config_src

被包含进发行版时,该文件将被重命名为 sys.configsys.config.src

如果都不存在,那么使用带有空列表的文件。

config docssystools docs 中,阅读关于 Erlang 配置的更多信息。

4.3. 环境变量替换

OTP-21+ 和 Rebar3 3.6+

从 Erlang/OTP 21 和 Rebar3 3.6.0 开始,可以使用配置选项 sys_config_srcvm_args_src 显式地包含运行时渲染的模板,将定义为 ${VARIABLE} 的变量替换为 Shell 环境中的等价值。从 Rebar3 3.14.0 开始,在使用变量时,可以通过将变量定义为 ${VARIABLE:-DEFAULT} 的方式,设置默认值。

从 Rebar3 3.14.0 开始,如果配置文件存在,那么将包含配置,因此仅当文件未被命名为 config/sys.config.srcconfig/vm.args.src 时,才需要在 relx 配置中包含 {sys_config_src, <filename>}{vm_args_src, <filename>}

当使用 .src 文件进行配置时,无需设置 RELX_REPLACE_OS_VARS=true

4.4. 覆盖:构建时配置

覆盖提供定义包含在目标系统中的文件和模板的能力。例如,用于管理节点的自定义脚本或在 Heroku 上运行所需的 Procfile。

支持的操作包括:

Relx 的模版暴露 Mustache 模版系统(查看 mustache)的变量以及全部能力。查看文档,获取支持的全部语法。

在默认情况下,有一组变量可用,下一个会话将描述它们,另外可以在 {overlay_vars, "vars.config"} 指定的文件中声明自定义变量,其形式如下:

默认变量的定义如下:

预定义覆盖变量

名称描述
log<logname>:<loglevel> 格式的当前日志级别
output_dir构建的发行版的当前输出目录
target_diroutput_dir 相同;为向后兼容而存在
overridden被重写的应用程序的当前列表(应用程序名称列表)
goals系统中用户指定的目标列表
lib_dirs库目录的列表,包括用户指定的和派生的
config_file系统中使用的配置文件列表
providers用于此 relx 运行的提供者名称列表
sys_configsys.config 文件的位置
root_dir当前项目的根目录
default_release_name用于 relx 运行的当前默认发行版名称
default_release_version用于 relx 运行的当前默认发行版版本
default_release用于 relx 运行的当前默认发行版
release_erts_version使用中的 Erlang 运行时系统的版本
erts_vsnrelease_erts_version 相同(为向后兼容)
release_name当前正在执行的发行版
release_version当前正在执行的版本
rel_vsnrelease_version 相同。为向后兼容而存在
release_applications发行版中包含的应用程序列表
4.4.1. 拆分配置

可以拆分覆盖文件,以处理更复杂的场景。接下来看一个简化的示例。

在构建应用程序时,我们希望通过拼写为 "prod""dev" 的覆盖变量 build 区分生产构建和开发构建,因此 app.confg 文件应该在其配置中包含该变量,我们可以开启或禁用特性。

为此,我们构建三个覆盖文件:

对于开发构建,使用 dev.config 作为 overlay_vars;对于生产,使用 prod.config


5. 可部署的 Tar 包

5.1. 包含 ERTS

目标系统不能有像使用 dev_mode 时那样创建的符号连接,并且通常希望在系统中包含 ERTS,以便不需要预先在目标机器上安装它。

如果使用 prod profile 构建发行版,那么 Rebar3 将自动地将 {mod, prod} 添加到 Relx 配置。比如:

可以将 Tar 包 myrel-0.1.0.tar.gz 拷贝到其它兼容的系统及启动:

5.2. 不带 ERTS

当需要将 ERTS 排除在发行版之外时,可以在 rebar.config 中的 profile 下设置 prod profile 配置。比如,如果想使用目标机器上的 ERTS 和基础应用程序(比如 kernalstdlib),那么在 relx 配置元组中,设置 modeminimalsystem_libsfalse

或手动地设置 include_ertsfalse

当运行 rebar3 as prod tar 时,生成的 Tar 包将不包含 ERTS 或 kernalstdlib 之类的应用程序。

5.3. 带为其它系统构建的 ERTS

如果希望包含用于运行 rebar3 的版本之外的 Erlang 运行时系统,比如在 macOS 上构建,但希望包含为 GNU/Linux 版本构建的 ERTS,那么在 relx 配置元组中,为 include_erts 提供路径,而非布尔值,同时为 system_libs 提供路径:

使用这些路径与 Profile 可以更容易地设置交叉编译。


6. 扩展启动脚本

6.1. 命令

relx 附带的扩展启动脚本提供若干种启动和连接发行版的方式。

对于本地开发,可以使用 console。在生产环境中,无论是在 tmux 之类的工具中手动启动,还是使用 systemd 之类的 init 系统,或者在 Docker 容器中运行发行版,都需要 foreground

如果想在使用 foreground 启动的节点上打开控制台,那么使用 remote_console

命令的完整列表如下:

命令描述
foreground启动发行版,输出到标准输出
remote将远程 Shell 连接到正在运行的节点
console使用交互式 Shell 启动发行版
console_clean启动交互式 Shell,但不启动发行版的应用程序
rpc [Mod [Fun [Args]]]]在正在运行的节点上,运行 apply(Mod, Fun, Args)
eval [Exprs]在正在运行的节点上,运行表达式
status验证节点是否正在运行,然后运行状态钩子脚本
restart重启应用程序,但不重启 VM
reboot重启整个 VM
stop停止正在运行的节点
pid打印 OS 进程的 PID
ping如果节点活着,那么打印 pong
daemon使用 run_erl(命名管道)在后台启动发行版
daemon_attach使用 to_erl(命名管道)连接到以 Daemon 方式启动的节点

6.2. 发行版处理:安装和升级

此外,扩展启动脚本包含使用 release_handler 的命令:

命令描述
unpack [Version]解开发行版 Tar 包
install [Version]安装发行版
uninstall [Version]卸载发行版
upgrade [Version]将正在运行的发行版升级到新版本
downgrade [Version]将正在运行的发行版降级到新版本
versions打印可用的发行版版本

如欲理解上述命令的工作方式,请查看 OTP 设计原则章节 Release Handling

对于详细的工作流程,包括版本增量和应用程序更新,请查看 Richard Jones 围绕 rebar3 构建的 relflow 工具。

对于发行版安装后的基本版本升级,假设我们有一个名为 myrel 的发行版,它有版本 0.0.10.0.2

6.3. 钩子

可以在扩展启动脚本的特定操作上定义钩子,操作包括 startstopinstall_upgrade;可以为每个操作定义 prepost 钩子。

钩子是内建的(发行版中已经包含它们)或自定义的(用户为自定义功能编写的脚本)。提供预打包功能的内建脚本包括:

6.4. 扩展

生成的扩展启动脚本附带一组用于管理发行版的内建命令:foregroundstoprestart 等。

有时,暴露一些特定于应用程序的定制命令很有用。比如,如果正在运行一个游戏服务器,那么调用 bin/gameserver games 输出有用的信息很方便。

扩展启动脚本扩展支持创建附加到启动脚本的可用命令列表的自定义 Shell 脚本。扩展 Shell 脚本可以接受参数,并且可以访问启动脚本自身中定义的所有 Shell 变量。以在 rebar.config 中定义扩展开始,比如:

这里是正在添加的 status 脚本扩展,它将调用 extensions/status Shell 脚本。

该路径相对于生成的发行版中的启动脚本的位置,可以使用 overlay 将它放在正确的位置:

扩展脚本本身是标准的 Shell 脚本,可以以如下方式实现上面描述的游戏服务器示例:


7. 其它配置

可以在运行发行版的目标系统上设置 RELX_RPC_TIMEOUT 环境变量值,以选择脚本在放弃与正在运行的 Erlang 系统的联系之前可以等待多长时间。如果未指定值,那么默认为 NODETOOL_TIMEOUT 的值(从毫秒转换为秒)。如果未设置 NODETOOL_TIMEOUT 本身,那么默认为 60 秒。