computer:ubuntu:restic-backup-docker-hermes

This is an old revision of the document!


Restic Backup im Netzwerk

Übersicht über alle Backups: http://resticinfo.local

hermes macht Pull-Backups von sich selbst und den Servern florapower und netcup mit hilfe des Scripts /usr/local/bin/run-restic.sh

( von https://fedoramagazine.org/automate-backups-with-restic-and-systemd/ )
Systemd erlaubt das Starten von Services auch einmal/Tag.

sudo systemctl edit –force –full restic.service

# from https://fedoramagazine.org/automate-backups-with-restic-and-systemd/
# Änderungen mitteilen mit systemctl daemon-reload
# Starten mit systemctl start restic.service
# Als Dienst einrichten mit systemctl enable restic.service
# Dienst prüfen mit systemctl status restic.service
[Unit]
Description=Restic backup service
[Service]
Type=oneshot
ExecStart=/usr/bin/restic backup --host $HOST --verbose --one-file-system --tag systemd.timer $BACKUP_EXCLUDES $BACKUP_PATHS
EnvironmentFile=/root/restic-backup.conf

Environment-File

HOME=/root
HOST=denkbrett
RESTIC_REPOSITORY=rest:https://<abc>:<def>@restic.<meine.domain>
RESTIC_PASSWORD_FILE=/root/resticpw
BACKUP_PATHS="/root /etc /home/springm /root /usr/local /var/spool/cron/crontabs"
BACKUP_EXCLUDES="--exclude-file /root/restic-exclude.txt --exclude-if-present .exclude_from_backup"

Das Backup sollte im Prinzip täglich von hermes getriggert als Pull-Backup laufen, allerdings sind diese Rechner nicht immer zur festgesetzten Backup-Zeit eingeschaltet.
Die Alternative ist ein Push-Backup, das vom Client getriggert entweder nach dem Booten oder nach dem Aufwachen aus dem Suspend/Hibernate stattfindet.

Achtung: Das Paket at muss installiert sein, überprüfen als root am einfachsten mit atq

Das Network-Connect-Script:

/etc/network/if-up.d/run_restic_backup_once_a_day.sh
#!/bin/bash
LOCKFILE=/var/lib/restic/run_restic_once_a_day.lock
LOG=/tmp/run_restic_backup_once_a_day.log
echo "">>$LOG && echo $(date) >> $LOG
if [ -e "$LOCKFILE" ]; then
  echo Check if the lock file is from today >> $LOG
  if [ "$(date +%Y-%m-%d)" != "$(cat $LOCKFILE)" ]; then
    echo Lock file is from a previous day, remove it >> $LOG
    rm "$LOCKFILE"
  else
    echo Lock file is from today, exit >> $LOG
    exit 0
  fi
fi
echo Run restic backup >> $LOG
logger -p local0.notice -s "Running restic backup"
/usr/bin/date +%Y-%m-%d > $LOCKFILE && /usr/bin/touch $LOCKFILE # create lockfile immediately
sleep 30 
HOME=/root RESTIC_REPOSITORY=rest:https://test:test@restic.hermes.markus-spring.info /usr/bin/restic -v --password-file /root/resticpw --host denkbrett --exclude-caches --exclude-file=/root/restic-exclude.txt backup /etc /home/springm /root /usr/local /var/spool/cron/crontabs 2>>$LOG 
if [[$? -eq 0]];
then
  echo "Backup successful">>$LOG
else
  rm $LOCKFILE
fi

Das Resume-Script:

/usr/lib/systemd/system-sleep/run_restic_backup_once_a_day
#!/bin/sh
set -e
# logger -p local0.notice -s "Running $0 with event $1"
RESTIC_START_SCRIPT=/etc/network/if-up.d/run_restic_backup_once_a_day.sh
case "$1" in
    post)
        logger -p local0.notice -s $0 event $1 submitting restic to at-daemon
        echo $RESTIC_START_SCRIPT | at now
esac

Achtung: lt https://man.archlinux.org/man/systemd-sleep.8 : Note that scripts or binaries dropped in /usr/lib/systemd/system-sleep/ are intended for local use only and should be considered hacks. If applications want to react to system suspend/hibernation and resume, they should rather use the Inhibitor interface[1]

sudo systemctl edit --force --full restic_run_when_network_up.service
[Unit]
Description=Run restic backup after network available
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=root
#ExecStart=/etc/network/if-up.d/run_restic_backup_once_a_day.sh
ExecStart=/usr/local/bin/run_restic_backup_once_a_day.sh

[Install]
WantedBy=multi-user.target
sudo systemctl enable restic_run_when_network_up.service
sudo systemctl start restic_run_when_network_up.service

from: https://askubuntu.com/questions/41400/how-do-i-make-the-script-to-run-automatically-when-tun0-interface-up-down-events

Ein inotify-Eintrag auf das snapshots-Verzeichnis im Restic-Repository triggert das Schreiben einer JSON-Zustandsdatei mit dem Befehl

restic snapshots -c --json > /var/lib/restic/snapshots.json

Die Automatisierung erfolgt über einen systemd-Service (vgl. https://www.putorius.net/systemd-path-units.html)

/etc/systemd/system/restic-snapshots-mon.path
[Unit]
Description="Monitor restic snapshots for changes"
 
[Path]
PathModified=/backupdisk/restic-repo/snapshots/
Unit=restic-snapshots-mon.service
 
[Install]
WantedBy=multi-user.target
/etc/systemd/system/restic-snapshots-mon.service
[Unit] 
Description="Recreate /var/lib/restic/snapshots.json when snapshots have changed"
 
[Service]
ExecStart=/usr/local/sbin/restic_json.sh
/usr/local/sbin/restic-json.sh
#!/bin/bash
RESTIC_PASSWORD=abc RESTIC_REPOSITORY=/backupdisk/def /usr/bin/restic snapshots -c --json > /tmp/restic.json

Analysieren mit systemd-analyze verify /etc/systemd/system/restic-snapshots-mon*
Starten mit systemctl start restic-snapshots-mon.path
Beim Systemstart hochfahren mit systemctl enable restic-snapshots-mon.path
Status überprüfen mit systemctl status restic-snapshots-mon.path
Log-Einträge ansehen mit journalctl -u restic-snapshots-mon.path
Erzeugen der /tmp/restic.json beim booten durch einen cron-eintrag

@reboot /usr/local/sbin/restic-json.sh

Ein Docker-Container mit dem Programm restic2html stellt die JSON-Datei als sortierbare und entsprechend des Snapshot-Alters zeilenweise eingefärbte HTML-Tabelle dar.

docker-compose.yml
  services:
    restic2html:
      image: restic2html
      container_name: restic2html
      build:
        context: .
        dockerfile: Dockerfile
      restart: always
      environment:
        - PUID=1000
        - PGID=1000
        - TZ=Europe/Berlin
      volumes:
        - /tmp:/tmp
      ports:
        - 5001:5001
      environment:
        - TZ=${TZ:-Europe/Berlin}
      labels:
        - "traefik.enable=true" # <== Enable traefik on itself to view dashboard and assign subdomain to view it
        - "traefik.http.services.resticinfo.loadbalancer.server.port=5001"
        - "traefik.http.routers.resticinfo.rule=Host(`resticinfo.local`)"
      networks:
        - proxy
  
  networks:
    proxy:
      external: true

Der Container ist nur aus dem Intranet unter https://resticinfo.local erreichbar.

ist nötig, um überflüssige Backups zu löschen.

restic forget --keep-daily 6 --keep-weekly 4 --keep-monthly 6 --keep-yearly 99 --keep-tag yearly

Auf restic forget muss restic prune folgen, um den Löschvorgang durchzuführen

restic mount /resticrestore

Kann z.b. mit –host kudell eingeschränkt werden.

Das Verzeichnis kann über nfs freigegeben werden, z.B. folgender Eintrag in /etc/exports:

/resticrestore kudell.fritz.box(ro,async,no_subtree_check,crossmnt,fsid=0)

Achtung: dieser Prozess bleibt im Terminal geöffnet. Sicherstellen dass der Mount wieder aufgehoben wird!

Zur Vereinfachung der Befehlseingabe können sowohl Repository als auch Paßwort im Environment gesetzt werden.

export RESTIC_REPOSITORY=/repo/restic RESTIC_PASSWORD=hättestduwohlgerne

rsnapshot-Sicherungen können importiert werden - das dauert! - und mit dem Tag “historisch” versehen werden.

Tags können nachträglich hinzugefügt werden, wenn kein anderer restic-Vorgang läuft.

restic tag --set historisch ba37903f

Der restic-Befehl “forget” kann mit –keep-tag angewiesen werden, diese historischen Snapshots nicht zu löschen. In einer Übergangszeit müssen dann monatliche, wöchentliche und tägliche Snapshots manuell gelöscht werden.

Das Importieren der historischen rsnapshot-Daten vorbereiten mit

export HOST=hermes; \
ls -lt --time-style="+%Y-%m-%d" /backupdisk/$HOST/ | \
grep -v total | \
perl -pe 's!.*\s(\d+-\d+-\d+)\s(.*?)\.(\d+)$!restic backup --host $ENV{HOST} --tag historisch --tag $1 --tag $2 /backupdisk/$ENV{HOST}/$2.$3!'

Backup-Server induziertes backup mit
Example command on the server:

ssh -R 127.0.0.1:20022:<backup server IP>:22 <client IP> "sudo restic -r 'sftp:127.0.0.1:/path/to/repo' -p .resticpw backup -x /"

For that to work you need:

  a .ssh/config specifying the "Port 20022" for 127.0.0.1 on the client
  proper known_hosts files
  ssh pubkeys exchanged between client and server (via 127.0.0.1)

Erfolgreich getestet mit

RESTIC_PASSWORD=geheim restic -v -v --exclude-file=/root/restic-exclude.txt \
  -o sftp.command='ssh hermes.markus-spring.info -p 22022 -s sftp' \
  -r sftp:hermes.markus-spring.info:/repo/restic backup /home/springm

Aber:

  • braucht passwortlosen root-Zugang auf hermes
  • Eventuell geht ein nur-sftp-account
  • Und eine Nur-Hinzufügen-Konfiguration des Repos
version: '2'

services:
  restserver:
    image: restic/rest-server:latest
    container_name: restic-rest
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Berlin
      - "OPTIONS=--append-only"
    volumes:
      - /repo/restic:/data
    ports:
      - "127.0.0.1:8010:8000"
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.restic-rest.rule=Host(`restic.hermes.markus-spring.info`)" 
      - "traefik.http.routers.restic-rest.entrypoints=https"
      - "traefik.http.routers.restic-rest.tls=true"
      - "traefik.http.routers.restic-rest.tls.certresolver=cloudflare"
      - "traefik.http.services.restic-rest.loadBalancer.server.port=8000"

networks:
  proxy:
    external: true

Benutzer anlegen

docker exec -it restic-rest create_user user passwort

Test auf dem netcup-Server

export RESTIC_REPOSITORY="rest:https://user:passwort@restic.hermes.markus-spring.info"
restic -v snapshots
restic -v --exclude-file=/root/restic-exclude.txt backup /home/springm

Backup löschen schlägt fehl, weil das Restic-Repo mit –append-only eingebunden ist.

restic forget d50ada2f
enter password for repository: 
repository 01a62565 opened successfully, password is correct
Remove(<snapshot/d50ada2fcd>) returned error, retrying after 552.330144ms: blob not removed, server response: 403 Forbidden (403)
...
unable to remove <snapshot/d50ada2fcd> from the repository
[0:45] 0.00%  0 / 1 files deleted
blob not removed, server response: 403 Forbidden (403)

Restic arbeitet wesentlich schneller, wenn die Pfade zwischen den Backups gleich sind.Dann werden nur Datei-Metadaten verglichen, anstatt die Datei komplett zu lesen, zu chunken und zu prüfen ob die Chunks schon existieren. Beim Importieren historischer Backups aus rsnapshots ist das aber nicht der Fall. In einer chroot-Umgebung können die historischen Backups mit bind-mounts so zur Verfügung gestellt werden, dass die Wurzelpfade immer gleich sind, d.h., daily.0-n, weekly.0-n etc werden für restic unsichtbar

Chroot-Umgebung erstellen nach Anweisung in https://www.maketecheasier.com/use-chroot-linux/

mkdir /restic-chroot
cd /restic-chroot
mkdir bin
cp /usr/bin/restic bin/
cp /bin/mount bin/
cp /bin/ls bin/
cp /bin/rm bin/
cp /bin/bash bin/

Skript /restic-chroot/copydependancy.sh anlegen und ausführbar machen:

#!/bin/bash
#Setting the chroot directory
mte="/restic-chroot"
# enter your binary name
echo -e "Please enter your binary name \n"
#Reading from terminal input
read binaryname
# Listing all the dependencies
list="$(ldd /bin/$binaryname | egrep -o '/lib.*\.[0-9]')"
# Looping through the dependency list
for i in $list; do cp -v --parents "$i" "${mte}"; done

Dynamic Libraries in die chroot-Umgebung kopieren

# für jedes Binary ausser restic
./copydependancy.sh

Repo mit Bind-Mount zur Verfügung stellen

mount -o bind /restic /restic-chroot/restic

Testen

RESTIC_PASSWORD=keineAhnung RESTIC_REPOSITORY=/restic/mein_repo chroot /restic-chroot /bin/restic --host kudell snapshots
  • computer/ubuntu/restic-backup-docker-hermes.1709056761.txt.gz
  • Last modified: 2024/02/27 17:59
  • by spring