Wie synchronisiere ich Dateien zwischen zwei Fernbedienungen?


54

Ich möchte Dateien zwischen zwei Remote-Hosts über die lokale Shell übertragen, aber es scheint, dass rsync die Synchronisierung nicht unterstützt, wenn zwei Remotes wie folgt angegeben sind:

$ rsync -vuar host1:/var/www host2:/var/www
The source and destination cannot both be remote.

Welche anderen Problemumgehungen / Befehle könnte ich verwenden, um ähnliche Ergebnisse zu erzielen?



1
Tatsächlich können Sie zwischen 2 Remote-Hosts eine Synchronisierung durchführen, indem Sie sshfs auf einem dritten Host einsetzen. Verwenden Sie einfach sshfs, um Host1 und Host2 auf Host 3 zu mounten. Dann rsync zwischen 1 und 2.
William Legg

@WilliamLegg Der Nachteil bei der Verwendung von sshfsist, dass dann rsyncdas Quell- und das Zieldateisystem als lokal angesehen werden und der Delta-Algorithmus deaktiviert wird. An diesem Punkt können Sie fast genauso gut einfach verwenden cp -p. Siehe die Antwort, die dies und die nachfolgenden Kommentare vorschlägt .
Roaima

Antworten:


50

Wie Sie festgestellt haben, können Sie rsync nicht mit einer Remote-Quelle und einem Remote-Ziel verwenden. Angenommen, die beiden Server können nicht direkt miteinander kommunizieren, ist es möglich, mit ssh über Ihren lokalen Computer zu tunneln.

Anstatt von

rsync -vuar host1:/var/www host2:/var/www

Sie können dies verwenden

ssh -R localhost:50000:host2:22 host1 'rsync -e "ssh -p 50000" -vuar /var/www localhost:/var/www'

Falls Sie sich fragen, -Rrichtet die Option einen Rückkanal von Port 50000 auf Host1 ein, der (über Ihren lokalen Computer) Port 22 auf Host2 zuordnet. Es gibt keine direkte Verbindung von Host1 zu Host2.


1
Ich mag die Lösung von @ roaima, konnte sie jedoch aus verschiedenen Gründen nicht zum Laufen bringen. Am Ende habe ich sshfsbeide Remote-Verzeichnisse lokal gemountet und dann rsyncüber die beiden lokal gemounteten Verzeichnisse hinweg verwendet.
Aidan

Ist es möglich, ein Beispiel zu sehen, in dem ein Schlüssel verwendet wird? Probleme beim Festlegen -ider für die ssh-Befehle erforderlichen Schlüssel.
Onassar

@onassar füge den -i key...Parameter in die Anführungszeichen nach dem sshBefehl ein. Wenn Ihnen das nicht hilft, können Sie
gerne

1
Die umgekehrte Verbindung liest nicht die ~ / .ssh / config auf der lokalen Seite - müssen etwas verwenden, das aufgelöst werden kann, als gäbe es keine SSH-Konfigurationsdatei
Florenz Kley

1
"Angenommen, die beiden Server können nicht direkt miteinander kommunizieren." Diese Lösung umgeht ein Firewall- oder NAT-Problem, das eine direkte SSH- Verbindung verhindert . Es wird jedoch nicht der Fall behandelt, in dem der Quellbenutzer (auf Host1) aus Sicherheitsgründen keinen Schlüssel, keine Anmeldeinformationen oder keine ausreichenden Schreibberechtigungen für das Ziel hat. Sehen Sie sich hierzu die Lösung von Kevin Cox an oder greifen Sie mithilfe eines Skripts oder auf eine indirekte Verbindung zu scp -3.
Cedric Knight

22

Sie haben nicht gesagt, warum Sie sich nicht bei einem Host anmelden und dann auf den anderen kopieren möchten, sodass ich einen meiner Gründe und Lösungen erläutere.

Ich konnte mich nicht bei einem Rechner anmelden und dann mit dem anderen synchronisieren, da keiner der Hosts einen SSH-Schlüssel hatte, der sich bei dem anderen anmelden konnte. Ich habe dieses Problem gelöst, indem ich die SSH-Agentenweiterleitung verwendet habe, damit der erste Host meinen SSH-Schlüssel verwenden kann, während ich angemeldet war.

WARNUNG: Durch die SSH-Weiterleitung kann der Host Ihren SSH-Schlüssel für die Dauer Ihrer Anmeldung verwenden. Obwohl sie Ihren Schlüssel nicht kopieren können, können sie sich damit auf anderen Rechnern anmelden. Stellen Sie sicher, dass Sie die Risiken verstehen, und verwenden Sie die Agentenweiterleitung nicht für Computer, denen Sie nicht vertrauen.

Der folgende Befehl verwendet die SSH-Agentenweiterleitung, um eine direkte Verbindung von host1zu herzustellen host2. Dies hat den Vorteil, dass der Computer, auf dem der Befehl ausgeführt wird, die Übertragung nicht beeinträchtigt.

ssh -A host1 rsync -vuar /var/www host2:/var/www

3
+1 zur Erläuterung eines gültigen Anwendungsfalls (wenn der ferne Benutzer auf Host1 keine Berechtigungen auf dem Zielserver hat); für die wichtige Sicherheitsvorkehrung (verwenden Sie die Portweiterleitung, -Danstatt das -ANetzwerk zu umgehen, und nicht die wichtigsten Einschränkungen); zur Erläuterung des Vorteils; weil der Befehl kurz ist; und es funktioniert tatsächlich. Beachten Sie, dass Sie möglicherweise angeben müssen, username@host1ob es sich um einen anderen als den lokalen Benutzernamen handelt. Außerdem führt rsync eine Überprüfung des Hostschlüssels durch, wenn eine Verbindung zu Host2 hergestellt wird. Daher sollte sich der Schlüssel von Host1 bereits in ~ / .ssh / known_hosts auf Host2 befinden, da sonst der Befehl fehlschlägt.
Cedric Knight

Die phänomenale Antwort: Dies hat mir geholfen, einige Dinge in TeamCity zu orchestrieren, die ich vorher nicht konnte (Hinweis: Für andere TeamCity-Benutzer müssen Sie Ihrer Build-Konfiguration das "Build-Feature" mit dem Namen "SSH-Agent" hinzufügen, bevor Sie es verwenden ssh -Akönnen. Siehe Zusammenfluss. jetbrains.com/display/TCD10/SSH+Agent ).
John Zwinck

12

Ich mag die Antwort von Roaima, aber die Wege sind in beiden Beispielen die gleichen und verdecken, welcher welcher ist. Wir haben festgestellt, dass Folgendes nicht funktioniert:

rsync -vuar host1:/host1/path host2:/host2/path

Dies ist jedoch der Fall (ich habe die explizite bind_address von localhost in der -ROption weggelassen, da dies die Standardeinstellung ist):

ssh -R 50000:host2:22 host1 'rsync -e "ssh -p 50000" -vuar /host1/path localhost:/host2/path'

Beachten Sie, dass ssh-Schlüssel zwischen den beiden Remote-Hosts korrekt eingerichtet sein müssen, wobei der private Schlüssel auf Host1 und der öffentliche Schlüssel auf Host2 vorhanden sein müssen.

Um die Verbindung zu debuggen, teilen Sie dies in zwei Teile und fügen Sie den ausführlichen Status hinzu:

localhost$ ssh -v -R 50000:host2:22 host1

Wenn dies funktioniert, haben Sie eine Shell auf Host1. Probieren Sie jetzt den Befehl rsync von host1 aus. Ich empfehle, dies in einem anderen Fenster zu tun, damit die ausführlichen ssh-Informationen nicht mit den rsync-Statusinformationen gemischt werden:

host1$ rsync -e "ssh -p 50000" -vuar /host1/path localhost:/host2/path

In meinem Beispiel sind die Pfade Quelle, Ziel. Das rsyncwird auf Host1 mit dem Ziel auf Host2 initiiert. (Sie hätten in einem Kommentar um Klarstellung bitten können.)
Roaima

1
Ich hätte es kommentiert, aber Sie können den Beitrag eines anderen nicht kommentieren, ohne einen Ruf von 50+ zu haben.
Jaybrau

7

Neuformatierung der Antwort von Roaima in der Bash-Skript-Syntax (und Hinzufügen von Zeilenfortsetzungszeichen '\' zur Klarheit) Ich habe zufällig Port 22000 ausgewählt ...

SOURCE_USER=user1
SOURCE_HOST=hostname1
SOURCE_PATH=path1

TARGET_USER=user2
TARGET_HOST=host2
TARGET_PATH=path2

ssh -l $TARGET_USER -A -R localhost:22000:$TARGET_HOST:22 \
$SOURCE_USER@$SOURCE_HOST "rsync -e 'ssh -p 22000' -vuar $SOURCE_PATH \
$TARGET_USER@localhost:$TARGET_PATH"

1
es scheint mir, dass alles, was Sie getan haben, ist, seine willkürlichen Hostnamen durch Variablen zu ersetzen?
Jeff Schaller

3
Ja, habe ich. Es verleiht mir Klarheit darüber, auf welchem ​​Quellcomputer sich das Ziel befindet und wohin die Quell- und Zielpfade führen. Ich habe eine Weile gebraucht, um all das herauszufinden, und es war nicht offensichtlich anhand einfacher Platzhalter-Hostnamen.
David I.

Beim nächsten Mal können Sie die Antwort eines anderen direkt verbessern, indem Sie sie bearbeiten.
Roaima

Diese Antwort war die Lösung für mich, da sie die Weiterleitung von ssh-Agenten (-A) mit dem umgekehrten Tunnel (-R) kombiniert.
Kamelthemammel

3

Der ideale Weg wäre, den rsyncauf einem dieser Server auszuführen . Aber wenn Sie kein Skript auf dem Remote-Server ausführen möchten. Sie könnten ein Skript auf Ihrem lokalen System ausführen und ein ssh ausführen und dort den rsync ausführen.

ssh user@$host1 <<ENDSSH >> /tmp/rsync.out 2>&1 rsync -vuar /var/www host2:/var/www ENDSSH

Wie Sie vielleicht wissen, führt rysnc auch eine einseitige Synchronisation durch. Wenn Sie eine bidirektionale Synchronisierung wünschen, können Sie sich osync ( https://github.com/deajan/osync ) ansehen . Ich benutze es und fand es hilfreich.


0

Nur als zusätzliche Info:

Wenn Sie einen Sprunghost verwenden, um die beiden anderen Maschinen zu verbinden, diese sich jedoch nicht direkt erreichen können, können Sie sshfs wie folgt als Medium zwischen diesen beiden Maschinen verwenden (auf dem Sprunghost):

$ mkdir ~/sourcepath ~/destpath
$ sshfs sourcehost:/target/dir ~/sourcepath
$ sshfs desthost:/target/dir ~/destpath
$ rsync -vua ~/sourcepath ~/desthpath

SSHFS stellt die beiden Pfade auf dem Sprunghost bereit und rsync verwaltet die Synchronisierung der Dateien wie immer (nur mit dem Unterschied, dass dies praktisch lokal erfolgt).


1
Beachten Sie, dass die Leistung schrecklich sein wird. Dies liegt daran, dass rsync zum Erkennen von Änderungen die Datei vom Quellserver liest und das Ganze über das Netzwerk überträgt. Das heißt, wenn Sie keine direkte Übertragung durchführen können, müssen Sie dies essen, wenn Sie rsync verwenden. Mounten Sie das Ziel auch nicht, es ist unnötig und führt dazu, dass rsync einige Standardeinstellungen ändert, da es denkt, dass es mit einer lokalen Festplatte kommuniziert.
Kevin Cox

0

Sie könnten einen rsyncd (Server) auf einem der Computer ausführen.

Dies ist der Ansatz, den ich verfolge, da ich ssh nicht verwenden möchte, um der Quelle (in Rsync-Form) den Zugriff auf das Ziel als root ohne Passwort zu ermöglichen (wie es erforderlich ist, um SSH-Tunneling mit Rsync in zu verwenden) ein Skript)

In meinem Fall habe ich einfach einen rsyncd-Server auf dem Zielcomputer eingerichtet, wobei nur ein Benutzer vom Quell-PC aus berechtigt war und rsync von der Quellseite aus verwendet wurde.

Funktioniert super.


0

Versuchen Sie dies. Für mich geht das.

ssh src_user@src_host 'rsync -av /src/dir/location/ dest_user@dest_host:/dest/dir/loc/'

0

Ein einfach zu verwendendes Skript

Im Laufe der Jahre habe ich dies viele Male mit mehr oder weniger den gleichen Tricks wie bei jeder anderen Antwort hier getan. Da es jedoch sehr einfach ist, einige Details falsch zu machen und viel Zeit damit zu verbringen, das Problem herauszufinden, habe ich mir das folgende Skript ausgedacht:

  1. Erleichtert die Angabe aller Details (Quelle, Ziel, Optionen)
  2. Testet inkrementell jeden einzelnen Schritt und gibt Feedback, wenn etwas schief geht, damit Sie wissen, was zu beheben ist.
  3. Umgeht Fälle, in denen ssh -AAuthentifizierungsdaten nicht weitergegeben werden können (ich weiß nicht, warum dies manchmal passiert, da die Problemumgehung einfacher war als die Ermittlung der Hauptursache).
  4. Endlich macht der Job.

Verwendung des Skripts

  1. Stellen Sie sicher, dass Sie von localhost zu beiden Hosts ssh können, ohne ein Kennwort einzugeben.
  2. Stellen Sie die Variablen in den ersten Zeilen des Skripts ein
  3. Führ es aus.

Wie es funktioniert

Wie gesagt, es werden die gleichen Tricks angewendet wie bei jeder anderen Antwort hier:

  • Die -ROption von ssh, von localhost zu host1 zu ssh, während gleichzeitig eine Portweiterleitung eingerichtet wird, die es host1 ermöglicht, eine Verbindung über localhost zu host2 ( -R localhost:$FREE_PORT:$TARGET_ADDR_PORT) herzustellen.
  • Die -AOption von ssh, um eine einfache Authentifizierung des zweiten ssh-Kanals zu ermöglichen

Mein das IST kompliziert! Gibt es einen einfacheren Weg?

Wenn Sie alle oder die meisten Bytes von der Quelle zum Ziel kopieren , ist die Verwendung um ein Vielfaches einfacher tar:

ssh $SOURCE_HOST "tar czf - $SOURCE_PATH" \
    | ssh $TARGET_HOST "tar xzf - -C $TARGET_PATH/"

Das Drehbuch

#!/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 someuser@1.2.3.4 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"

tarist großartig, wenn Sie eine einzelne (nicht inkrementelle) Übertragung haben und Ihre Übertragung in einem einzigen Durchgang abgeschlossen ist. Auf der anderen Seite werden rsyncmit Weiterleitungsgriffen Neustarts und inkrementelle Übertragungen durchgeführt.
Roaima

1
Natürlich @roaima - ich halte Teer nicht für gleichwertig. Ich habe diese Referenz für den Tag hinterlassen, an dem ich sie lesen werde, um ein Problem zu lösen, bei dem rsync nicht zu 100% erforderlich ist.
Ndemou

-1

Es ist möglich, tarvia sshzu verwenden, um die Dateien zu übertragen:

ssh -n user1@host1 'tar jcf - -C /var/www .' | ssh user2@host2 'tar jxvf - -C /var/www'

Ändern Sie den jParameter (für tar) an zzwei Stellen auf, wenn Sie das Archiv mit gzipanstelle von komprimieren möchten bzip2. Normalerweise bzip2ist die Komprimierung höher als gzip, aber langsamer. Ändern Sie sie entsprechend Ihren Anforderungen (siehe: bzip2 vs gzip ).

Verwandte Themen: Wie kopiert man zwischen zwei Remote-Hosts mithilfe von tar, das von einem Remote-Server hinter einer Firewall in SSH geleitet wird?


Alternativ (um die Bandbreite zu schonen, wegen der transparenten Komprimierung) ist es möglich, das sshfsentfernte Dateisystem als lokal einzubinden und rsyncwie gewohnt zu verwenden, z

$ sshfs user1@host1:/var/www /mnt
$ rsync -vuar /mnt user2@host2:/var/www

1
Durch das lokale Mounten des Dateisystems wird keine Bandbreite zwischen der Quelle und dem lokalen Computer gespart. Es wird jedoch Bandbreite zwischen dem lokalen und dem Ziel gespart (da es nicht lokal bereitgestellt wird). Die Verwendung von rsync auf diese Weise ist nur sinnvoll, wenn Sie versuchen, Bandbreite auf dem Ziel zu sparen.
Kevin Cox
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.