rsync

Siehe auch

rsync dient der Synchronisation von Dateien und Verzeichnissen, auch über Rechnergrenzen hinweg. Mit rsync ist sowohl das Netzwerkprotokoll als auch die gleichnamige Programm-Implementierung gemeint.

Bei rsync läuft die Synchronisation immer unidirektional von einem Quellverzeichnis oder einer Datei (in den Beispielen src) zu einem Zielverzeichnis oder einer Datei ab (dest). Per Parameter lässt sich unter anderem steuern, ob Dateien im Zielverzeichnis auch gelöscht werden sollen, wenn sie im Quellverzeichnis nicht mehr existieren. Während der Synchronisation werden nur die geänderten Teile der Dateien übertragen (Delta-Kodierung), was Zeit spart. Abgebrochene Transfers werden ab der Stelle wieder aufgenommen, an denen wirklich abgebrochen wurde.

Endet eine Verzeichnisangabe mit einem /, wechselt rsync in das Verzeichnis, ignoriert den Verzeichnisnamen und verarbeitet alle Dateien innerhalb des Verzeichnisses; fehlt der abschliessende Slash, wird der Verzeichnisname berücksichtigt und dieser ebenfalls gesynct.

Installation:

dnf -y install rsync

Kurz- vs. Lang-Parameter:

  • -A: --acls

  • -D: --devices --specials

  • -g: --group

  • -H: --hard-links

  • -l: --links

  • -o: --owner

  • -p: --perms

  • -r: --recursive

  • -t: --times

  • -X: --xattrs

Nützliche Parameter:

  • --archive:
    Ist die Kurzform für -rlptgoD. Meist wird er durch --human-readable und --progress ergänzt.
  • --checksum
    rsync soll nicht anhand von Modifikationsdatum, sondern anhand on Checksummen arbeiten.
  • --delete:
    Löscht Dateien im Zielverzeichnis, die im Quellverzeichnis nicht mehr existieren.
  • --dry-run
    Test-Modus.
  • --exlude:
    Dateien und/oder Verzeichnisse von der Synchronisation ausnehmen. Der Parameter kann mit Wildcards arbeiten und mehrfach verwendet werden.
  • --one-file-system:
    Lokal bleiben, Dateien auf Netzwerk-Mounts ignorieren.
  • --password-file /path/to/secrets.rsync:
    Datei, die das Passwort für den Zugriff auf einen per rsync-Daemon exportiertes Verzeichnis enthält. Muss per chmod 0400 geschützt werden.
  • --times:
    Zeitstempel beim Kopieren behalten.
  • --update:
    Ist die Datei im Zielverzeichnis neuer, wird sie nicht kopiert.

Verwendung von rsync

Achtung

  • Kopiert KEINE versteckten Dateien: rsync source/* dest

  • Sync INKLUSIVE versteckter/hidden dot-Files: rsync source/ dest (also den Wildcard weglassen)

Der einfachste Aufruf: alle Dateien der obersten Verzeichnisebene synchronisieren:

rsync src dest

Eine Datei von lokal auf einen entfernten Server kopieren (ohne weitere Angaben wird das per SSH erledigt). Dabei „sudo“ verwenden:

rsync --archive --human-readable --progress --verbose --rsync-path='sudo rsync' src user@host:dest

Gewünschte Art der Remoteverbindung spezifizieren und für SSH zum Beispiel den Port ändern:

rsync --archive --human-readable --progress --verbose --rsh "ssh -p 2222" user@host:src dest

Eine Datei von lokal auf einen entfernten Server kopieren, hier per rsync:

rsync --archive --human-readable --progress --verbose src rsync://user@host:dest

Sync mit Ausnahmen von einem per rsync:// erreichbarem, entfernten Verzeichnis auf ein lokales Verzeichnis. Passwort wird nicht in einer Datei definiert, sondern als Umgebungsvariable gesetzt:

export RSYNC_PASSWORD=myrsyncpwd
rsync --archive --human-readable --progress --verbose --exclude=cache --exclude=files/*.pdf --delete rsync://user@host:src dest

Gegen einen rsync-Daemon authentifizieren; Passwort wird in einer Datei definiert:

secrets.rsync
mypassword
chmod 0600 secrets.rsync

rsync --human-readable --verbose --recursive --times --update --delete --password-file secrets.rsync src rsync://user@host:dest

Sync auf Basis einer Datei-Liste - nur ausgewählte Dateien auf einen Remote-Host kopieren. Hier wird ausgehend vom aktuellen Verzeichnis die relativ organisierte Datei-Liste abgearbeitet:

rsync --archive --human-readable --progress --files-from=myfilelist . host:dest

Alles bis auf einen Unterordner synchronisieren:

tree /tmp/src
# /tmp/src
# ├── config
# │   ├── data
# │   │   └── file
# │   ├── file1
# │   ├── file2
# │   └── file3
# └── data
#     ├── file1
#     ├── file2
#     └── file3

# test using --dry-run and --debug
rsync --progress --archive --dry-run --debug=FILTER --exclude=/data/ /tmp/src/ /tmp/dest
# sending incremental file list
# [sender] hiding directory data because of pattern /data/
# created directory /tmp/dest
# ./
# config/
# config/file1
# config/file2
# config/file3
# config/data/
# config/data/file

# actually run
rsync --progress --archive --exclude=/data/ /tmp/src/ /tmp/dest

Rsync nice und bandbreitenschonend laufen lassen, in Units per Second (hier KByte/sec):

/usr/bin/nice --adjustment 19 /usr/bin/rsync --bwlimit 1.5K --archive --human-readable --progress --verbose src dest

Daemon-Mode

dnf -y install rsync-daemon

Für den Daemon-Modus muss ein Username/Passwort und eine Liste der zu exporierenden Verzeichnisse konfiguriert werden.

/etc/rsyncd.secrets
myuser1:mypassword1
myuser2:mypassword2
chmod 0600 /etc/rsyncd.secrets

Im Beispiel wird /etc als etc und /var/www/html als var-www-html exportiert. Der anzugebene Benutzername gilt nur innerhalb von rsync, muss also nicht auf dem System existieren:

/etc/rsyncd.conf
 1# global parameters go here
 2auth users = myuser1,myuser2
 3gid = root
 4lock file = /var/lock/subsys/rsync.lock
 5log file = /var/log/rsyncd.log
 6pid file = /var/run/rsyncd.pid
 7read only = yes
 8secrets file = /etc/rsyncd.secrets
 9uid = root
10
11[etc]
12path = /etc
13
14[var-www-html]
15path = /var/www/html

In SELinux erlauben, dass diese (eigentlich alle) Verzeichnisse read-only exportiert werden dürfen:

setsebool -P rsync_export_all_ro on

# and, if more needed (for /var/spool/postfix for example)
setsebool -P rsync_full_access on

Start des Daemons:

systemctl enable --now rsyncd

Firewall einrichten und Port 873 (tcp und udp) freigeben, zum Beispiel per

firewall-cmd --permanent --add-service=rsyncd
firewall-cmd --reload

rsync-Logdateien über Logrotate aufräumen, im Beispiel nach 14 Tagen:

cat >/etc/logrotate.d/rsync <<EOF
/var/log/rsyncd.log {
    compress
    daily
    dateext
    missingok
    rotate 14
    size 1
}
EOF

Der Client synct dann beispielshalber per:

export RSYNC_PASSWORD=mypassword1
rsync --archive rsync://myuser1@host:/var-www-html .

Robustes Backup-Skript mit Deduplizierung

Das nachfolgende Backup-Skript arbeitet mit Backup-Ordnern beginnend bei „00“ zum Beispiel bis „99“, wobei „00“ das aktuellste und „99“ das älteste Backup enthält. Die Backups werden bei jedem Aufruf rotiert - das älteste Backup „99“ wird entfernt, aus „98“ wird „99“ usw., bis aus „00“ das Verzeichnis „01“ wird. Mit Hilfe von rsync wird synchronisiert, mit Hilfe von Hard-Links wird dedupliziert und nur das gesichert, was sich geändert hat. Jedes Verzeichnis enthält durch die durchgehende Verwendung von Hard-Links die gesamte Quell-Verzeichnisstruktur, so dass sich ein Restore sehr einfach durchführen lässt. Die zu sichernden Rechner und deren Verzeichnisse werden in einer CSV-Datei gepflegt.

mycoolbackup.sh
 1#!/usr/bin/env bash
 2
 3# call with parameter: "backup daily.conf|weekly.conf|... myhostlist.csv"
 4
 5# http://mywiki.wooledge.org/BashFAQ/045
 6LOCK_FILE=/var/lock/subsys/backup-$(basename $1 | cut -d. -f1)
 7if mkdir -- $LOCK_FILE; then
 8    trap 'rm -rf -- $LOCKFILE' EXIT # remove directory when script finishes
 9    trap 'rm -rf -- $LOCKFILE' TERM # remove directory when script gets terminated
10    echo $$ > "$LOCKFILE/pid"
11    logger "$0 successfully acquired lock."
12else
13    logger "$0 - Backup is already running, exiting..."
14    exit 1
15fi
16
17# include configuration file given via first parameter
18BACKUP_TYPE=$1
19BACKUP_LIST=$2
20source "$BACKUP_TYPE"
21
22# create structure for backups if it does not exist
23mkdir -p $BACKUP_DIR
24for (( i = 0; i <= $BACKUP_MAX ; i++ ))
25do
26    if [ ${#i} == 1 ]; then
27        mkdir -p $BACKUP_DIR/0$i
28    else
29        mkdir -p $BACKUP_DIR/$i
30    fi
31done
32
33# rotate backup folders
34\mv $BACKUP_DIR/$BACKUP_MAX $BACKUP_DIR/tmp
35for (( i = $BACKUP_MAX ; i > 0 ; i-- ))
36do
37    j=$(($i - 1))
38    if [ ${#j} == 1 ]; then
39        j="0$j"
40    fi
41    if [ ${#i} == 1 ]; then
42        \mv $BACKUP_DIR/$j $BACKUP_DIR/0$i
43    else
44        \mv $BACKUP_DIR/$j $BACKUP_DIR/$i
45    fi
46done
47\mv $BACKUP_DIR/tmp $BACKUP_DIR/00
48
49# now let's do the backup
50cd $BACKUP_DIR
51
52# the backup takes place here, based on CSV files
53while IFS=\; read -r SERVER SRC OPTIONS
54do
55    if [ "$SERVER" != "" ]; then
56        if [ "${SERVER:0:1}" != "#" ]; then
57
58            TS=$(date +"%Y-%m-%d %H:%M:%S")
59            mkdir -p 00/$SERVER
60
61            logger "-------------------------------------------------------------------------------"
62            logger "Job:"
63            logger "* Current Dir:     $(pwd)"
64            logger "* Source Dir:      $SERVER:$SRC"
65            logger "* Destination Dir: 00/$SERVER$SRC/"
66            logger "* Options:         $OPTIONS"
67            logger "* Start:           $TS"
68            logger "$0 - $TS - $SERVER:$SRC - Start"
69
70            rsync --rsh="ssh" --recursive --links --perms --group --owner --devices --specials \
71                  --human-readable --compress --compress-level=9 --checksum --delete \
72                  $OPTIONS --link-dest=../../01/$SERVER $SERVER:$SRC 00/$SERVER
73
74            TS=$(date +"%Y-%m-%d %H:%M:%S")
75            logger "* Stop:            $TS\n";
76
77            logger "$0 - $TS - $SERVER:$SRC - Stop"
78        fi
79    fi
80done < $BACKUP_LIST
81
82logger "-------------------------------------------------------------------------------"
83logger "Finished."
84
85logger "$0 - finished"

Eine passende Konfigurationsdatei dazu:

daily.conf
1#!/usr/bin/env bash
2
3# where to store the backups
4BACKUP_DIR=/backup/daily
5
6# number of backups: 01, 30, 99, whatever (don't forget the leading zero)
7BACKUP_MAX=99

Eine passende Serverliste, die angibt, welche Dateien und Verzeichnisse mit welchen Optionen zu sichern sind:

daily.csv
1# expecting "SERVER;SRC[; RSYNC-OPTIONS]" - don't forget an empty last line
2
3server1;/etc;--exclude-from=exclude-filelist.server.txt
4
5server2;/boot
6server2;/etc
7server2;/root

Aufruf mit:

mycoolbackup.sh daily.conf daily.csv

Troubleshooting

rsync: opendir „/.“ (in dest) failed: Permission denied (13)

Neustart des rsync-Daemons, oder der rsyncd wird durch SELinux daran gehindert, die konfigurierten Verzeichnisse zu exportieren (setsebool -P rsync_export_all_ro on ausführen).

rsync: rsync_xal_set: lremovexattr(„/backup“,“security.selinux“) failed: Permission denied (13)

Taucht bei Verwendung von --acls --xattrs mit Quelle:xfs und Ziel:ZFS auf.

rsync --archive erzeugt trotz bzw. zusammen mit --checksum und --link-dest keine Hard-Links, wenn sich der Timestamp einer Datei, aber nicht deren Inhalt geändert hat.

Hier hilft nur, --archive wegzulassen und selbst anzugeben, sowie die Option --time dabei zu entfernen (--archive = -rlptgoD no -H,-A,-X; daraus macht man --recursive, --links, --perms, --group, --owner, --acls, --xattrs, --devices --specials).

Built on 2024-04-18