我有一台本地计算机,它应该与远程master
计算机建立 SSH 会话,然后从master
每个远程计算机建立另一个内部 SSH 会话slaves
,然后执行 2 个命令,即删除特定目录并重新创建它。
请注意,本地计算机具有到主服务器的无密码 SSH,并且主服务器具有到从服务器的无密码 SSH。此外,.ssh/config
本地/主机中的所有主机名都是已知的,从机的主机名位于slaves.txt
本地,我从那里读取它们。
所以我所做的和工作是这样的:
username="ubuntu"
masterHostname="myMaster"
while read line
do
#Remove previous folders and create new ones.
ssh -n $username@$masterHostname "ssh -t -t $username@$line "rm -rf Input Output Partition""
ssh -n $username@$masterHostname "ssh -t -t $username@$line "mkdir -p EC2_WORKSPACE/$project Input Output Partition""
#Update changed files...
ssh -n $username@$masterHostname "ssh -t -t $username@$line "rsync --delete -avzh /EC2_NFS/$project/* EC2_WORKSPACE/$project""
done < slaves.txt
该集群位于 Amazon EC2 上,我注意到每次迭代都会创建 6 个 SSH 会话,这会导致明显的延迟。我想将这 3 个命令合并为 1 个,以减少 SSH 连接。所以我尝试将前两个命令组合成
ssh -n $username@$masterHostname "ssh -t -t $username@$line "rm -rf Input Output Partition && mkdir -p EC2_WORKSPACE/$project Input Output Partition""
But it doesn't work as expected. It seems to execute the first one (rm -rf Input Output Partition
) and then exits the session and goes on. What can I do?
答案1
考虑这&&
是一个逻辑运算符。确实如此不是意思是“也运行这个命令”,它的意思是“如果其他命令成功则运行这个命令”。
这意味着如果rm
命令失败(如果三个目录中的任何一个不存在就会发生这种情况),则mkdir
不会执行。这听起来不像您想要的行为;如果这些目录不存在,那么创建它们可能就可以了。
使用;
分号;
用于分隔命令。这些命令按顺序运行,等待每个命令后再继续执行下一个命令,但它们的成功或失败不会相互影响。
转义内引号
其他引号内的引号应进行转义,否则您将创建额外的终点和起点。您的命令:
ssh -n $username@$masterHostname "ssh -t -t $username@$line "rm -rf Input Output Partition && mkdir -p EC2_WORKSPACE/$project Input Output Partition""
变成:
ssh -n $username@$masterHostname "ssh -t -t $username@$line \"rm -rf Input Output Partition && mkdir -p EC2_WORKSPACE/$project Input OutputPartition\""
由于缺少转义引号,您当前的命令应该执行:
ssh -n $username@$masterHostname "ssh -t -t $username@$line "rm -rf Input Output Partition
如果成功的话:
mkdir -p EC2_WORKSPACE/$project Input Output Partition"" # runs on your local machine
您会注意到语法突出显示将整个命令显示为红色,这意味着整个命令是传递给 ssh 的字符串。检查你的本地机器;您可能有目录Input
Output
以及Partition
运行它的位置。
答案2
您始终可以在 Jumpbox 中定义OpenSSH 中的多路复用
多路复用是一种通过单条线路或连接发送多个信号的能力。通过多路复用,OpenSSH 可以将现有的 TCP 连接重新用于多个并发 SSH 会话,而不是每次都创建一个新连接。
SSH 多路复用的优点是消除了创建新 TCP 连接的开销。机器可以接受的连接总数是有限的资源,并且该限制在某些机器上比其他机器上更明显,并且根据负载和使用情况而变化很大。打开新连接时也会出现明显的延迟。使用多路复用可以显着加快重复打开新连接的活动。
为此,请执行以下操作/etc/ssh/ssh_config
:
ControlMaster auto
ControlPath ~/.ssh/controlmasters/ssh_mux_%h_%p_%r
ControlPersist 30m
这样,在接下来的 30 分钟内对同一服务器进行的任何连续连接都将重复使用之前的 ssh 连接。
您还可以为一台机器或一组机器定义它。取自提供的链接。
Host machine1
HostName machine1.example.org
ControlPath ~/.ssh/controlmasters/%r@%h:%p
ControlMaster auto
ControlPersist 10m
答案3
您可以将所有命令放入“主”服务器上的单独脚本中。
主脚本
#!/bin/bash
rm -rf "Input Output Partition"
mkdir -p "EC2_WORKSPACE/$project Input Output Partition"
然后在你的 ssh 脚本中这样调用它: SSH脚本
username="ubuntu"
masterHostname="myMaster"
while read line
do
ssh -n $username@$masterHostname "ssh -t -t $username@$line < /path/to/masterscript.sh"
ssh -n $username@$masterHostname "ssh -t -t $username@$line "rsync --delete -avzh /EC2_NFS/$project/* EC2_WORKSPACE/$project""
done < slaves.txt
或者 如果所有文件都必须位于初始计算机上,您可以执行以下操作:
脚本1
script2="/path/to/script2"
username="ubuntu"
while read line; do
cat $script2 | ssh -t -t $username@line
done < slaves.txt
脚本2
#!/bin/bash
rm -rf "Input Output Partition"
mkdir -p "EC2_WORKSPACE/$project Input Output Partition"
rsync --delete -avzh "/EC2_NFS/$project/* EC2_WORKSPACE/$project"
ssh脚本
script1="/path/to/script1"
username="ubuntu"
masterHostname="myMaster"
cat $script1 | ssh -n $username@$masterHostname
答案4
前段时间,我有机会像其他答案推荐的那样使用控制套接字(这个答案本质上是使用控制套接字的组合,例如这个答案和像这样的脚本这个答案)。
该用例是一个黑客:authorized_keys
目标用户的文件被计划任务定期覆盖,我想快速测试一些东西,而无需通过向该文件添加某些内容所需的繁文缛节。因此,我设置了一个 while 循环,根据需要将密钥添加到该文件,运行我的测试,然后取消循环。但是,会有一个小窗口,计划任务将覆盖该文件,而我的循环仍将sleep
运行。因此,在开始时设置一个控制套接字将使我的脚本稍后可以顺利进行 SSH:
#! /bin/bash -xe
. "${CONFIG_DIR}/scripts/setup-ssh.sh"
# Build and test
export TEST_LABEL="${_started_by}-${BUILD_TAG%-BUILD*}"
#...
xargs --arg-file test-list \
--no-run-if-empty \
--process-slot-var=NUM \
--max-procs=${#SERVERS[@]} \
--max-args="${BATCH_SIZE:-20}" \
"${CONFIG_DIR}/scripts/run-test.sh"
哪里setup-ssh.sh
:
export SSH_CONFIG="${CONFIG_DIR}/scripts/.ssh-config"
mapfile -t SERVERS < "${CONFIG_DIR}/scripts/hosts"
for SERVER in "${SERVERS[@]}"
do
while ! ssh -F "${SSH_CONFIG}" "${SERVER}" -fnN; do sleep 1; done
scp -F "${SSH_CONFIG}" "${CONFIG_DIR}/scripts/ssh-script.sh" "${SERVER}":"${TEST_LABEL}.sh"
done
和.ssh-config
:
Host test-*
User test
StrictHostKeyChecking no
ControlMaster auto
ControlPath /tmp/ssh-%h-%p-%r
和run-test.sh
:
mapfile -t TEST_SERVERS < "${CONFIG_DIR}/scripts/hosts"
ssh -F "${SSH_CONFIG}" "${TEST_SERVERS[$NUM]}" "./${TEST_LABEL}.sh"
顺序是这样的:
- 主要脚本(首先显示)来源
setup-ssh.sh
。 setup-ssh.sh
忙循环服务器,直到所有服务器都设置了控制套接字。该hosts
文件仅每行列出一个服务器主机名。- 由于指定控制套接字的配置仅在 中
${CONFIG_DIR}/scripts/.ssh-config
,除非我使用 指定该文件-F
,否则 SSH 连接将不会使用它。因此,这允许我仅在需要使用该F
选项的地方使用控制套接字。 - 设置脚本还将测试执行脚本复制到服务器。执行脚本本身包含一堆命令,并且因为我复制了执行脚本,所以我不必担心 SSH 的额外引用层(以及用于确定何时扩展什么的额外认知开销)。
- 然后,主脚本
xargs
通过在运行的作业结束后立即启动新作业来在服务器上分配工作负载。