【问题标题】:Unset all paths before node boot在节点启动之前取消设置所有路径
【发布时间】:2015-09-22 05:53:13
【问题描述】:

TLDR; 启动 erlang 节点时(仅对实例使用 erl 命令),如何强制它不使用本地 OTP 库并将 code:get_path() 设为空?

基本原理。

我想触摸erl_boot_server。不要做确定的事情,只是玩。我已经构建了示例版本并希望通过网络加载它。就是这样。

[vkovalev@t30nix foobar]$ tree -L 2
.
|-- bin
|   |-- foobar
|   |-- foobar-0.0.0+build.1.ref307ae38
|   |-- install_upgrade.escript
|   |-- nodetool
|   `-- start_clean.boot
|-- erts-6.1
|   |-- bin
|   |-- doc
|   |-- include
|   |-- lib
|   |-- man
|   `-- src
|-- lib
|   |-- foobar-0.1.0
|   |-- kernel-3.0.1
|   |-- sasl-2.4
|   `-- stdlib-2.1
`-- releases
    |-- 0.0.0+build.1.ref307ae38
    |-- RELEASES
    `-- start_erl.data

首先我启动引导节点。

[vkovalev@t30nix foobar]$ erl -sname boot -pa lib/*/ebin -pa releases/0.0.0+build.1.ref307ae38/ -s erl_boot_server start localhost

(boot@t30nix)1> {ok, _, _} = erl_prim_loader:get_file("foobar.boot").
(boot@t30nix)2> {ok, _, _} = erl_prim_loader:get_file("foobar_app.beam").

如您所见,这里一切正常。然后我启动从节点:

[vkovalev@t30nix ~]$ erl -sname slave -loader inet -hosts 127.0.0.1  -boot foobar
{"init terminating in do_boot",{'cannot get bootfile','foobar.boot'}}

Crash dump was written to: erl_crash.dump
init terminating in do_boot ()

我挖掘了 erl_prim_loader 并找到了that stuff。一个子句在 Paths 为空时起作用(它只是将请求的文件名原样转发到引导服务器),另一个子句在 Paths 非空时起作用。在这种情况下(我想知道为什么)prim loader 使用自己的(客户端)路径削弱请求的文件名,然后要求 SERVER 提供此路径。在我看来,这是一件很奇怪的事情,但没关系。然后我检查了从节点上的code:get_path(),是的,它有本地otp安装的路径。

所以,回到主题。如何强制从节点不使用任何本地 OTP 安装(如果它已经存在)?

UPD:添加了更多调查结果。

  1. 第一件事 - https://github.com/erlang/otp/blob/maint/erts/preloaded/src/erl_prim_loader.erl#L669。 erl_prim_loader(在 inet 模式下)出于某些(我不清楚)原因尝试 使用本地(客户端)路径削弱任何请求的模块。

  2. 似乎没有办法强制从节点上的加载程序保持其 路径为空:https://github.com/erlang/otp/blob/maint/erts/preloaded/src/init.erl#L697

  3. 我的引导脚本中的路径看起来像 {path,["$ROOT/lib/kernel-4.0/ebin","$ROOT/lib/stdlib-2.5/ebin"]},所以 看来,如果我要加载引导脚本,无论如何,我将无法启动 系统。

发生了什么事? erlang 网络启动功能是否损坏?或者只是我的 大脑?如何让节点成功通过网络启动?

【问题讨论】:

    标签: erlang


    【解决方案1】:

    确保使用-setcookie 选项。来自erl -man erl 页面:

    -loader 加载器:

    指定 erl_prim_loader 使用的方法 将 Erlang 模块加载到系统中。看 erl_prim_loader(3)。支持两种 Loader 方法, efile和inet。 efile 表示使用本地文件 系统,这是默认的。 inet 表示使用引导 服务器在另一台机器上,以及 -id、-hosts 和 -setcookie 标志也必须指定。如果装载机 是别的东西,用户提供的加载程序端口 程序已启动。

    【讨论】:

      【解决方案2】:

      我认为钢筋项目可以帮助实现类似的目的。它包括如何操作路径:

      来自rebar_core.erl 文件: process_dir1(目录,命令,目录集,配置,当前代码路径, {DirModules, ModuleSetFile}) -> Config0 = rebar_config:set(Config, current_command, Command), %% 获取“任何目录”的模块列表。这是一个包罗万象的清单 除了关联的模块之外,还处理了 %% 的模块 %% 使用此目录类型。这些 any_dir 模块被处理 %% 第一的。 {ok, AnyDirModules} = application:get_env(rebar, any_dir_modules),

          Modules = AnyDirModules ++ DirModules,
      
          %% Invoke 'preprocess' on the modules -- this yields a list of other
          %% directories that should be processed _before_ the current one.
          {Config1, Predirs} = acc_modules(Modules, preprocess, Config0,
                                           ModuleSetFile),
      
          %% Remember associated pre-dirs (used for plugin lookup)
          PredirsAssoc = remember_cwd_predirs(Dir, Predirs),
      
          %% Get the list of plug-in modules from rebar.config. These
          %% modules may participate in preprocess and postprocess.
          {ok, PluginModules} = plugin_modules(Config1, PredirsAssoc),
      
          {Config2, PluginPredirs} = acc_modules(PluginModules, preprocess,
                                                 Config1, ModuleSetFile),
      
          AllPredirs = Predirs ++ PluginPredirs,
      
          ?DEBUG("Predirs: ~p\n", [AllPredirs]),
          {Config3, DirSet2} = process_each(AllPredirs, Command, Config2,
                                            ModuleSetFile, DirSet),
      
          %% Make sure the CWD is reset properly; processing the dirs may have
          %% caused it to change
          ok = file:set_cwd(Dir),
      
          %% Check that this directory is not on the skip list
          Config7 = case rebar_config:is_skip_dir(Config3, Dir) of
                        true ->
                            %% Do not execute the command on the directory, as some
                            %% module has requested a skip on it.
                            ?INFO("Skipping ~s in ~s\n", [Command, Dir]),
                            Config3;
      
                        false ->
                            %% Check for and get command specific environments
                            {Config4, Env} = setup_envs(Config3, Modules),
      
                            %% Execute any before_command plugins on this directory
                            Config5 = execute_pre(Command, PluginModules,
                                                  Config4, ModuleSetFile, Env),
      
                            %% Execute the current command on this directory
                            Config6 = execute(Command, Modules ++ PluginModules,
                                              Config5, ModuleSetFile, Env),
      
                            %% Execute any after_command plugins on this directory
                            execute_post(Command, PluginModules,
                                         Config6, ModuleSetFile, Env)
                    end,
      
          %% Mark the current directory as processed
          DirSet3 = sets:add_element(Dir, DirSet2),
      
          %% Invoke 'postprocess' on the modules. This yields a list of other
          %% directories that should be processed _after_ the current one.
          {Config8, Postdirs} = acc_modules(Modules ++ PluginModules, postprocess,
                                            Config7, ModuleSetFile),
          ?DEBUG("Postdirs: ~p\n", [Postdirs]),
          Res = process_each(Postdirs, Command, Config8,
                             ModuleSetFile, DirSet3),
      
          %% Make sure the CWD is reset properly; processing the dirs may have
          %% caused it to change
          ok = file:set_cwd(Dir),
      
          %% Once we're all done processing, reset the code path to whatever
          %% the parent initialized it to
          restore_code_path(CurrentCodePath),
      
          %% Return the updated {config, dirset} as result
          Res.
      
      restore_code_path(no_change) ->
          ok;
      restore_code_path({added, Paths}) ->
          %% Verify that all of the paths still exist -- some dynamically
          %% added paths can get blown away during clean.
          [code:del_path(F) || F <- Paths, erl_prim_loader_is_file(F)],
          ok.
      
      erl_prim_loader_is_file(File) ->
          erl_prim_loader:read_file_info(File) =/= error.
      

      【讨论】:

      • 抱歉,这些东西有什么用? erl_prim_loader 是在初始化后立即启动的进程。这是太早的启动阶段。此时节点上可用的模块集非常有限。模拟器中只有预置模块:github.com/erlang/otp/tree/maint/erts/preloaded/src
      【解决方案3】:

      你认为slave类型的节点只是以“slave”命名吗?

      以下代码来自项目tsung,文件名为“ts_os_mon_erlang.erl”。

      start_beam(Host) ->
          Args = ts_utils:erl_system_args(),
          ?LOGF("Starting os_mon beam on host ~p ~n", [Host], ?NOTICE),
          ?LOGF("~p Args: ~p~n", [Host, Args], ?DEB),
          slave:start(list_to_atom(Host), ?NODE, Args).
      

      另外,从模块限制如下:

      Slave nodes on other hosts than the current one are started with the program rsh. The user must be allowed to rsh to the remote hosts without being prompted for a password. This can be arranged in a number of ways (refer to the rsh documentation for details). A slave node started on the same host as the master inherits certain environment values from the master, such as the current directory and the environment variables. For what can be assumed about the environment when a slave is started on another host, read the documentation for the rsh program.
      
      An alternative to the rsh program can be specified on the command line to erl as follows: -rsh Program.
      
      The slave node should use the same file system at the master. At least, Erlang/OTP should be installed in the same place on both computers and the same version of Erlang should be used.
      

      如果你想用不同的路径启动一个节点,我认为你可以通过不同环境变量的脚本来完成,对于主节点,而不是从节点。

      【讨论】:

      • 问题不在于使用slave 模块启动的从站,而是关于那些使用erl_boot_server 加载代码的从站。他们也是奴隶。见http://www.erlang.org/doc/man/erl_boot_server.html。而且我不想以“不同的路径”启动节点,我想完全取消设置所有路径并根据erl_prim_loader 中的变量保持 ampty。为了让事情更清楚,我用详细的调查结果更新了原始问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-09
      • 1970-01-01
      相关资源
      最近更新 更多