查看源码 release_handler (sasl v4.2.2)
解包和安装发布包
发布处理程序进程属于 SASL 应用程序,它负责发布处理,即解包、安装和删除发布包。
关于发布处理的介绍和示例,请参阅 系统文档中的 OTP 设计原则。
发布包是一个压缩的 tar 文件,其中包含某个版本发布的代码,通过调用 systools:make_tar/1,2
创建。发布包应位于先前版本发布的 $ROOT/releases
目录中,其中 $ROOT
是安装根目录,code:root_dir()
。可以使用 SASL 配置参数 releases_dir
或 OS 环境变量 RELDIR
来指定另一个 releases
目录。发布处理程序必须具有此目录的写入权限才能安装新发布。发布处理程序的持久状态存储在一个名为 RELEASES
的文件中。
发布包始终包含:
- 发布资源文件,
Name.rel
- 启动脚本,
Name.boot
.rel
文件包含有关发布的信息:其名称、版本以及它使用的 ERTS 和应用程序版本。
发布包还可以包含:
- 发布升级文件,
relup
- 系统配置文件,
sys.config
- 系统配置源文件,
sys.config.src
relup
文件包含有关如何升级或降级到此版本发布的说明。
可以解包发布包,这会提取文件。可以安装解包的发布。然后,通过评估 relup
文件中的说明,将当前使用的发布版本升级或降级到指定的版本。可以使已安装的发布成为永久发布。系统中只能存在一个永久发布,如果系统重新启动,则使用此发布。已安装的发布(永久发布除外)可以删除。删除发布时,会删除仅属于该发布的所有文件。
每个发布版本都有一个状态,可以是 unpacked
、current
、permanent
或 old
。始终有一个最新的发布,其状态为 permanent
(正常情况)或 current
(已安装,但尚未设为永久)。状态值的含义在下表中说明:
Status Action NextStatus
-------------------------------------------
- unpack unpacked
unpacked install current
remove -
current make_permanent permanent
install other old
remove -
permanent make other permanent old
install permanent
old reboot_old permanent
install current
remove -
发布处理程序进程是每个节点上本地注册的进程。在分布式系统中安装发布时,必须调用每个节点上的发布处理程序。可以在节点之间同步发布安装。从操作员的角度来看,指定每个节点可能不令人满意。目标是在系统中安装一个发布包,无论有多少个节点。建议编写软件管理功能来解决此问题。此类函数可以了解系统架构,因此它可以联系每个单独的发布处理程序来安装软件包。
为了使发布处理正常工作,运行时系统必须知道它正在运行哪个发布。它还必须能够在运行时更改如果系统重新启动将要使用的启动脚本和系统配置文件。如果 Erlang 作为嵌入式系统启动,则会自动处理此问题。有关此内容的更多信息,请阅读系统文档中的 嵌入式系统。在这种情况下,系统配置文件 sys.config
是必需的。
新发布的安装可以重新启动系统。要使用的程序由 SASL 配置参数 start_prg
指定,默认为 $ROOT/bin/start
。
Windows NT 上的模拟器重新启动期望系统使用 erlsrv
程序(作为服务)启动。此外,发布处理程序期望服务名为 NodeName
_Release
,其中 NodeName
是 Erlang 节点名称的第一部分(直到但不包括“@”),Release
是当前发布版本。发布处理程序还期望像 start_erl.exe
这样的程序被指定为 erlsrv
的“machine”。在重新启动的升级过程中,将注册并启动新服务。新服务设置为自动,当新发布成为永久发布时,旧服务将被删除。
在无盘计算机上或具有只读文件系统的节点上运行的发布处理程序必须使用以下 SASL 配置参数进行相应配置(有关详细信息,请参阅 sasl(6)):
masters
- 此节点使用一些主节点来存储和获取发布信息。当此节点写入发布信息时,所有主节点都必须处于运行状态。client_directory
- 必须指定主节点的目录结构中的client_directory
。static_emulator
- 此参数指定 Erlang 模拟器是否静态安装在客户端节点上。具有静态模拟器的节点无法动态切换到新的模拟器,因为可执行文件静态写入内存。
当不将 Erlang 作为嵌入式系统运行时,也可以使用发布处理程序来解包和安装发布包。但是,在这种情况下,如果系统必须重新启动,用户必须以某种方式确保使用正确的启动脚本和配置文件。
提供了用于使用与 OTP 中定义的结构不同的文件结构的函数。这些函数可用于在本地测试发布升级。
常见错误原因
{bad_masters, Masters}
- 主节点Masters
未处于活动状态。{bad_rel_file, File}
- 无法读取指定的.rel
文件File
或它不包含单个项。{bad_rel_data, Data}
- 指定的.rel
文件不包含可识别的发布规范,而是另一个项Data
。{bad_relup_file, File}
- 指定的relup
文件Relup
包含错误的数据。{cannot_extract_file, Name, Reason}
- 从 tar 文件中提取时出现问题,erl_tar:extract/2
返回{error, {Name, Reason}}
。{existing_release, Vsn}
- 指定的发布版本Vsn
已在使用中。{Master, Reason, When}
- 在主节点Master
上,某些操作(由项When
指示)失败,并出现指定的错误原因Reason
。{no_matching_relup, Vsn, CurrentVsn}
- 找不到在CurrentVsn
和Vsn
之间升级/降级的脚本。{no_such_directory, Path}
- 目录Path
不存在。{no_such_file, Path}
- 路径Path
(文件或目录)不存在。{no_such_file, {Master, Path}}
- 路径Path
(文件或目录)在主节点Master
上不存在。{no_such_release, Vsn}
- 指定的发布版本Vsn
不存在。{not_a_directory, Path}
-Path
存在,但不是目录。{Posix, File}
- 某些文件操作对于File
失败。Posix
是一个从 Posix 错误代码命名的原子,例如enoent
、eacces
或eisdir
。请参阅 Kernel 中的file
。Posix
- 某些文件操作失败,与列表中的上一个项目相同。
应用程序升级/降级
应用程序升级/降级部分中的函数可用于测试单个应用程序的升级和降级(而不是升级/降级整个发布)。根据应用程序的 .appup 文件动态创建与 relup 文件中的指令对应的脚本,并以与 release_handler 完全相同的方式进行评估。
警告
这些函数主要用于简化 .appup 文件的测试。它们不在 release_handler 进程的上下文中运行。因此,它们不能与对 install_release/1,2 的调用一起使用,因为这会导致 release_handler 最终处于不一致的状态。
不会更新持久信息,因此这些函数可以在任何 Erlang 节点上使用,无论是否嵌入。此外,使用这些函数不会影响重新启动时加载的代码。
如果升级或降级失败,应用程序最终可能会处于不一致的状态。
另请参阅
摘要
应用程序升级/降级
根据 .appup
文件,将应用程序 App
从当前版本降级到位于 Dir
中的先前版本 OldVsn
。
尝试查找从当前版本到位于 Dir
中的先前版本 OldVsn
的 App
应用程序降级脚本。
评估应用程序升级或降级脚本 Script
(调用 upgrade_script/2
或 downgrade_script/3
的结果),其方式与 install_release/1,2
完全相同。
根据 .appup
文件,将应用程序 App
从当前版本升级到位于 Dir
中的新版本。
尝试查找从当前版本到位于 Dir
中的新版本的 App
应用程序升级脚本。
函数
检查是否可以安装指定的发布版本 Vsn
。
创建一个初始的 RELEASES
文件,供发布处理程序使用。
在发布结构中安装一个与发布相关的特定文件。
安装指定的发布版本 Vsn
。
使指定的发布版本 Vsn
成为永久版本。
通过使旧版本成为永久版本来重启系统,并直接调用 init:reboot()
。
从系统中删除一个发布及其文件。
允许在发布处理程序之外处理发布的删除。
允许在发布处理程序之外处理发布的解包。
解包位于 releases
目录中的发布包 Name.tar.gz
。
返回发布处理程序已知的所有发布。
返回发布处理程序已知的所有具有特定状态的发布。
应用程序升级/降级
-spec downgrade_app(App, Dir) -> {ok, Unpurged} | restart_emulator | {error, Reason} when App :: atom(), Dir :: string(), Unpurged :: [Module], Module :: atom(), Reason :: term().
等效于 downgrade_app/3
。
-spec downgrade_app(App, OldVsn, Dir) -> {ok, Unpurged} | restart_emulator | {error, Reason} when App :: atom(), Dir :: string(), OldVsn :: string(), Unpurged :: [Module], Module :: atom(), Reason :: term().
根据 .appup
文件,将应用程序 App
从当前版本降级到位于 Dir
中的先前版本 OldVsn
。
App
是必须启动的应用程序的名称。OldVsn
是之前的应用程序版本,如果 Dir
的格式为 "App-OldVsn"
,则可以省略。Dir
是 App
的先前版本的库目录。相应的模块和旧的 .app
文件位于 Dir/ebin
下。.appup
文件位于应用程序当前库目录的 ebin
目录中(code:lib_dir(App)
)。
该函数在 .appup
文件中查找,并尝试使用 downgrade_script/3
查找先前应用程序版本的降级脚本。此脚本使用 eval_appup_script/4
进行评估,与 install_release/1,2
的方式完全相同。
返回以下之一
- 如果脚本评估成功,则返回
{ok, Unpurged}
,其中Unpurged
是未清除模块的列表 - 如果在脚本中遇到此指令,则返回
restart_emulator
- 如果在查找或评估脚本时发生错误,则返回
{error, Reason}
-spec downgrade_script(App, OldVsn, Dir) -> {ok, Script} when App :: atom(), OldVsn :: string(), Dir :: string(), Script :: Instructions :: term().
尝试查找从当前版本到位于 Dir
中的先前版本 OldVsn
的 App
应用程序降级脚本。
然后可以使用 eval_appup_script/4
评估降级脚本。建议改用 downgrade_app/2,3
,但此函数(downgrade_script
)可用于检查脚本的内容。
App
是必须启动的应用程序的名称。Dir
是 App
的先前库目录。相应的模块和旧的 .app
文件位于 Dir/ebin
下。.appup
文件位于应用程序当前库目录的 ebin
目录中(code:lib_dir(App)
)。
该函数在 .appup
文件中查找,并尝试查找从当前应用程序版本降级的脚本。高级指令将转换为低级指令。指令的排序方式与生成 relup
文件时相同。
如果成功,则返回 {ok, Script}
。有关 Script
的详细信息,请参见 appup(4)
。
失败:如果找不到脚本,该函数将因适当的错误原因而失败。
-spec eval_appup_script(App, ToVsn, ToDir, Script :: term()) -> {ok, Unpurged} | restart_emulator | {error, Reason} when App :: atom(), ToVsn :: string(), ToDir :: string(), Unpurged :: [Module], Module :: atom(), Reason :: term().
评估应用程序升级或降级脚本 Script
(调用 upgrade_script/2
或 downgrade_script/3
的结果),其方式与 install_release/1,2
完全相同。
App
是必须启动的应用程序的名称。ToVsn
是要升级/降级到的版本,ToDir
是此版本的库目录。相应的模块以及 .app
和 .appup
文件位于 Dir/ebin
下。
返回以下之一
- 如果脚本评估成功,则返回
{ok, Unpurged}
,其中Unpurged
是未清除模块的列表 - 如果在脚本中遇到此指令,则返回
restart_emulator
- 如果在查找或评估脚本时发生错误,则返回
{error, Reason}
如果在脚本中找到 restart_new_emulator
指令,eval_appup_script/4
将返回 {error,restart_new_emulator}
。这是因为 restart_new_emulator
需要先启动新版本的仿真器,然后才能执行其余的升级指令,而这只能通过 install_release/1,2
完成。
-spec upgrade_app(App, Dir) -> {ok, Unpurged} | restart_emulator | {error, Reason} when App :: atom(), Dir :: string(), Unpurged :: [Module], Module :: atom(), Reason :: term().
根据 .appup
文件,将应用程序 App
从当前版本升级到位于 Dir
中的新版本。
App
是必须启动的应用程序的名称。Dir
是 App
的新库目录。相应的模块以及 .app
和 .appup
文件位于 Dir/ebin
下。
该函数在 .appup
文件中查找,并尝试使用 upgrade_script/2
查找从当前应用程序版本升级的脚本。此脚本使用 eval_appup_script/4
进行评估,与 install_release/1,2
的方式完全相同。
返回以下之一
- 如果脚本评估成功,则返回
{ok, Unpurged}
,其中Unpurged
是未清除模块的列表 - 如果在脚本中遇到此指令,则返回
restart_emulator
- 如果在查找或评估脚本时发生错误,则返回
{error, Reason}
如果在脚本中找到 restart_new_emulator
指令,upgrade_app/2
将返回 {error,restart_new_emulator}
。这是因为 restart_new_emulator
需要先启动新版本的仿真器,然后才能执行其余的升级指令,而这只能通过 install_release/1,2
完成。
-spec upgrade_script(App, Dir) -> {ok, NewVsn, Script} when App :: atom(), Dir :: string(), NewVsn :: string(), Script :: Instructions :: term().
尝试查找从当前版本到位于 Dir
中的新版本的 App
应用程序升级脚本。
然后可以使用 eval_appup_script/4
评估升级脚本。建议改用 upgrade_app/2
,但此函数(upgrade_script
)可用于检查脚本的内容。
App
是必须启动的应用程序的名称。Dir
是 App
的新库目录。相应的模块以及 .app
和 .appup
文件位于 Dir/ebin
下。
该函数在 .appup
文件中查找,并尝试查找从当前应用程序版本升级的脚本。高级指令将转换为低级指令。指令的排序方式与生成 relup
文件时相同。
如果成功,则返回 {ok, NewVsn, Script}
,其中 NewVsn
是新的应用程序版本。有关 Script
的详细信息,请参见 appup(4)
。
失败:如果找不到脚本,该函数将因适当的错误原因而失败。
函数
-spec check_install_release(Vsn, Opts) -> {ok, OtherVsn, Descr} | {error, Reason} when Vsn :: string(), OtherVsn :: string(), Opts :: [Opt], Opt :: purge, Descr :: term(), Reason :: term().
检查是否可以安装指定的发布版本 Vsn
。
发布版本不能具有 current
状态。如果 relup
文件或 sys.config
不存在,则会发出警告。如果存在 relup
文件,则检查其内容,如果发现错误,则返回 {error,Reason}
。此外,还检查是否所有必需的应用程序都存在以及是否可以加载所有新代码;如果发现错误,则返回 {error,Reason}
。
评估发布升级脚本中 point_of_no_return
指令之前出现的所有指令。
返回与 install_release/1
相同的结果。如果未找到 relup
文件,Descr
默认为 ""。
如果指定选项 purge
,则在成功完成所有其他检查后,会清除所有可以软清除的旧代码。这有助于减少 install_release/1
所需的时间。
-spec create_RELEASES(Root, RelDir, RelFile, AppDirs) -> ok | {error, Reason} when Root :: string(), RelDir :: string(), RelFile :: string(), AppDirs :: [{App, Vsn, Dir}], App :: atom(), Vsn :: string(), Dir :: string(), Reason :: term().
创建一个初始的 RELEASES
文件,供发布处理程序使用。
必须存在此文件才能安装新版本。
Root
是安装的根目录($ROOT
),如前所述。RelDir
是要创建 RELEASES
文件的目录(通常是 $ROOT/releases
)。RelFile
是描述初始版本的 .rel
文件的名称,包括扩展名 .rel
。如果未提供 Root
,则 RELEASES
文件将与位置无关(即,除非 AppDirs
中存在绝对路径,否则它将不包含绝对路径)。如果安装的 $ROOT
未知,则应使 RELEASES
文件与位置无关。 release_handler
模块会将运行系统中 RELEASES
文件中的相对路径解释为相对于 $ROOT
的路径。
可以使用 AppDirs
指定要从哪里加载指定应用程序的模块。App
是应用程序的名称,Vsn
是版本,Dir
是 App-Vsn
所在的目录的名称。相应的模块位于 Dir/App-Vsn/ebin
下。未在 AppDirs
中指定的应用程序的目录假定位于 $ROOT/lib
中。
-spec install_file(Vsn, File) -> ok | {error, Reason} when Vsn :: string(), File :: string(), Reason :: term().
在发布结构中安装一个与发布相关的特定文件。
当安装新版本时,与发布相关的特定文件必须位于发布结构中:start.boot
、relup
和 sys.config
。
例如,可以在目标生成这些文件时调用此函数。此函数应在调用 set_unpacked/2
后调用。
-spec install_release(Vsn, [Opt]) -> {ok, OtherVsn, Descr} | {continue_after_restart, OtherVsn, Descr} | {error, Reason} when Vsn :: string(), OtherVsn :: string(), Opt :: {error_action, Action} | {code_change_timeout, Timeout} | {suspend_timeout, Timeout} | {update_paths, Bool}, Action :: restart | reboot, Timeout :: default | infinity | pos_integer(), Bool :: boolean(), Descr :: term(), Reason :: {illegal_option, Opt} | {already_installed, Vsn} | {change_appl_data, term()} | {missing_base_app, OtherVsn, App} | {could_not_create_hybrid_boot, term()} | term(), App :: atom().
安装指定的发布版本 Vsn
。
首先查找版本为 Vsn
的 relup
文件,以及该文件中用于从当前版本升级的脚本 {UpFromVsn,Descr1,Instructions1}
。如果未找到,则该函数查找当前版本的 relup
文件,以及该文件中用于降级到 Vsn
的脚本 {Vsn,Descr2,Instructions2}
。
如果找到了脚本,首先会根据 .app
文件和属于版本 Vsn
的 sys.config
来更新应用程序规范。
更新应用程序规范后,会评估脚本中的指令,如果成功,则该函数返回 {ok,OtherVsn,Descr}
。OtherVsn
和 Descr
是脚本中指定的版本(UpFromVsn
或 Vsn
)和描述(Descr1
或 Descr2
)。
如果返回 {continue_after_restart,OtherVsn,Descr}
,则会在执行升级指令之前重启仿真器。如果仿真器或任何应用程序(Kernel、STDLIB 或 SASL)被更新,则会发生这种情况。重启后,将执行新版本的仿真器和这些核心应用程序。对于所有其他应用程序,旧版本将启动,并通过执行升级指令来正常执行升级。
如果发生可恢复的错误,该函数返回 {error,Reason}
,并且原始的应用程序规范将被恢复。如果发生不可恢复的错误,系统将被重启。
选项:
error_action
- 定义在安装期间发生错误时,节点是重启(init:restart()
)还是重新启动(init:reboot()
)。默认值为restart
。code_change_timeout
- 定义所有调用sys:change_code
的超时时间。如果未指定值或指定了default
,则使用sys
中定义的默认值。suspend_timeout
- 定义所有调用sys:suspend
的超时时间。如果未指定值,则使用upgrade
或suspend
指令的Timeout
参数定义的值。如果指定了default
,则使用sys
中定义的默认值。{update_paths,Bool}
- 指示是否要更新所有应用程序代码路径(Bool==true
)或仅更新修改后的应用程序的代码路径(Bool==false
,默认值)。此选项仅对默认目录$ROOT/lib/App-Vsn
以外的应用程序目录有效,即在调用create_RELEASES/4
或set_unpacked/2
时,在参数AppDirs
中指定的应用程序目录。示例
在当前版本为
CurVsn
的发布版本中,myapp
的应用程序目录为$ROOT/lib/myapp-1.0
。一个新的版本NewVsn
在发布处理程序之外解压,并通过如下调用通知发布处理程序。release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]). => {ok,NewVsn}
如果使用选项
{update_paths,true}
安装NewVsn
,则code:lib_dir(myapp)
返回/home/user/myapp-1.0
。
注意
如果系统中有许多进程,则安装新版本可能很耗时。原因是必须在清除模块之前检查每个进程对旧代码的引用。此检查可能导致垃圾回收和数据复制。
为了加快
install_release
的执行速度,首先使用purge
选项调用check_install_release
。 这会对旧代码进行相同的检查。然后清除所有可以软清除的模块。清除的模块不再有任何旧代码,install_release
不需要进行检查。这不会减少升级的总时间,但它允许在启动真正的升级之前在后台执行检查和清除。
注意
当从低于 OTP R15 的版本升级仿真器时,会尝试将新的应用程序 beam 代码加载到旧的仿真器中。有时旧的仿真器无法读取新的 beam 格式,因此代码加载失败,并且整个升级终止。为了解决这个问题,新的应用程序代码要使用旧的仿真器进行编译。有关从 OTP R15 之前的版本升级仿真器的更多信息,请参阅系统文档中的设计原则。
-spec make_permanent(Vsn) -> ok | {error, Reason} when Vsn :: string(), Reason :: {bad_status, Status :: term()} | term().
使指定的发布版本 Vsn
成为永久版本。
-spec reboot_old_release(Vsn) -> ok | {error, Reason} when Vsn :: string(), Reason :: {bad_status, Status :: term()} | term().
通过使旧版本成为永久版本来重启系统,并直接调用 init:reboot()
。
发布版本必须具有 old
状态。
-spec remove_release(Vsn) -> ok | {error, Reason} when Vsn :: string(), Reason :: {permanent, Vsn} | client_node | term().
从系统中删除一个发布及其文件。
该发布版本不能是永久发布版本。仅删除另一个发布版本未使用的文件和目录。
-spec set_removed(Vsn) -> ok | {error, Reason} when Vsn :: string(), Reason :: {permanent, Vsn} | term().
允许在发布处理程序之外处理发布的删除。
告诉发布处理程序该发布版本已从系统中删除。此函数不会删除任何文件。
-spec set_unpacked(RelFile, AppDirs) -> {ok, Vsn} | {error, Reason} when RelFile :: string(), AppDirs :: [{App, Vsn, Dir}], App :: atom(), Vsn :: string(), Dir :: string(), Reason :: term().
允许在发布处理程序之外处理发布的解包。
告诉发布处理程序该发布版本已解压缩。Vsn
从发布资源文件 RelFile
中提取。
可以使用 AppDirs
指定要从哪里加载指定应用程序的模块。App
是应用程序的名称,Vsn
是版本,Dir
是 App-Vsn
所在的目录的名称。相应的模块位于 Dir/App-Vsn/ebin
下。未在 AppDirs
中指定的应用程序的目录假定位于 $ROOT/lib
中。
-spec unpack_release(Name) -> {ok, Vsn} | {error, Reason} when Name :: string(), Vsn :: string(), Reason :: client_node | term().
解包位于 releases
目录中的发布包 Name.tar.gz
。
对软件包执行一些检查,例如,检查所有必需的文件是否存在,并提取其内容。
-spec which_releases() -> [{Name, Vsn, Apps, Status}] when Name :: string(), Vsn :: string(), Apps :: [AppVsn :: string()], Status :: unpacked | current | permanent | old.
返回发布处理程序已知的所有发布。
-spec which_releases(Status) -> [{Name, Vsn, Apps, Status}] when Name :: string(), Vsn :: string(), Apps :: [AppVsn :: string()], Status :: unpacked | current | permanent | old.
返回发布处理程序已知的所有具有特定状态的发布。