如何通过 SSH 远程自动运行命令到并行的多个服务器?

Posted

技术标签:

【中文标题】如何通过 SSH 远程自动运行命令到并行的多个服务器?【英文标题】:How can I automate running commands remotely over SSH to multiple servers in parallel? 【发布时间】:2010-09-19 14:45:33 【问题描述】:

我已经搜索了一些类似的问题,但除了运行一个命令或一些带有以下项目的命令之外:

ssh user@host -t sudo su -

但是,如果我基本上需要同时在(比方说)15 台服务器上运行脚本,该怎么办。这在 bash 中可行吗?在一个完美的世界中,如果可能的话,我需要避免安装应用程序。为了论证起见,假设我需要跨 10 个主机执行以下操作:

    部署新的 Tomcat 容器 在容器中部署一个应用,并配置它 配置 Apache 虚拟主机 重新加载 Apache

我有一个脚本可以完成所有这些工作,但它依赖于我登录所有服务器,从 repo 中提取一个脚本,然后运行它。如果这在 bash 中不可行,您有什么替代方案?我是否需要更大的锤子,例如 Perl(Python 可能是首选,因为我可以保证 Python 在 RHEL 环境中的所有机器上,这要归功于 yum/up2date)?如果有人可以向我指出任何有用的信息,我们将不胜感激,特别是如果它在 bash 中是可行的。我会满足于 Perl 或 Python,但我就是不知道这些(正在研究)。谢谢!

【问题讨论】:

使用sudo -sH 而不是sudo su - 您更想问的是如何远程执行多行,或者如何同时跨多个主机“并行”执行行?对于并行,请参阅serverfault.com/a/875812/27813 【参考方案1】:

您可以运行 che 和 Yang 所示的本地脚本,和/或您可以使用 Here 文档:

ssh root@server /bin/sh <<\EOF  
wget http://server/warfile    # Could use NFS here  
cp app.war /location  
command 1  
command 2  
/etc/init.d/httpd restart  
EOF 

【讨论】:

【参考方案2】:

通常,我只会使用 Expect 的原始 Tcl 版本。您只需要在本地计算机上安装它。如果我在使用 Perl 的程序中,我会使用 Net::SSH::Expect 执行此操作。其他语言也有类似的“期望”工具。

【讨论】:

走这条路。感谢您的提示(和编辑)。其他人有好主意,但我希望运行一个大约 300 行的脚本。 Perl 带来了一些 bash 无法提供的“优雅”。 (Perl..优雅..从没想过我会这么说):)【参考方案3】:

如何同时在多台服务器上运行命令的问题前几天出现在 Perl 邮件列表中,我将给出相同的建议 I gave there,即使用 gsh

http://outflux.net/unix/software/gsh

gsh 类似于“for box in box1_name box2_name box3_name”解决方案already given,但我发现 gsh 更方便。您设置了一个 /etc/ghosts 文件,其中包含您的服务器组,例如 web、db、RHEL4、x86_64 或其他(man ghosts)组,然后在调用 gsh 时使用该组。

[pdurbin@beamish ~]$ gsh web "cat /etc/redhat-release; uname -r"
www-2.foo.com: Red Hat Enterprise Linux AS release 4 (Nahant Update 7)
www-2.foo.com: 2.6.9-78.0.1.ELsmp
www-3.foo.com: Red Hat Enterprise Linux AS release 4 (Nahant Update 7)
www-3.foo.com: 2.6.9-78.0.1.ELsmp
www-4.foo.com: Red Hat Enterprise Linux Server release 5.2 (Tikanga)
www-4.foo.com: 2.6.18-92.1.13.el5
www-5.foo.com: Red Hat Enterprise Linux Server release 5.2 (Tikanga)
www-5.foo.com: 2.6.18-92.1.13.el5
[pdurbin@beamish ~]$

您还可以合并或拆分幽灵组,例如使用 web+db 或 web-RHEL4。

我还要提一下,虽然我从未使用过 shmux,但its website 包含一个软件列表(包括 gsh),可让您同时在多台服务器上运行命令。 Capistrano 已经被提及并且(据我了解)也可能在该列表中。

【讨论】:

【参考方案4】:

看看 Expect (man expect)

我过去曾使用 Expect 完成过类似的任务。

【讨论】:

【参考方案5】:

您可以将本地脚本通过管道传输到远程服务器并使用一个命令执行它:

ssh -t user@host 'sh' < path_to_script

这可以通过使用公钥身份验证和使用脚本包装以执行并行执行来进一步自动化。

【讨论】:

【参考方案6】:

你可以试试paramiko。这是一个纯 python ssh 客户端。您可以对 ssh 会话进行编程。无需在远程机器上安装任何东西。

请参阅great article,了解如何使用它。

【讨论】:

【参考方案7】:

为您提供结构,无需实际代码。

    使用 scp 将您的安装/设置脚本复制到目标框。 使用 ssh 在远程机器上调用您的脚本。

【讨论】:

这最终可能必须奏效。我希望有比这更漂亮的东西。最后,这真的只是减少了从 subversion 中拉下脚本,而导出 bash 脚本是最小的时间损失。 您可以使用单个脚本自动完成所有操作。无需您键入所有命令,以防不清楚,f4nt【参考方案8】:

pssh 可能很有趣,因为与这里提到的大多数解决方案不同,这些命令是并行运行的。

(我自己写了一个比较简单的小脚本,和GavinCattell的很相似,就是documented here - in french)。

【讨论】:

【参考方案9】:

您是否看过Puppet 或Cfengine 之类的内容。他们可以做你想做的事,而且可能更多。

【讨论】:

【参考方案10】:

对于那些偶然发现这个问题的人,我将提供一个使用 Fabric 的答案,它完全解决了上面描述的问题:通过 ssh 在多个主机上运行任意命令。

一旦安装了 fabric,您将创建一个fabfile.py,并实现可以在您的远程主机上运行的tasks。例如,重新加载 Apache 的任务可能如下所示:

from fabric.api import env, run

env.hosts = ['host1@example.com', 'host2@example.com']

def reload():
    """ Reload Apache """
    run("sudo /etc/init.d/apache2 reload")

然后,在您的本地机器上运行fab reloadsudo /etc/init.d/apache2 reload 命令将在env.hosts 中指定的所有主机上运行。

【讨论】:

不幸的是,Fabric v2 删除了大部分使它对这个用例有用的功能。【参考方案11】:

您可以像以前一样执行此操作,只需编写脚本而不是手动执行。以下代码远程连接到名为“loca”的机器并在那里运行两个命令。你需要做的只是插入你想在那里运行的命令。

che@ovecka ~ $ ssh loca 'uname -a;回声某事_else'
Linux 本地 2.6.25.9 #1 (blahblahblah)
something_else

然后,要遍历所有机器,请执行以下操作:

for box1_name box2_name box3_name
做
   ssh $box 'commmands_to_run_everywhere'
完成

为了使这个 ssh 事情在不输入密码的情况下一直工作,您需要设置密钥身份验证。你可以在IBM developerworks阅读它。

【讨论】:

不幸的是,这不会并行运行它们(尽管您可以在 ssh 命令的末尾添加 &amp; 并在底部添加 wait 并且它们会并行运行:) 【参考方案12】:

您可以使用cluster ssh 之类的工具一次在多台服务器上运行相同的命令。该链接是关于 Debian 软件包每日博客上关于集群 ssh 的讨论。

【讨论】:

【参考方案13】:

好吧,对于第 1 步和第 2 步,是否有一个 tomcat 管理器 Web 界面;你可以使用 curl 或 zsh 和 libwww 插件编写脚本。

对于 SSH,您希望: 1)不提示输入密码(使用密钥) 2) 在 SSH 的命令行上传递命令,这类似于受信任网络中的rsh

其他帖子已经向您展示了该怎么做,我可能也会使用sh,但我很想像ssh tomcatuser@server perl -e 'do-everything-on-one-line;' 一样使用perl,或者您可以这样做:

任一 scp the_package.tbz tomcatuser@server:the_place/.ssh tomcatuser@server /bin/sh &lt;&lt;\EOF定义类似 TOMCAT_WEBAPPS=/usr/local/share/tomcat/webappstar xj the_package.tbz rsync rsync://repository/the_package_placemv $TOMCAT_WEBAPPS/old_war $TOMCAT_WEBAPPS/old_war.oldmv $THE_PLACE/new_war $TOMCAT_WEBAPPS/new_wartouch $TOMCAT_WEBAPPS/new_war [你通常不需要重新启动 tomcat]mv $THE_PLACE/vhost_file $APACHE_VHOST_DIR/vhost_file$APACHECTL restart [可能需要以apache 用户移动该文件并重新启动]EOF

【讨论】:

【参考方案14】:

您需要 DSH 或分布式 shell,它在集群中被大量使用。这是链接:dsh

您基本上拥有节点组(其中包含节点列表的文件),并且您指定要在哪个节点组上运行命令,然后您将使用 dsh,就像您将 ssh 在它们上运行命令一样。

dsh -a /path/to/some/command/or/script

它将同时在所有机器上运行该命令并返回带有主机名前缀的输出。命令或脚本必须存在于系统中,因此共享的 NFS 目录可能对这类事情很有用。

【讨论】:

【参考方案15】:

为所有访问的机器创建主机名 ssh 命令。 通过奎拉蒂 http://pastebin.com/pddEQWq2

#Use in .bashrc
#Use "HashKnownHosts no" in ~/.ssh/config or /etc/ssh/ssh_config 
# If known_hosts is encrypted and delete known_hosts

[ ! -d ~/bin ] && mkdir ~/bin
for host in `cut -d, -f1 ~/.ssh/known_hosts|cut -f1 -d " "`;
  do
    [ ! -s ~/bin/$host ] && echo ssh $host '$*' > ~/bin/$host
done
[ -d ~/bin ] && chmod -R 700 ~/bin
export PATH=$PATH:~/bin 

前执行:

$for i in hostname1..10; do $i who;done

【讨论】:

【参考方案16】:

有一个名为FLATT (FLexible Automation and Troubleshooting Tool) 的工具允许您通过单击按钮在多个 Unix/Linux 主机上执行脚本。它是一个桌面 GUI 应用程序,可在 Mac 和 Windows 上运行,但也有一个命令行 java 客户端。 您可以创建批处理作业并在多个主机上重复使用。 需要 Java 1.6 或更高版本。

【讨论】:

【参考方案17】:

虽然这是一个复杂的话题,但我强烈推荐Capistrano。

【讨论】:

【参考方案18】:

我不确定这种方法是否适用于您想要的一切,但您可以尝试以下方法:

$ cat your_script.sh | ssh your_host bash

它将在远程服务器上运行脚本(驻留在本地)。

【讨论】:

【参考方案19】:

只需使用setsid 读取一个新的blog,除了主流内核之外无需任何进一步的安装/配置。在 Ubuntu14.04 下测试/验证。

虽然作者也有非常清晰的解释和示例代码,但这里有一个神奇的部分,可以快速浏览一下:

#----------------------------------------------------------------------
# Create a temp script to echo the SSH password, used by SSH_ASKPASS
#----------------------------------------------------------------------
SSH_ASKPASS_SCRIPT=/tmp/ssh-askpass-script
cat > $SSH_ASKPASS_SCRIPT <<EOL
#!/bin/bash
echo "$PASS"
EOL
chmod u+x $SSH_ASKPASS_SCRIPT

# Tell SSH to read in the output of the provided script as the password.
# We still have to use setsid to eliminate access to a terminal and thus avoid
# it ignoring this and asking for a password.
export SSH_ASKPASS=$SSH_ASKPASS_SCRIPT
......
......
# Log in to the remote server and run the above command.
# The use of setsid is a part of the machinations to stop ssh 
# prompting for a password.
setsid ssh $SSH_OPTIONS $USER@$SERVER "ls -rlt"

【讨论】:

【参考方案20】:

我发现无需安装或配置太多软件的最简单方法是使用普通的旧 tmux。假设您有 9 台 linux 服务器。选择一个盒子作为你的主要。开始一个 tmux 会话:

tmux

然后通过执行 8 次创建 9 个拆分 tmux 窗格:

ctrl-b + %

现在 SSH 进入每个窗格中的每个框。您需要了解一些 tmux 快捷方式。要导航,请按:

ctrl+b <arrow-keys>

一旦您登录到每个窗格上的所有框。现在打开窗格同步,它可以让您在每个框中输入相同的内容:

ctrl+b :setw synchronize-panes on

现在,当您按任意键时,它会显示在每个窗格中。要关闭它,只需打开关闭即可。要循环调整窗格大小,请按 ctrl+b 。

这对我来说效果更好,因为我需要查看每个终端输出,因为有时在下载或升级软件时服务器会因任何原因崩溃或挂起。任何问题,您都可以单独隔离和解决。

【讨论】:

以上是关于如何通过 SSH 远程自动运行命令到并行的多个服务器?的主要内容,如果未能解决你的问题,请参考以下文章

通过远程 ssh 运行完整命令 [重复]

如何在SSH断开后让远程服务器程序继续运行

使用ansible远程执行命令

.screenrc并登录到多个远程服务器

如何使用 SCP 或 SSH 将文件复制到 Python 中的远程服务器?

如何使用java通过ssh的方式登录远程服务器执行命令并返回结果