erlang 接入远程shell控制台(请重点学习作业JCL模式)

578 阅读11分钟

blog.csdn.net/mycwq/artic…

erlang shell是用户与 erlang 运行时系统交互的界面程序。事实上,erlang VM的运行不依赖任何shell,只要在启动的时候添加参数detached就可以脱离终端。

- detached

Starts the Erlang runtime system detached from the system console. Useful for running daemons and backgrounds processes. Implies -noinput.

实际上,detached 等效于noshell 加上 noinput。

# erl - detached -emu_args
Executing: /home/erl/lib/erlang/erts-5.10.3/bin/beam /home/erl/lib/erlang/erts-5.10.3/bin/beam -- -root /home/erl/lib/erlang -progname erl -- -home /root --  -noshell -noinput\

另外,需要注意的是,windows不直接支持detached,而是以服务的形式在后台运行,见  erlsrv

\

现在讨论erlang 接入远程shell控制台的几种方式。

作业(JCL )模式 

在 Erlang shell 中按下^G键,就可以看到作业控制模式(JCL mode)的菜单。在菜单中,有 个选项能让我们连接到一个远程 shell。

先以detached运行一个节点1:

# erl -name  1@127.0.0.1  -setcookie 123456 -detached

检查这个erlang进程是否运行\

# ps -ef | grep beam

root 20672 1 0 01:32 ? 00:00:00 /home/erl/lib/erlang/erts-5.10.3/bin/beam -- -root /home/erl/lib/erlang -progname erl -- -home /root -- -name  1@127.0.0.1  -setcookie 123456 -noshell -noinput  

启动另外一个节点2,并接入到节点1

# erl -name  2@127.0.0.1 -setcookie 123456  Erlang R16B02 (erts-5.10.3) [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]  Eshell V5.10.3 (abort with ^G)  ( 2@127.0.0.1)1>  User switch command   --> h     c  [nn]                       - connect to job     i   [nn]                       - interrupt job     k  [nn]                       - kill job     j                                 - list all jobs     s [shell]                    - start local shell     r [node [shell]]        - start remote shell     q                               - quit erlang     ? | h                          - this message   --> r '1@127.0.0.1'   --> j    1 {shell,start,[init]}    2* {' 1@127.0.0.1',shell,start,[]}   --> c 2  Eshell V5.10.3 (abort with ^G)  ( 1@127.0.0.1)1>

注意了,windows下要使用werl

\

连接到远程 shell 后,所有的终端输入解析操作都由本地 shell 完成,不过求值的工作是在远  程完成的。远程求值的结果输出全部被转发给本地 shell。  

 

要退出 shell, 按^G回到 JCL 模式。 终端输入解析操作是在本地进行的, 因此通过^G q 的方式退出 shell  是安全的。

Eshell V5.10.3 (abort with ^G) 
( 1@127.0.0.1)1> 
User switch command 
--> q

\

\

Remsh 模式

Remsh和 JCL 模式很类似,但是调用方式不同的机制。使用这种机制,JCL 模式的所有  操作步骤都可以被绕过,只需像下面这样启动 shell,对于长名字:

\

-remsh Node

Starts Erlang with a remote shell connected to Node.

\

以下方式启动节点2,将直接接入节点1控制台:

# erl -name 2@127.0.0.1 -setcookie 123456 -remsh 1@127.0.0.1

Erlang R16B02 (erts-5.10.3) [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V5.10.3  (abort with ^G)

(1@127.0.0.1)1> node().

'1@127.0.0.1'

这种方式和JCL很相像,本地也会启动一个erlang节点用于接入远程shell

\

\

SSH 模式

erlang自带了 SSH的功能,我们可以很方便的开启SSH服务 ,对外提供远程 shell服务。 SSH的使用需要开启crypto,如果erlang显示以下错误,可以参考这篇文章

1> crypto:start().  

** exception error: undefined function crypto:start/0

要使用该功能,通常需要先准备好具有远程访问 SSH 权限的 key,不过这里为了快速 测试,可以这样做:

节点1启动ssh服务:

Eshell V5.10.3  (abort with ^G)( 1@127.0.0.1)1> ssh:start().ok( 1@127.0.0.1)2> ssh:daemon(8888, [{password, "12345"}]).{ok,<0.57.0>}

本地不需要启动erlang节点,直接使用ssh连接即可,输入以上设置的密码,就可以接入节点1的shell控制台。

# ssh -p 8888 1@127.0.0.1The authenticity of host '[127.0.0.1]:8888 ([127.0.0.1]:8888)' can't be established.RSA key fingerprint is ad:03:b4:6b:df:51:97:23:dc:47:cb:75:85:15:44:89.Are you sure you want to continue connecting (yes/no)?  yesWarning: Permanently added '[127.0.0.1]:8888' (RSA) to the list of known hosts.1@127.0.0.1's password:Eshell V5.10.3  (abort with ^G)( 1@127.0.0.1)1> 

这种方式,erlang  shell所有操作都是在远程节点完成的。

\

\

管道(pipe)模式

在使用管道(pipe)连接到一个Erlang节点时,和SSH一样不需要启动本地erlang节点 。这种方法很少用,每次输出时都调用fsync,如果输出过多时,会有很大的性能损失。

具体做法为:用 run_erl 启动 erlang,相当于把 erlang 进程包在一个 管道中:

# mkdir /tmp/erl_log

# cd /home/erl/bin

# ./run_erl -daemon /tmp/erl_pipe /tmp/erl_log "erl -name 1@127.0.0.1 -setcookie 123456"

其中,daemon 表示以后台进程运行,/tmp/erl_pipe是管道文件的名称,/tmp/erl_log指定了日志保存文件夹

然后使用 to_erl 程序来连接节点: 

# ./to_erl /tmp/erl_pipe

Attaching to /tmp/erl_pipe (^D to exit) 
( 1@127.0.0.1)1> node(). 
' 1@127.0.0.1'

\

参考:blog.csdn.net/mycwq/artic…

s3.amazonaws.com/erlang-in-a…

\

[Erlang 0033] 接入Erlang控制台的几种方法

      在window中调试的时候我们可以通过启动多个cmd窗口运行Erlang节点,在生产环境中我们需要Erlang服务在Centos服务器上后台运行;这就需要在启动的时候添加启动参数detached来脱离终端:

-detached Starts the Erlang runtime system detached from the system console. Useful for running daemons and backgrounds processes. Implies -noinput.

      对于我们自己的服务,即使部署到了生产环境一定要做到"像魔术师的飞刀,出手但并没有脱手",还是需要一些方式进入到Erlang后台进程来做一些工作比如:查看某一个Erlang节点的运行时信息(内存,进程数等),让服务优雅的退出而不是kill进程,或者做一下热更新(参见:[Erlang 0010] Erlang 热更新 当然热更新可以使用reloader.erl的方案来简化);一开始的时候服务器比较少,我们采用的是JCL的方式去处理的;

Erlang Shell JCL

JCL是Erlang Shell的一种运行模式,即Job Control Mode (JCL, in which jobs can be started, killed, detached and connected).我们启动两个节点来完成这个操作;

2012-11-14新增备注:下面的实验是在Linux下完成的,Windows下JCL需要启动werl.exe 

Node_1 添加了-detached选项,启动之后直接在后台运行并没有启动Shell
erl -setcookie abc -name node_1@192.168.1.123 -detached 
Node_2 使用了和Node_1相同的cookie,启动之后进入Erlang Shell界面
erl -setcookie abc -name node_2@192.168.1.123
下面我们开始在node_2@192.168.1.123演练JCL:

复制代码

Eshell V5.9  (abort with ^G)
(node_2@192.168.1.123)1> node().    %当前这是在node_2
'node_2@192.168.1.123'
(node_2@192.168.1.123)2>                %Ctrl + G 进入JCL模式
User switch command
--> h  
  c [nn]            - connect to job
  i [nn]            - interrupt job
  k [nn]            - kill job
  j                 - list all jobs
  s [shell]         - start local shell
  r [node [shell]]  - start remote shell
  q        - quit erlang
  ? | h             - this message
--> r'node_1@192.168.1.123'                           %尝试连接到node_1@192.168.1.123  
--> j
   1  {shell,start,[init]}                                          %列出所有的Job
   2* {'node_1@192.168.1.123',shell,start,[]}
--> c 2                                                               %这里2job的编号,切换到job 2 
Eshell V5.9  (abort with ^G)
(node_1@192.168.1.123)1> node().                %注意提示符,现在已经是在node_1
'node_1@192.168.1.123'
(node_1@192.168.1.123)2> erlang:now().
{1326,801888,347570}
(node_1@192.168.1.123)3>                             %再一次Ctrl + G
User switch command                                   
--> j                                                                   
   1  {shell,start,[init]}
   2* {'node_1@192.168.1.123',shell,start,[]}
--> c 1                                                               %切换到job 1
(node_2@192.168.1.123)2> node().                %注意提示符,我们已经回到了node_2
'node_2@192.168.1.123'
(node_2@192.168.1.123)3> 

复制代码

这样来来回回切换是不是有点盗梦空间的意思?是不是可以更简单一点,比如直接进入node_1呢?借助**-remsh**参数就可以做到

看看-remsh的说明恰好是我们需要的:

If you want an Erlang node to have a remote job active from the start (rather than the default local job), you start Erlang with the -remsh flag. Example: erl -sname this_node -remsh other_node@other_host

动手试一下:
erl -setcookie abc -name node_3@192.168.1.123 -remsh node_1@192.168.1.123 %%这样就直接进入了node_1节点
注意:直接进入到了node_1,执行完操作了想要退出怎么办? 你要是在这里执行一下q(). node_1这个节点就直接死掉了;
正确的方法还是Ctrl+G进入JCL模式然后执行q命令退出;使用ps aux|grep node查看一下进程是不是还在

ejabberd网站上提到了这个方法 
[1] Attach an Erlang Shell to an Already Running ejabberd Process www.ejabberd.im/tricks
[2] Interconnecting Erlang Nodes www.ejabberd.im/interconnec…

 

 另一种实用的接入erlang控制台的方法

地址:mryufeng.iteye.com/blog/362394  Powered by mryufeng

按照mryufeng老大的方法操练了一番,成功,中间 /usr/local/lib/erlang/bin/start 启动失败,同样是目录和配置文件缺失的问题
mkdir /usr/local/lib/erlang/log
echo "[]." > /usr/local/lib/erlang/releases/R15B/sys.config

Stackoverflow的相关问题: How to create deamon program with erlang? stackoverflow.com/questions/5…

题外话:在embedded模式下是没有交互式shell可用的,能够接入Erlang VM的方法就是to_erl

 

原理见下图:

 

erl_call

  随着服务器增多,登录一台台服务器然后使用JCL去做热更新之类的就有点麻烦了,取而代之的解决方案就是ssh登录到每台服务器然后向该服务器上的Erlang节点发送消息,执行热更新(当然不限于热更新了);

  这里有一个很强大的工具可以用:erl_call;erl_call模块的设计目标就是使用Unix Shell脚本与分布式Erlang节点进行交互,erl_call可以启动或者连接到一个分布式的Erlang节点进行简单的方法调用甚至执行表达式序列;erl_call构建在erl_interface之上,通过标准的RPC调用方式与Erlang的rex server交互,并不需要额外的工具支持.注意Erlang节点的RPC服务启动的时候注册的名字是rex www.google.com/codesearch#…

看一下erl_call的帮助,注意Erlang版本不同erl_call的路径也有所不同:

Usage: /usr/local/lib/erlang/lib/erl_interface-3.7.6/bin/erl_call [-[demqrsv]] [-c Cookie] [-h HiddenName]
[-x ErlScript] [-a [Mod [Fun [Args]]]]
(-n Node | -sname Node | -name Node)

where: -a  apply(Mod,Fun,Args) (e.g -a 'erlang length [[a,b,c]]'
-c  cookie string; by default read from ~/.erlang.cookie
-d  direct Erlang output to ~/.erl_call.out.<Nodename>
-e  evaluate contents of standard input (e.g echo "X=1,Y=2,{X,Y}."|erl_call -e ...)
-h  specify a name for the erl_call client node
-m  read and compile Erlang module from stdin
-n  name of Erlang node, same as -name
-name  name of Erlang node, expanded to a fully qualified
-sname name of Erlang node, short form will be used
-q  halt the Erlang node (overrides the -s switch)
-r  use a random name for the erl_call client node
-s  start a new Erlang node if necessary
-v  verbose mode, i.e print some information on stderr
-x  use specified erl start script, default is erl

简单翻译一下:

-a    执行erlang:apply/3,需要提供的就是m f a,m是必须的,f默认值start,a默认值[],注意这个flag只接受一个参数,所以最好通过添加括号把m f a组织起来.

-c 执行cookie值如果没有指定就会使用  ~/.erlang.cookie 文件中定义的cookie,Erlang 节点之间通信必须要有相同的cookie

-d 调试模式; 所有的IO都会输出到~/.erl_call.out.Nodename文件,其中NodeName就是我们要处理的erlang节点名;

-e 读一系列的Erlang表达式并返回最后一个表达式的结果,这些表达式用','分隔,'.'结束,这个Erlang Shell是一致的,Ctrl+D组合键结束输入,如果成功返回结果{ok,Result}.

-h HiddenName 隐藏erl_call代表的节点

-m 读入并编译一个module

-n -name -sname 这三个参数三选一,name和n是同样的含义是为了兼容依然保留,sname代码短名称

-q 停止Erlang 节点

-r 为erl_call生一个随机name

-s 必要的话启动一个Erlang的分布式节点,意思是自由第一个调用会启动一个Erlang节点后续的调用就会快很多.

-v 多打印一些冗余信息

-x ErlScript:指定Erlang启动脚本如果不指定就是用erl start-up脚本

 

比如我们现在要获取节点node_1的内存信息

/usr/local/lib/erlang/lib/erl_interface-3.7.6/bin/erl_call -s -a 'erlang memory ' -name node_1@192.168.1.123 -c abc

 

实践中遇到两个问题:

问题一:当name里面包含ip地址的时候执行会报错,这个erlang论坛里面有人也提到了:

erl_call can now use an IP address for remote node name  erlang.org/pipermail/e…

erl_call: remove get_hostent  erlang.org/pipermail/e…

我的解决方案就是把name换成了sname并去掉了ip地址就OK了;

 

问题二:在使用-e参数的时候怎么结束掉输入并查看结果

这个问题的确挺二的,就因为没有仔细阅读文档,结束的时候需要用Ctrl+D, www.erlang.org/doc/man/erl…

 

/usr/local/lib/erlang/lib/erl_interface-3.7.6/bin/erl_call -e -name node_1@192.168.1.123 -c abc

A=1,
B=2,
C=A+B.
%Ctrl + D 
{ok, 3}

 

 

SSH

  如果是在没有Erlang的机器接入怎么办?使用ssh即可,直接关闭SSH即可正常退出,如果接入有问题可以添加 -oLogLevel=DEBUG 输出debug信息看一下

 

 

1234567891011121314$ mkdir /tmp/ssh``$ ssh-keygen -t rsa -f /tmp/ssh/ssh_host_rsa_key``$ ssh-keygen -t rsa1 -f /tmp/ssh/ssh_host_key``$ ssh-keygen -t dsa -f /tmp/ssh/ssh_host_dsa_key``$ erl``1> application:ensure_all_started(ssh).``{ok,[crypto,asn1,public_key,ssh]}``2> ssh:daemon(8989, [{system_dir,  "/tmp/ssh" },``2> {user_dir,  "/home/ferd/.ssh" }]).``{ok,<0.52.0>} $ ssh -p 8989 ferd@127.0.0.1``Eshell Vx.x.x (abort with ^G)``1>

  

  

 有了这些方法,我们可以方便的了解和控制运行中的Erlang节点,洋溢着幸福感啊!

 

各位春节快乐!!!

另一种实用的接入erlang控制台的方法

能对运行中的erl系统进行控制是非常重要的一个福利,但是假如你的erl系统是后台运行的, 根本就没有shell可以让你输入。 

如果你的节点有name 那么可以用JCL 或者-remsh 接入. 否则的话 你就得用如下方法: 

请先参考 Embedded Systems User's Guide. 这种方式的好处是你的所有输入输出都记录在log文件里面 方便你日后查阅。 


先运行 
[root@localhost R13A]# which erl 
/usr/local/bin/erl 
确认下你的erl系统安装在那个路径。 

[root@localhost bin]# /usr/local/lib/erlang/bin/start 
但是我用的R12B5或者R13A发行版这样有点小问题 start没有运行起来, 我调查了半天发现有2个问题: 

1. run_erl的log是设定在/usr/local/lib/erlang/log但是没有这个目录, 通过运行mkdir /usr/local/lib/erlang/log搞定 

2. /usr/local/lib/erlang/releases/R13A/sys.config文件没有. 
其中 R13A可能是R12B5. 
通过运行echo "[]." >  /usr/local/lib/erlang/releases/R13A/sys.config搞定 

做了以上的步骤,现在运行 
[root@localhost ~]# ps -ef|grep beam 
root     19947 19946  0 03:35 pts/3    00:00:00 /usr/local/lib/erlang/erts-5.7/bin/beam.smp -- -root /usr/local/lib/erlang -progname start_erl -- -home /root -boot /usr/local/lib/erlang/releases/R13A/start -config /usr/local/lib/erlang/releases/R13A/sys 
确认beam已经运行, 同时/tmp/目录下有erlang.pipe.1.r  erlang.pipe.1.w 的pipe. 

如果还没有运行起来 那么就看下 tail /var/log/syslog 查明出错原因 

收获的时候到了 
[root@localhost bin]# to_erl 
Attaching to /tmp/erlang.pipe.3 (^D to exit) 
1> 
1> 
1> 
看到熟悉的shell提示符号了。退出的时候记得用^D, 而不是^C. 
\

\