背景
在日常的运维过程中,经常会需要同时管理整个集群的所有node节点,如果一个节点一个节点的去维护那么效率势必将大大降低。有什么好的办法可以解决吗?SSH::Batch
就是一个很好的工具。
概述
基于并行 SSH、集合和间隔算术的集群操作。SSH::Batch 允许您使用 ~/.fornodesrc 配置文件中的变量和间隔/设置语法(或 SSH_BATCH_RC 环境指定的不同文件名)来命名集群。 例如:
$ cat ~/.fornodesrc
A=foo[01-03].com bar.org
B=bar.org baz[a-b,d,e-g].cn foo02.com
C={A} * {B}
D={A} - {B}
官网地址:metacpan.org/dist/SSH-Ba…
核心命令
fornodes
将模式匹配到机器主机列表。
$ cat > ~/.fornodesrc
ps=blah.ps.com bloo.ps.com boo[2-25,32,41-70].ps.com
as=ws[1101-1105].as.com
# use set operations to define new sets:
foo={ps} + {ps} * {as} - {ps} / {as}
bar = foo.com bar.org \
bah.cn \
baz.com
^D
$ fornodes'api[02-10].foo.bar.com''boo*.ps.com'
$ fornodes'tq[ab-ac].[1101-1105].foo.com'
$ fornodes'{ps} + {as} - ws1104.as.com'# set union and subtraction
$ fornodes'{ps} * {as}'# set intersect
SSH::Batch 允许您使用 ~/.fornodesrc 配置文件中的变量和间隔/设置语法(或 SSH_BATCH_RC 环境指定的不同文件名)来命名集群。 例如:
$ cat ~/.fornodesrc
A=foo[01-03].com bar.org
B=bar.org baz[a-b,d,e-g].cn foo02.com
C={A} * {B}
D={A} - {B}
其中集群 C 是集群 A 和 B 的交集,而 D 是位于 A 但不在 B 中的机器的 sef。然后您可以使用 SSH::Batch 的 fornodes 脚本查询机器主机列表:
$ fornodes'{C}'
bar.org foo02.com
$ fornodes'{D}'
foo01.com foo03.com
$ fornodes blah.com'{C} + {D}'
bar.org blah.com foo01.com foo02.com foo03.com
最佳实践始终是在 +、-、* 和 / 等集合运算符周围放置空格,以便在主机名中允许使用这些字符(尤其是破折号 -),如下所示:
$ fornodes'foo-bar-[a-d].com - foo-bar-c.com'
foo-bar-a.com foo-bar-b.com foo-bar-d.com
对于像 [a-z] 这样的范围,还有一种替代语法:[a..z]
要从特定范围中排除某些离散值,您需要设置减法:foo[1-100].com - foo[32,56].com
,或同等地,foo[1-31,33-55,57-100].com
fornodes 在 shell 编程中非常方便。 例如要测试集群A的80端口HTTP服务,简单地说
$fornode in `fornodes'{A}'`; \
docurl "'; \
done
atnodes
atnodes
是一个用perl写成的工具,它可以方便的在集群上执行命令,上述的命令就会在后面两个列表的主机上都执行一遍了。
atnodes "echo alias grep=\'grep -n --color\' >> ~/.bashrc " xxx.xx[1-10].com yyy.yy[1-10].com
在集群上运行命令。 (atnodes 在内部调用 fornodes。)
# run a command on the specified servers:
$ atnodes $'ps -fe|grep httpd''ws[1101-1105].as.com'
# multiple-arg command requires "--":
$ atnodes ls /opt/ --'{ps} + {as}''localhost'
# or use single arg command:
$ atnodes'ls /opt/''{ps} + {as}''localhost'# ditto
# specify a different user name and SSH server port:
$ atnodes hostname'{ps}'-u agentz -p 12345
# use -w to prompt for password if w/o SSH key (no echo back)
$ atnodes hostname'{ps}'-u agentz -w
# or prompt for password if both login and sudo are required...
$ atnodes'sudo apachectl restart''{ps}'-w
# or prompt for password for sudo only...
$ atnodes'sudo apachectl restart''{ps}'-W
# run sudo command if tty required...
$ atnodes -tty'sudo apachectl restart''{ps}'
# or specify a timeout:
$ atnodes'ping foo.com''{ps}'-t 3
atnodes
满足在远程集群上运行命令的常见要求。 例如:
# at the concurrency level of 6:
atnodes'ls -lh''{A} + {B}'my.more.com -c 6
tonodes
上传本地文件/目录到远程集群
$ tonodes /tmp/*.inst --'{as}:/tmp/'
$ tonodes foo.txt'ws1105*':/tmp/bar.txt
# use rsync instead of scp:
$ tonodes foo.txt'ws1105*':/tmp/bar.txt -rsync
$ tonodes -r /opt /bin/* --'ws[1101-1102].foo.com''bar.com':/foo/bar/
key2nodes
将 SSH 公钥(如果没有的话生成一个)推送到远程集群。
$ key2nodes 'ws[1101-1105].as.com'
一些小技巧
运行 sudo 命令
通常,我们希望运行需要 root 访问权限的命令,例如在远程计算机上安装软件包时。 因此,您必须告诉 atnodes 提示您输入密码:
$ atnodes 'sudo yum install blah' '{my_cluster}' -w
然后,您将收到“密码:”提示,然后输入远程密码(关闭回显)。由于远程 sshd 可能足够聪明,可以在(一小段)时间内“记住”sudo 密码,因此紧随其后的“sudo”可能会省略 -w 选项,如:
$ atnodes 'sudo mv ~/foo /usr/local/bin/' {my_cluster}
但请记住,您可以在短时间内使用不带密码的 sudo ;),如果您在使用 atnodes 执行 sudo 时看到以下错误消息
sudo: sorry, you must have a tty to run sudo
那么你应该添加选项 -tty,或者你可以注释掉服务器的 /etc/sudoers 文件中的“Defaults requiretty”行(最好只为你自己的帐户执行此操作)。
将自定义选项传递给底层 ssh
默认情况下,atnodes
依赖 Net::OpenSSH 来定位 OpenSSH 客户端可执行文件“ssh”。 但您可以定义 SSH_BATCH_SSH_CMD 环境来显式指定命令。 您可以使用 -ssh 选项进一步覆盖它。 (key2nodes 脚本还支持 SSH_BATCH_SSH_CMD 环境。 请注意,指定您自己的“ssh”也是使用 atnodes
时将更多选项传递给底层 OpenSSH 客户端可执行文件的一种方法:
$ cat > ~/bin/myssh
#!/bin/sh
# to enable X11 forwarding:
execssh -X"$@"
^D
$chmod+x ~/bin/myssh
$ export SSH_BATCH_SSH_CMD=~/bin/myssh
$ atnodes'ls -lh''{my_cluster_name}'
在您自己的 ssh 包装器脚本中使用“exec”非常重要,否则您可能会看到 atnodes
挂起。这个技巧也适用于 key2nodes
脚本。
对簇表达式使用通配符以节省输入
集群规范中的通配符可以节省大量输入。 假设你的 ~/.fornodesrc 文件中出现了 api10.foo.bar.baz.bah.com.cn :
$ cat ~/.fornodesrc
MyCluster=api[01-22].foo.bar.baz.bah.com.cn
那么如果您想在命令行上单独引用 api10.foo.bar.baz.bah.com.cn 节点,您可以只说 api10* 或 api10.*.com.cn 或更具体的内容。 但请谨慎使用通配符。 您可能在生成的主机列表中包含不需要的节点。 因此,当您在 atnodes 或 tonodes 中使用通配符时,最佳实践是使用 -l 选项,如下所示
$ atnodes 'rm -rf /opt/blah' 'api10*' -l
因此,atnodes 只会回显其将操作的确切主机列表,但不执行任何操作。 (这实际上是一次“空运行”。)检查后,您可以安全地删除 -l 选项并继续。
指定不同的 ssh 端口或用户名
您可能已经了解到可以使用 -u 和 -p 选项来指定非默认用户帐户或 SSH 端口。 但也可以将其作为集群规范表达式的一部分,而且通常更方便,可以放在 ~/.fornodesrc 中,也可以放在命令行上,如下所示:
$ cat > ~/.fornodesrc
# cluster A uses the default user name:
A=foo[01-25].com
# cluster B uses the non-default user name "jim" and a port 12345
B=jim@foo[26-28].com:12345
$ atnodes'ls -lh''{B} + bob@bar[29-31].org:5678'
还可以通过环境变量 SSH_BATCH_RC 指定与 ~/.fornodesrc 不同的 rc 配置文件。 例如,
export SSH_BATCH_RC=/opt/my-fornodes-rc
那么将使用文件 /opt/my-fornodes-rc 而不是默认的 ~/.fornodesrc 文件。
使用 -L 帮助按主机名 grep 输出
当管理数百甚至数千台机器时,按主机名 grep atnodes 或 tonodes 的输出通常会更方便。 -L 选项使 atnodes 和 tonodes 为远程命令(如果有)的每个输出行添加主机名前缀。 如在:
$ atnodes'top -b|head -n5''{my_big_cluster}'-L > out.txt 2>&1
$grep'some.specific.host.com'out.txt
指定超时以防止挂起
为 SSH 操作指定超时通常是明智的做法。 例如,如果网络流量静默 3 秒,以下命令将退出并打印错误消息:$ atnodes -t 3 'sleep 4' {my_cluster}
限制 tonode 使用的带宽以利于防火墙
如果您的 Intranet 防火墙对您的带宽使用很偏执,您可以使用 -b 选项告诉 tonodes 使用有限的带宽:$ tonodes my_big_file {my_cluster}:/tmp/ -b 8000
其中 8000 的单位为 Kbits/sec,因此传输速度不会超过 1 MByte/sec。
避免首次手动记录
当您使用 key2nodes 或 atnodes 访问从未手动登录过的远程服务器时,您可能会看到以下错误:
===================== foo.com =====================
Failed to spawn command.
ERROR: unable to establish master SSH connection: the authenticity of the target host can't be established,tryloging manually first
解决方法是使用“ssh”手动登录到 foo.com 计算机,然后再次尝试 key2nodes 或 atnodes。
另一个更好的解决方法是将 -o 'StrictHostKeyChecking=no' 选项传递给 SSH::Batch 使用的底层 ssh 可执行文件。 以下是快速操作方法:
$ cat > ~/bin/myssh
#!/bin/sh
# to disable StrictHostKeyChecking
execssh -o'StrictHostKeyChecking=no'"$@"
^D
$chmod+x ~/bin/myssh
$ export SSH_BATCH_SSH_CMD=~/bin/myssh
# then we try again
$ key2nodes foo.com
$ atnodes'hostname'foo.com