一、为什么需要OTP release打包

当你用Erlang开发完一个应用后,总不能每次都手动启动一堆进程、加载配置吧?尤其是在生产环境,手动操作既容易出错,又难以维护。这时候,OTP release打包就派上用场了。

简单来说,OTP release就是把你的Erlang应用、依赖库、运行时环境打包成一个完整的、可独立运行的包。这样部署时只需要解压、运行,不需要关心复杂的依赖关系。

举个例子,假设我们有个简单的Erlang应用:

%% 技术栈: Erlang/OTP 25
%% 文件: my_app/src/my_app.erl
-module(my_app).
-behaviour(application).

-export([start/2, stop/1]).

start(_Type, _Args) ->
    io:format("启动my_app...~n"),
    my_app_sup:start_link().

stop(_State) ->
    io:format("停止my_app...~n").

如果没有release打包,每次启动都得手动确保所有依赖正确加载。而用release后,这些全自动搞定。

二、从零开始创建release

创建一个完整的release需要几个关键步骤:

  1. 准备项目结构
    标准的OTP应用目录结构是这样的:

    my_app/
    ├── rel/              # release配置目录
    ├── src/              # 源代码
    │   ├── my_app.app.src  # 应用元数据
    │   └── ...           # 其他源文件
    └── rebar.config      # 构建配置
    
  2. 配置rebar3
    在rebar.config中添加release配置:

%% rebar.config
{erl_opts, [debug_info]}.
{deps, []}.

{profiles, [
    {prod, [
        {relx, [
            {release, {my_app, "1.0.0"}, [my_app, sasl]},
            {sys_config, "./config/sys.config"},
            {vm_args, "./config/vm.args"},
            {dev_mode, false},
            {include_erts, true}  % 包含Erlang运行时
        ]}
    ]}
]}.
  1. 生成release
    运行命令:
    rebar3 as prod release
    
    这会生成一个完整的release包,包含Erlang运行时、你的应用和所有依赖。

三、多环境配置管理实战

开发、测试、生产环境的配置通常不同。OTP release通过sys.config解决这个问题。

  1. 创建不同环境的配置

    config/
    ├── dev.sys.config    # 开发配置
    ├── prod.sys.config   # 生产配置
    └── sys.config        # 默认配置
    
  2. 示例配置内容

%% prod.sys.config
[
    {my_app, [
        {db_host, "prod.db.example.com"},
        {max_connections, 100}
    ]},
    {kernel, [
        {logger_level, warning}
    ]}
].
  1. 构建时指定配置
    修改rebar.config:
{relx, [
    {sys_config, "./config/${RELX_SYS_CONFIG}.config"}  % 动态加载
]}

然后通过环境变量指定配置:

RELX_SYS_CONFIG=prod rebar3 as prod release

四、版本回滚的两种实用方案

发布后发现问题怎么办?回滚!这里介绍两种可靠方法。

方案1:使用release的旧版本

Erlang release自带版本管理功能。假设我们有两个版本:

$ ls releases/
1.0.0/  1.0.1/

回滚到1.0.0:

bin/my_app downgrade 1.0.0

方案2:热代码降级

如果只是某个模块有问题,可以单独降级:

%% 查看当前版本
code:which(my_module).

%% 加载旧版beam文件
code:load_file(my_module).

五、自动化发布流程设计

手动发布容易出错,我们需要自动化流程。以下是基于Shell脚本的示例:

#!/bin/bash
# 部署脚本示例

REL="1.0.0"
TARGET_DIR="/opt/my_app"

# 1. 上传release包
scp _build/prod/rel/my_app/my_app-$REL.tar.gz server:$TARGET_DIR

# 2. 解压并切换版本
ssh server <<EOF
  cd $TARGET_DIR
  tar -xzf my_app-$REL.tar.gz
  bin/my_app upgrade $REL
EOF

更复杂的场景可以用Ansible或Docker,但核心逻辑不变:传输包→解压→切换版本。

六、常见问题与解决方案

问题1:启动时报缺失依赖
➤ 检查rebar.config是否包含所有依赖
➤ 确保include_erts设为true

问题2:配置不生效
➤ 检查sys.config语法是否正确
➤ 确认配置文件路径被正确指定

问题3:回滚后仍报错
➤ 可能依赖的库版本不兼容
➤ 建议回滚整个release而非单个应用

七、什么时候该用OTP release?

适用场景

  • 需要部署到多台服务器
  • 要求稳定的版本控制
  • 环境配置差异大

不适用场景

  • 快速原型开发
  • 单次执行的脚本类应用

八、技术方案对比

方案 优点 缺点
手动启动 简单直接 难以维护
OTP release 完整可靠 学习曲线陡峭
Docker打包 环境隔离 镜像体积较大

九、总结与最佳实践

  1. 保持release纯净:不要在生产环境手动修改代码
  2. 版本命名规范:建议使用语义化版本(如1.0.0)
  3. 自动化测试:在CI流程中加入release打包测试
  4. 监控:对release运行状态进行监控

最后附上一个完整的部署命令示例:

# 全新安装
tar -xzf my_app-1.0.0.tar.gz
bin/my_app start

# 升级版本
bin/my_app upgrade 1.0.1