一、为什么要打包Erlang代码

当你写完一个Erlang应用后,总不能每次都手动启动虚拟机、加载模块吧?我们需要一种标准化的方式,把代码、依赖和配置打包成一个完整的发布包,方便部署到任何环境。这就是Relx的用武之地——它能把你的Erlang项目变成开箱即用的独立包。

举个例子,假设你开发了一个简单的TCP服务器:

%% 技术栈:Erlang/OTP 25 + Relx 4.0
-module(my_tcp_server).
-behaviour(gen_server).

-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2]).

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

init(_Args) ->
    {ok, Port} = application:get_env(my_app, tcp_port),
    {ok, ListenSocket} = gen_tcp:listen(Port, [binary, {reuseaddr, true}]),
    spawn_link(fun() -> acceptor(ListenSocket) end),
    {ok, #{socket => ListenSocket}}.

acceptor(ListenSocket) ->
    {ok, Socket} = gen_tcp:accept(ListenSocket),
    spawn(fun() -> handle_client(Socket) end),
    acceptor(ListenSocket).

这样的代码如果直接扔到服务器上运行,光是处理依赖就会让人头疼。

二、Relx基础配置

Relx通过relx.config文件定义打包规则。先看一个最小化配置:

%% relx.config
{release, {my_app, "1.0.0"}, [my_app, sasl]}.  % 包含主应用和SASL工具库
{dev_mode, false}.                             % 生产环境模式
{include_erts, true}.                          % 嵌入Erlang运行时

关键参数说明:

  • include_erts:打包时带上Erlang虚拟机,这样目标机器就不需要预装Erlang
  • extended_start_script:生成高级启动脚本,支持热升级等操作
  • sys_config:指定系统配置文件路径

进阶示例添加依赖管理:

{release, {my_app, "1.0.0"}, [
    my_app,
    cowboy,       % HTTP服务依赖
    jsone         % JSON处理库
]}.
{overlay_vars, "vars.config"}.  % 动态变量文件

三、实战打包流程

假设项目结构如下:

my_app/
├── ebin/
├── src/
├── priv/
└── rel/         # Relx输出目录

具体操作步骤:

  1. 编译项目:
rebar3 compile
  1. 生成发布包:
rebar3 release
  1. 打包成tar.gz(适合服务器部署):
rebar3 tar

生成的发布包包含完整目录结构:

_build/prod/rel/my_app/
├── bin/        # 启动脚本
├── erts-12.3/  # Erlang运行时
├── lib/        # 所有依赖库
└── releases/   # 版本元数据

四、高级技巧与陷阱规避

4.1 环境变量注入

通过vm.argssys.config实现多环境配置:

%% vm.args(JVM参数风格)
-name my_app@127.0.0.1
-setcookie secret
-kernel inet_dist_listen_min 9100
%% sys.config(Erlang术语格式)
[
    {my_app, [
        {tcp_port, 5566},
        {max_connections, 1000}
    ]},
    {kernel, [
        {start_timer, true}
    ]}
].

4.2 常见问题处理

  1. 依赖冲突:用rebar3 tree查看依赖树
  2. 路径问题{lib_dirs, ["../deps"]}指定非标准路径
  3. 版本锁定:在rebar.lock中固定依赖版本

4.3 热升级实战

假设要从1.0升级到1.1:

# 生成升级包
rebar3 release -n my_app -v 1.1.0 --upfrom 1.0.0

# 部署命令
_build/prod/rel/my_app/bin/my_app upgrade "1.1.0"

五、为什么选择Relx

优势亮点:

  • 零配置依赖:自动解析rebar.config中的依赖
  • 跨平台:Windows/Linux/macOS输出一致
  • 轻量化:基础包通常小于30MB

对比其他工具:

工具 优点 缺点
Rebar2 兼容老项目 已停止维护
Mix Elixir生态原生支持 纯Erlang项目支持有限

适用场景:

  • 需要交付给客户的闭源项目
  • 容器化部署(配合Docker更佳)
  • 嵌入式设备等无网络环境

六、总结与最佳实践

  1. 版本控制:始终在relx.config中指定版本号
  2. 安全隔离:生产环境务必禁用dev_mode
  3. 备份策略:保留旧版本包以便回滚
  4. 监控集成:通过-config sys.config加载监控配置

完整生产级配置示例:

{release, {my_app, "1.0.0"}, [
    my_app,
    sasl,
    observer  % 监控工具
]}.
{include_erts, true}.
{extended_start_script, true}.
{overlay, [
    {mkdir, "log"},
    {copy, "config/custom.vm.args", "releases/{{release_version}}/vm.args"}
]}.

记住:好的发布包应该像家电一样——插电即用,不需要用户看说明书。