Erlang/OTP 完全指南 / 19 - 发布与部署
第 19 章:发布与部署 — relx、热代码升级、版本管理
本章学习如何将 Erlang 应用打包为可部署的 Release,以及如何实现热代码升级(Hot Code Upgrade)。
19.1 Release 概念
19.1.1 什么是 Release?
Release 是一个自包含的软件包,包含:
- 应用代码
- 依赖库
- Erlang 运行时系统(ERTS)
- 配置文件
- 启动脚本
_release/
└── myapp/
├── bin/
│ ├── myapp ← 启动脚本
│ └── myapp.bat ← Windows 启动脚本
├── erts-14.0/ ← Erlang 运行时
├── lib/
│ ├── myapp-0.1.0/ ← 应用代码
│ ├── cowboy-2.12.0/ ← 依赖
│ └── stdlib-5.2/ ← 标准库
├── releases/
│ └── 0.1.0/
│ ├── sys.config ← 应用配置
│ ├── vm.args ← VM 参数
│ └── myapp.rel ← Release 规范
└── log/
19.1.2 为什么需要 Release?
| 优势 | 说明 |
|---|---|
| 自包含 | 无需在目标机器安装 Erlang |
| 版本化 | 可管理多个版本 |
| 热升级 | 运行时替换代码 |
| 可靠 | OTP 监督树在 Release 模式下自动启动 |
19.2 rebar3 Release
19.2.1 rebar.config 配置
%% rebar.config
{relx, [
{release, {myapp, "0.1.0"}, [
myapp, %% 你的应用
sasl, %% SASL(系统日志)
cowboy, %% 依赖
jsx
]},
{dev_mode, true}, %% 开发模式(符号链接)
{include_erts, false}, %% 不包含 ERTS(本地有 Erlang)
{sys_config, "config/sys.config"},
{vm_args, "config/vm.args"},
%% 生产配置覆盖
{extended_start_script, true} %% 生成 start/stop/attach 脚本
]}.
{profiles, [
{prod, [
{relx, [
{dev_mode, false},
{include_erts, true}
]}
]}
]}.
19.2.2 构建命令
# 构建 Release
rebar3 release
# 使用生产配置构建
rebar3 as prod release
# 生成 tar 包(用于部署)
rebar3 as prod tar
# 生成 OTP upgrade package
rebar3 relup
# 生成 overlay(模板文件)
rebar3 release --overlay_vars config/vars.config
19.2.3 VM 参数配置
%% config/vm.args
-name myapp@127.0.0.1
-setcookie myapp_secret_cookie
## 性能调优
+P 1048576 %% 最大进程数
+Q 65536 %% 最大端口数
+A 128 %% 异步线程数
## 垃圾回收
+MBas aobf
+MBacul 0
## SMP
-smp enable
19.2.4 应用配置
%% config/sys.config
[
{myapp, [
{http_port, 8080},
{db_host, "localhost"},
{db_port, 5432},
{pool_size, 10}
]},
{sasl, [
{sasl_error_logger, {file, "log/sasl-error.log"}},
{errlog_type, error}
]}
].
19.3 运行 Release
# 前台运行(调试)
_build/prod/rel/myapp/bin/myapp foreground
# 后台运行(生产)
_build/prod/rel/myapp/bin/myapp start
# 停止
_build/prod/rel/myapp/bin/myapp stop
# 附加到运行中的节点
_build/prod/rel/myapp/bin/myapp attach
# 重启
_build/prod/rel/myapp/bin/myapp restart
# 版本升级
_build/prod/rel/myapp/bin/myapp upgrade "0.2.0"
# 控制台模式(可交互)
_build/prod/rel/myapp/bin/myapp console
19.4 热代码升级
19.4.1 原理
Erlang 的热代码升级允许在不停机的情况下替换运行中的代码:
版本 0.1.0 → 版本 0.2.0
1. 加载新版本的模块代码
2. 调用 upgrade 回调
3. 切换到新版本代码
4. 继续处理请求
19.4.2 配置热升级
%% rebar.config - 配置 relup
{relx, [
{release, {myapp, "0.2.0"}, [myapp, sasl]},
{relup, true}
]}.
19.4.3 code_change 回调
%% GenServer 的 code_change 回调
-module(my_server).
-behaviour(gen_server).
%% 旧版本状态 → 新版本状态
code_change({down, "0.2.0"}, NewState, _Extra) ->
%% 降级:从新状态转为旧状态
{ok, downgrade_state(NewState)};
code_change("0.1.0", OldState, _Extra) ->
%% 升级:从旧状态转为新状态
{ok, upgrade_state(OldState)};
code_change(_Vsn, State, _Extra) ->
{ok, State}.
%% 状态转换函数
upgrade_state(#{name := Name, age := Age}) ->
#{name => Name, age => Age, email => "default@example.com"}.
downgrade_state(#{name := Name, age := Age, email := _Email}) ->
#{name => Name, age => Age}.
19.4.4 执行热升级
# 构建新版本
rebar3 as prod release
# 生成升级包
rebar3 relup
rebar3 as prod tar
# 在运行中的节点上执行升级
bin/myapp upgrade "0.2.0"
# 如果失败,可以降级
bin/myapp downgrade "0.1.0"
19.5 版本管理
19.5.1 语义化版本
主版本号.次版本号.修订号
例如:1.2.3
主版本号:不兼容的 API 变更
次版本号:向后兼容的功能新增
修订号:向后兼容的问题修复
19.5.2 应用版本 vs Release 版本
%% src/myapp.app.src - 应用版本
{application, myapp, [
{vsn, "0.1.0"} %% 应用自身版本
]}.
%% rebar.config - Release 版本
{relx, [
{release, {myapp, "1.0.0"}, [myapp, ...]} %% Release 整体版本
]}.
19.6 实战:完整发布流程
19.6.1 开发到生产流程
# 1. 开发
rebar3 shell
# 2. 运行测试
rebar3 eunit
rebar3 ct
# 3. 类型检查
rebar3 dialyzer
# 4. 构建 Release
rebar3 as prod release
# 5. 打包
rebar3 as prod tar
# 6. 部署到服务器
scp _build/prod/rel/myapp/myapp-0.1.0.tar.gz server:/opt/
# 7. 在服务器上解压和启动
tar xzf myapp-0.1.0.tar.gz
bin/myapp start
19.6.2 升级流程
# 1. 修改代码,更新版本号
# 2. 构建新版本
rebar3 as prod release
# 3. 生成升级包
rebar3 relup
# 4. 打包
rebar3 as prod tar
# 5. 上传到服务器
scp myapp-0.2.0.tar.gz server:/opt/
# 6. 执行热升级
bin/myapp upgrade "0.2.0"
19.7 注意事项
⚠️ 常见陷阱
- 热升级需要测试回退路径
- 状态迁移代码需要覆盖所有版本路径
- NIF 库无法热升级
- 监督树结构变更需要特殊处理
- 确保 sys.config 和 vm.args 在所有环境中一致
💡 最佳实践
- 开发时使用
dev_mode(符号链接更快) - 生产构建包含 ERTS(
include_erts, true) - 使用
extended_start_script生成管理脚本 - 定期测试升级和降级流程
- 使用容器(Docker)简化部署