Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5120100
  • 博文数量: 921
  • 博客积分: 16037
  • 博客等级: 上将
  • 技术积分: 8469
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-05 02:08
文章分类

全部博文(921)

文章存档

2020年(1)

2019年(3)

2018年(3)

2017年(6)

2016年(47)

2015年(72)

2014年(25)

2013年(72)

2012年(125)

2011年(182)

2010年(42)

2009年(14)

2008年(85)

2007年(89)

2006年(155)

分类: Python/Ruby

2012-09-04 14:58:49

原创文章,转载请注明: 转载自Erlang非业余研究

本文链接地址: Erlang集群自动化添加节点指南

Erlang的集群是由各个节点组成的,一个节点有一个名字来标识,而不管这个节点在网络的物理位置,所以在部署Erlang集群的时候就很方便。只要在集群里新启动一个节点,给个相对固定的引导的节点,让新节点和这个引导节点取得联系,由引导节点把新节点介绍入集群就OK了。
在实践中,新采购的机器上通常配置好IP,以及ssh访问权限。 我们需要在新机器上手工安装Erlang系统,部署新应用,然后启动应用节点,加入集群服务,这个步骤很繁琐。我们希望能够自动化去做这个事情。common_test的ct_系列模块来救助了。
common_test是A framework for automated testing of arbitrary target nodes,它随带的ct_ssh可以透过ssh在远程机器上执行各种各样的shell命令,通过scp传输数据;而ct_slave非常方便的可以连接到远程机器启动一个erlang节点。
pool()模块也可以远程启动节点,但是它要依赖于操纵系统的ssh工具,需要在机器之间做ssh互信,也就是说ssh targetip这样的不能出现任何的交互,比如说键入密码,很不方便。

我们首先来演示下如何在远程机器上执行ssh命令:
我们开个ssh以用户yourname连接到本机127.0.0.1执行模拟上传文件,下载文件,执行文件拷贝操纵等。

  1. #首先我们准备个配置文件
  2. $ cd ~
  3. $ cat sshdemo.config
  4. {sshdemo,
  5.      [
  6.      {ssh, "127.0.0.1"},
  7.      {port, 22},
  8.      {user, "yourname"},
  9.      {password, "yourpassword"}
  10.      ]
  11. }.

  12. $ mkdir /tmp/sshdemo
  13. $ run_test -shell -config sshdemo.config
  14. Erlang R14B01 (erts-5.8.5) 1 [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

  15. Common Test v1.5.1 starting (cwd is /Users/yufeng)

  16. Eshell V5.8.5 (abort with ^G)
  17. (ct@yufengdemacbook)1>
  18. Installing: [{ct_config_plain,["/Users/yufeng/sshdemo.config"]}]

  19. Updated /Users/yufeng/last_interactive.html
  20. Any CT activities will be logged here

  21. (ct@yufengdemacbook)1> {ok, CH}=ct_ssh:connect(sshdemo, sftp).
  22. {ok,}
  23. (ct@yufengdemacbook)2> ct_ssh:list_dir(CH, "/tmp/sshdemo").
  24. {ok,["..","."]}
  25. (ct@yufengdemacbook)3> ct_ssh:write_file(CH, "/tmp/sshdemo/test.dat", "hello").
  26. ok
  27. (ct@yufengdemacbook)4> ct_ssh:read_file(CH, "/tmp/sshdemo/test.dat").
  28. {ok,<>}
  29. (ct@yufengdemacbook)5> {ok, CH1}=ct_ssh:connect(sshdemo, ssh).
  30. {ok,}
  31. (ct@yufengdemacbook)6> ct_ssh:exec(CH1, "cp /tmp/sshdemo/test.dat /tmp/sshdemo/test1.dat").
  32. {ok,[]}
  33. (ct@yufengdemacbook)7>
  34. ...

  35. $ ls /tmp/sshdemo
  36. test.dat test1.dat

通过上面的试验,我们可以很方便的透过ssh在目标机器安装我们的erlang系统以及部署我们的应用。接下来我们来演示下如何在目标机器开启一个节点:

  1. $ run_test -shell -name x@127.0.0.1

  2. Erlang R14B01 (erts-5.8.5) 1 [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

  3. Common Test v1.5.1 starting (cwd is /Users/yufeng)

  4. Eshell V5.8.5 (abort with ^G)
  5. (x@127.0.0.1)1>

  6. Updated /Users/yufeng/last_interactive.html
  7. Any CT activities will be logged here

  8. (x@127.0.0.1)1> ct_slave:start('127.0.0.1', y, [{username, "yourname"},{password, "yourpassword"}]).
  9. {error,boot_timeout,'y@127.0.0.1'}

我们可以看到我们可耻的失败了,我们没在默认的3秒内看到我们的节点启动成功。问题出在哪里呢?我们来系统的跟踪下:
首先从源码ct_slave.erl:L403中我们可以清楚的看到:


 

  1. % spawn node remotely
  2. spawn_remote_node(Host, Node, Options) ->
  3.     Username = Options#options.username,
  4.     Password = Options#options.password,
  5.     ErlFlags = Options#options.erl_flags,
  6.     SSHOptions = case {Username, Password} of
  7.         {[], []}->
  8.             [];
  9.         {_, []}->
  10.             [{user, Username}];
  11.         {_, _}->
  12.             [{user, Username}, {password, Password}]
  13.     end ++ [{silently_accept_hosts, true}],
  14.     check_for_ssh_running(),
  15.     {ok, SSHConnRef} = ssh:connect(atom_to_list(Host), 22, SSHOptions),
  16.     {ok, SSHChannelId} = ssh_connection:session_channel(SSHConnRef, infinity),
  17.     ssh_connection:exec(SSHConnRef, SSHChannelId, get_cmd(Node, ErlFlags), infinity).
  18. ...
  19. % wait N seconds until node is pingable
  20. wait_for_node_alive(_Node, 0) ->
  21.     pang;
  22. wait_for_node_alive(Node, N) ->
  23.     timer:sleep(1000),
  24.     case net_adm:ping(Node) of
  25.         pong->
  26.             pong;
  27.         pang->
  28.             wait_for_node_alive(Node, N-1)
  29.     end.
  30. %%等待的逻辑
  31. ...
  32. MasterHost = gethostname(),
  33.     if
  34.         MasterHost == Host ->
  35.             spawn_local_node(Node, Options);
  36.         true->
  37.             spawn_remote_node(Host, Node, Options)
  38.     end,
  39.     BootTimeout = Options#options.boot_timeout,
  40.     InitTimeout = Options#options.init_timeout,
  41.     StartupTimeout = Options#options.startup_timeout,
  42.     Result = case wait_for_node_alive(ENode, BootTimeout) of
  43.         pong->
  44.             call_functions(ENode, Functions2),
  45.             receive
  46.                 {node_started, ENode}->
  47.                     receive
  48.                         {node_ready, ENode}->
  49.                             {ok, ENode}
  50.                     after StartupTimeout*1000->
  51.                         {error, startup_timeout, ENode}
  52.                     end
  53.             after InitTimeout*1000 ->
  54.                 {error, init_timeout, ENode}
  55.             end;
  56.         pang->
  57.             {error, boot_timeout, ENode}
  58.     end,
  59. ...

我们来用强大的dbg跟踪下ct_slave的执行情况:


 

  1. (x@127.0.0.1)2> dbg:tracer().
  2. {ok,}
  3. (x@127.0.0.1)3> dbg:p(all,c).
  4. {ok,[{matched,'x@127.0.0.1',32}]}
  5. (x@127.0.0.1)4> dbg:tpl(ssh_connection,exec, [{'_', [], [{return_trace}]}]).
  6. {ok,[{matched,'x@127.0.0.1',1},{saved,1}]}
  7. (x@127.0.0.1)5> dbg:tpl(ct_slave,wait_for_node_alive, [{'_', [], [{return_trace}]}]).
  8. {ok,[{matched,'x@127.0.0.1',1},{saved,2}]}
  9. (x@127.0.0.1)6> ct_slave:start('127.0.0.1', y, [{username, "yourname"},{password, "yourpassword"} ]).
  10. () call ssh_connection:exec(,0,"erl -detached -noinput -setcookie VSVDYDCFVTPSDPBFHQMY -name y ",infinity)
  11. () returned from ssh_connection:exec/4 -&gt; success
  12. () call ct_slave:wait_for_node_alive('y@127.0.0.1',3)
  13. () call ct_slave:wait_for_node_alive('y@127.0.0.1',2)
  14. () call ct_slave:wait_for_node_alive('y@127.0.0.1',1)
  15. {error,boot_timeout,'y@127.0.0.1'}
  16. (x@127.0.0.1)7> code:which(ct_slave).
  17. "/usr/local/lib/erlang/lib/common_test-1.5.4/ebin/ct_slave.beam"

从上面的跟踪命令我们知道我们的ssh命令是执行成功的,但是在等待节点的时候失败了,通过查看进程和epmd也可以验证这一点:

  1. $ ps -ef|grep beam
  2. ...
  3.  500 36161 36120 0 0:00.24 ttys000 0:00.81 /usr/local/lib/erlang/erts-5.8.5/bin/beam.smp -- -root /usr/local/lib/erlang -progname erl -- -home /Users/yufeng -- -name x@127.0.0.1 -s ct_run script_start -shell
  4.  501 26067 1 0 0:00.03 ?? 0:00.21 /opt/local/lib/erlang/erts-5.8.5/bin/beam -- -root /opt/local/lib/erlang -progname erl -- -home /Users/yufeng -noshell -noinput -noshell -noinput -setcookie VSVDYDCFVTPSDPBFHQMY -name y
  5. ...

  6. $ epmd -names
  7. epmd: up and running on port 4369 with data:
  8. name x at port 49869
  9. name y at port 49838

从上面的分析,我们可以清楚的看到:
我们想要的名字是 -name y@127.0.0.1, 等待的也是这个名字,但是ct_slave启动的是-name y.这明显是个bug!
我们看下代码确定如何修复:

  1. % get cmd for starting Erlang
  2. get_cmd(Node, Flags) ->
  3.     Cookie = erlang:get_cookie(),
  4.     "erl -detached -noinput -setcookie "++ atom_to_list(Cookie) ++
  5.     long_or_short() ++ atom_to_list(Node) ++ " " ++ Flags.

  6. % make a Erlang node name from name and hostname
  7. enodename(Host, Node) ->
  8.     list_to_atom(atom_to_list(Node)++"@"++atom_to_list(Host)).

我们可以看到get_cmd只是把node名字加到命令行去,但是node名字里面没有host部分。
知道原因修正就容易了,改下这行就好:

  1. ssh_connection:exec(SSHConnRef, SSHChannelId, get_cmd(enodename(Host, Node), ErlFlags), infinity).

我们来重新编译,安装下:

  1. $ pwd
  2. /Users/yufeng/otp
  3. $ export ERL_TOP=`pwd`
  4. $ cd lib/common_test/
  5. $ make
  6. $ sudo cp ebin/ct_slave.beam /usr/local/lib/erlang/lib/common_test-1.5.4/ebin/ct_slave.beam

代码升级好了,我们再试验下我们的猜想:


 

  1. (x@127.0.0.1)8> l(ct_slave).
  2. {module,ct_slave}
  3. (x@127.0.0.1)9> ct_slave:start('127.0.0.1', y, [{username, "yourname"},{password, "yourpassword"}]).
  4. (<0.39.0>) call ssh_connection:exec(<0.105.0>,0,"erl -detached -noinput -setcookie VSVDYDCFVTPSDPBFHQMY -name y@127.0.0.1 ",infinity)
  5. (<0.39.0>) returned from ssh_connection:exec/4 -> success
  6. (<0.39.0>) call ct_slave:wait_for_node_alive('y@127.0.0.1',3)
  7. (<0.39.0>) call ct_slave:wait_for_node_alive('y@127.0.0.1',2)
  8. (<0.39.0>) returned from ct_slave:wait_for_node_alive/2 -> pong
  9. (<0.39.0>) returned from ct_slave:wait_for_node_alive/2 -> pong
  10. {ok,'y@127.0.0.1'}
  11. (x@127.0.0.1)10> nodes().
  12. ['y@127.0.0.1']

 

这次试验成功了。我们透过远程启动节点,把y@127.0.0.1成功加入集群。

通过上面的2个步骤,我们就可以在新添加的裸机上方便的部署我们的Erlang系统,控制节点的运作和停止。

祝大家集群开心!








 

阅读(1315) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~