我想在本地 shell 上使用两个远程主机之间传输文件,但如果按如下方式指定两个远程主机,则 rsync 似乎不支持同步:
$ rsync -vuar host1:/var/www host2:/var/www
The source and destination cannot both be remote.
我可以使用哪些其他解决方法/命令来实现类似的结果?
答案1
正如您所发现的,您不能将 rsync 与远程源和远程目标一起使用。假设两台服务器无法直接相互通信,则可以使用 ssh 通过本地计算机建立隧道。
代替
rsync -vuar host1:/var/www host2:/var/www
你可以用这个
ssh -R localhost:50000:host2:22 host1 'rsync -e "ssh -p 50000" -vuar /var/www localhost:/var/www'
的第一个实例/var/www
适用于 上的源host1
,localhost:/var/www
对应于 上的目标host2
。
如果您好奇,该-R
选项会从主机 1 上的端口 50000 设置一个反向通道,该通道映射(通过本地计算机)到主机 2 上的端口 22。从主机 1 到主机 2 没有直接连接。
答案2
您没有说为什么您不想登录一台主机然后复制到另一台主机,所以我将分享我的原因和解决方案之一。
我无法登录一台机器然后 rsync 到另一台机器,因为两台主机都没有可以登录另一台机器的 SSH 密钥。我通过使用 SSH 代理转发来解决这个问题,以允许第一台主机在我登录时使用我的 SSH 密钥。
警告:SSH 转发允许主机在您登录期间使用您的 SSH 密钥。虽然他们无法复制您的密钥,但他们可以使用它登录其他机器。确保您了解风险,并且不要对您不信任的机器使用代理转发。
以下命令将使用 SSH 代理转发打开从host1
到 的直接连接host2
。这样做的优点是运行命令的机器不会成为传输的瓶颈。
ssh -A host1 rsync -vuar /var/www host2:/var/www
答案3
我喜欢 roaima 的答案,但两个例子中的路径是相同的,模糊了哪个是哪个。我们已经确定以下方法不起作用:
rsync -vuar host1:/host1/path host2:/host2/path
但这确实如此(我从-R
选项中省略了 localhost 的显式绑定地址,因为这是默认值):
ssh -R 50000:host2:22 host1 'rsync -e "ssh -p 50000" -vuar /host1/path localhost:/host2/path'
请注意,您必须在两个远程主机之间正确设置 ssh 密钥,私钥位于 host1 上,公钥位于 host2 上。
要调试连接,请将其分为两部分并添加详细状态:
localhost$ ssh -v -R 50000:host2:22 host1
如果这有效,您将在 host1 上拥有一个 shell。现在尝试从 host1 执行 rsync 命令。我建议在不同的窗口中执行此操作,以便详细的 ssh 信息不会与 rsync 状态信息混合在一起:
host1$ rsync -e "ssh -p 50000" -vuar /host1/path localhost:/host2/path
答案4
一个易于使用的脚本
多年来,我已经多次使用与这里其他答案中或多或少相同的技巧来完成此操作。然而,因为很容易出现一些细节错误并花费大量时间来解决问题,所以我想出了下面的脚本:
- 可以轻松指定所有详细信息(源、目的地、选项)
- 逐步测试每一个步骤,并在出现问题时提供反馈,以便您知道要修复什么。
- 解决
ssh -A
无法传播身份验证数据的情况(不知道为什么有时会发生这种情况,因为解决方法比查找根本原因更容易) - 终于完成了工作。
如何使用脚本
- 确保您可以从本地主机 ssh 到两台主机没有输入密码。
- 在脚本的前几行设置变量
- 执行它。
怎么运行的
正如我所说,它使用与此处所有其他答案相同的技巧:
- ssh 的
-R
选项是从本地主机 ssh 到主机 1,同时设置端口转发,然后允许主机 1 通过本地主机连接到主机 2 (-R localhost:$FREE_PORT:$TARGET_ADDR_PORT
) - ssh 的
-A
选项允许轻松验证第二个 ssh 通道
我的这很复杂!有没有更简单的方法?
将所有或大部分字节从源复制到目标时远的更容易使用tar
:
ssh $SOURCE_HOST "tar czf - $SOURCE_PATH" \
| ssh $TARGET_HOST "tar xzf - -C $TARGET_PATH/"
剧本
#!/bin/bash
#-------------------SET EVERYTHING BELOW-------------------
# whatever you type after ssh to connect to SOURCE/TARGE host
# (e.g. 1.2.3.4:22, user@host:22000, ssh_config_alias, etc)
# So if you use "ssh foo" to connect to SOURCE then
# you must set SOURCE_HOST=foo
SOURCE_HOST=host1
TARGET_HOST=host2
# The IP address or hostname and ssh port of TARGET AS SEEN FROM LOCALHOST
# So if ssh -p 5678 [email protected] will connect you to TARGET then
# you must set TARGET_ADDR_PORT=1.2.3.4:5678 and
# you must set TARGET_USER=someuser
TARGET_ADDR_PORT=1.2.3.4:5678
TARGET_USER=someuser
SOURCE_PATH=/mnt/foo # Path to rsync FROM
TARGET_PATH=/mnt/bar # Path to rsync TO
RSYNC_OPTS="-av --bwlimit=14M --progress" # rsync options
FREE_PORT=54321 # just a free TCP port on localhost
#---------------------------------------------------------
echo -n "Test: ssh to $TARGET_HOST: "
ssh $TARGET_HOST echo PASSED| grep PASSED || exit 2
echo -n "Test: ssh to $SOURCE_HOST: "
ssh $SOURCE_HOST echo PASSED| grep PASSED || exit 3
echo -n "Verifying path in $SOURCE_HOST "
ssh $SOURCE_HOST stat $SOURCE_PATH | grep "File:" || exit 5
echo -n "Verifying path in $TARGET_HOST "
ssh $TARGET_HOST stat $TARGET_PATH | grep "File:" || exit 5
echo "configuring ssh from $SOURCE_HOST to $TARGET_HOST via locahost"
ssh $SOURCE_HOST "echo \"Host tmpsshrs; ControlMaster auto; ControlPath /tmp/%u_%r@%h:%p; hostname localhost; port $FREE_PORT; user $TARGET_USER\" | tr ';' '\n' > /tmp/tmpsshrs"
# The ssh options that will setup the tunnel
TUNNEL="-R localhost:$FREE_PORT:$TARGET_ADDR_PORT"
echo
echo -n "Test: ssh to $SOURCE_HOST then to $TARGET_HOST: "
if ! ssh -A $TUNNEL $SOURCE_HOST "ssh -A -F /tmp/tmpsshrs tmpsshrs echo PASSED" | grep PASSED ; then
echo
echo "Direct authentication failed, will use plan #B:"
echo "Please open another terminal, execute the following command"
echo "and leave the session running until rsync finishes"
echo "(if you're asked for password use the one for $TARGET_USER@$TARGET_HOST)"
echo " ssh -t -A $TUNNEL $SOURCE_HOST ssh -F /tmp/tmpsshrs tmpsshrs"
read -p "Press [Enter] when done..."
fi
echo "Starting rsync"
ssh -A $TUNNEL $SOURCE_HOST "rsync -e 'ssh -F /tmp/tmpsshrs' $RSYNC_OPTS $SOURCE_PATH tmpsshrs:$TARGET_PATH"
echo
echo "Cleaning up"
ssh $SOURCE_HOST "rm /tmp/tmpsshrs"