Backup Linux через rsync: SSH-ключ, systemd timer и проверка восстановления
Ниже — рабочая схема резервного копирования каталога Linux на отдельный backup-сервер через rsync и SSH. В примере сохраняются права, ACL, xattrs и hard links, используется отдельный ключ, dry-run, systemd timer и обязательный restore-test.
1. Устанавливаем нужные пакеты
apt update
apt install rsync openssh-client acl attr
rsync --version
ssh -V2. Создаём отдельный SSH-ключ
install -d -m 700 /root/.ssh
ssh-keygen -t ed25519 -f /root/.ssh/rsync-backup -N '' -C 'server01 rsync backup'
chmod 600 /root/.ssh/rsync-backup
chmod 644 /root/.ssh/rsync-backup.pubПубличный ключ добавьте пользователю backup на backup-сервере. Проверяем вход:
ssh -i /root/.ssh/rsync-backup backup@backup01 'hostname; id; df -h /backup'3. Проверяем источник и метаданные
SOURCE=/srv/data
findmnt -T "$SOURCE"
du -sh "$SOURCE"
getfacl -p "$SOURCE" | head -50
getfattr -d -m- "$SOURCE" 2>/dev/null | head -504. Первый dry-run
rsync -aHAX --numeric-ids --itemize-changes --dry-run \
-e 'ssh -i /root/.ssh/rsync-backup -o BatchMode=yes' \
/srv/data/ backup@backup01:/backup/server01/data/Обратите внимание на завершающий слэш после /srv/data/: копируется содержимое каталога, а не сам каталог как дополнительный уровень.
5. Выполняем первую копию
rsync -aHAX --numeric-ids --info=stats2,progress2 \
--partial --partial-dir=.rsync-partial \
-e 'ssh -i /root/.ssh/rsync-backup -o BatchMode=yes' \
/srv/data/ backup@backup01:/backup/server01/data/
echo $?6. Проверяем результат
ssh -i /root/.ssh/rsync-backup backup@backup01 \
'du -sh /backup/server01/data; find /backup/server01/data -type f | wc -l'
rsync -aHAX --numeric-ids --checksum --dry-run --itemize-changes \
-e 'ssh -i /root/.ssh/rsync-backup -o BatchMode=yes' \
/srv/data/ backup@backup01:/backup/server01/data/Пустой итоговый список изменений при checksum-проверке означает, что содержимое совпадает. На больших объёмах --checksum создаёт нагрузку, поэтому не запускайте его слишком часто.
7. Создаём backup-скрипт
cat > /usr/local/sbin/rsync-backup.sh <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail
SRC=/srv/data/
DST=backup@backup01:/backup/server01/data/
KEY=/root/.ssh/rsync-backup
STAMP=/var/lib/rsync-backup/last-success
install -d -m 700 /var/lib/rsync-backup
rsync -aHAX --numeric-ids \
--partial --partial-dir=.rsync-partial \
--timeout=120 \
-e "ssh -i $KEY -o BatchMode=yes -o ConnectTimeout=15" \
"$SRC" "$DST"
touch "$STAMP"
EOF
chmod 700 /usr/local/sbin/rsync-backup.sh
/usr/local/sbin/rsync-backup.sh8. Добавляем systemd service и timer
cat > /etc/systemd/system/rsync-backup.service <<'EOF'
[Unit]
Description=Rsync backup to backup01
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/rsync-backup.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
EOF
cat > /etc/systemd/system/rsync-backup.timer <<'EOF'
[Unit]
Description=Daily rsync backup
[Timer]
OnCalendar=*-*-* 03:15:00
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now rsync-backup.timer
systemctl start rsync-backup.service
systemctl status rsync-backup.service --no-pager
systemctl list-timers rsync-backup.timer9. Контролируем последний успешный запуск
stat /var/lib/rsync-backup/last-success
journalctl -u rsync-backup.service -n 100 --no-pager
df -h
df -i10. Осторожно с —delete
Перед включением зеркальной очистки обязательно выполните:
rsync -aHAX --numeric-ids --delete --dry-run --itemize-changes \
-e 'ssh -i /root/.ssh/rsync-backup -o BatchMode=yes' \
/srv/data/ backup@backup01:/backup/server01/data/--delete допустим только при наличии snapshots или отдельной истории версий на backup-сервере. Иначе случайное удаление сразу уничтожит и копию.
11. Restore-test
install -d -m 700 /root/restore-test
rsync -aHAX --numeric-ids \
-e 'ssh -i /root/.ssh/rsync-backup -o BatchMode=yes' \
backup@backup01:/backup/server01/data/testdir/ /root/restore-test/testdir/
find /root/restore-test/testdir -type f -printf '%m %u:%g %p\n' | head
getfacl -p /root/restore-test/testdir | head -50
getfattr -d -m- /root/restore-test/testdir 2>/dev/null | head -50Для содержимого сравните checksum:
diff -u \
<(cd /srv/data/testdir && find . -type f -print0 | sort -z | xargs -0 sha256sum) \
<(cd /root/restore-test/testdir && find . -type f -print0 | sort -z | xargs -0 sha256sum)12. Rollback
systemctl disable --now rsync-backup.timer
rm -f /etc/systemd/system/rsync-backup.timer /etc/systemd/system/rsync-backup.service
systemctl daemon-reload
rm -f /usr/local/sbin/rsync-backup.shКритерий успеха: ручной и systemd-запуск возвращают код 0, timestamp последнего успеха обновляется, повторный dry-run не показывает неожиданных изменений, restore-test возвращает файлы с совпадающими checksum, ACL и xattrs.