Hard-Link-Backups

Du hast eine Idee für ein Projekt?
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Es gibt ja so einige Backup Programme, wie z.B. die Seite https://wiki.archlinux.org/index.php/Backup_programs beweist...

Ich suche eine Plattform unabhängige Lösung, welche auf Hard-Links Basis, platzsparende Snapshots erstellen kann... So richtig Platzsparend, wäre es zwar erst, mit einem Dateisystem wie SDFS (Deduplication Based Filesystem: https://github.com/opendedup/sdfs )
Aber ich möchte was haben, was die Dateien direkt benutztbar im Dateisystem ablegt. d.h. ist die Backupsoftware mist, hat man keine nicht-zu-öffnenbare Daten...

Es gab mal in der c't ein einfaches vbs Skript, welches rsync nutzt:
Hard-Link-Backups für Windows
Artikel aus c't 09/06, S. 126 (kav)

Keine Zeit fürs Backup? Das hier vorgestellte Windows- Skript hält mit dem Werkzeug rsync vollautomatisch Schnappschüsse Ihrer Dateien fest, ohne dass Sie die Arbeit unterbrechen müssen. rsync kopiert bei jedem Lauf inkrementell nur die geänderten Dateien – dank Hard-Links erscheint auf dem Ziellaufwerk aber jede Sicherung als Vollbackup.
-> http://www.heise.de/ct/ftp/06/09/126/

Mit Python kann man 3.2 auch unter Windows os.link() nutzten... Somit sollte es einfach möglich sein, ein kleines Backup Tool aufzubauen...

Bevor ich was anfange, wollte ich mal Fragen, was ihr so nutzt?!?

EDIT: So nebenbei: Hab gesehen, das https://github.com/opendedup/sdfs in Java geschrieben ist... Dann mal nach was in Python gesucht und das "proof of concept" gefunden: https://peterodding.com/code/python/dedupfs bzw. https://github.com/xolox/dedupfs :P

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@jens: Ich benutze `rdiff-backup` und `rsnapshot`. Letzteres dürfte Deiner Beschreibung entsprechen. Ist die die Frage ob man etwas in Python neu schreiben muss nur weil es in Perl ist. Gerade bei Sicherungskopien würde ich da lieber etwas Erprobtes verwenden statt selber etwas zu schreiben.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

In diesem Falle brauche ich in ersterlinie eine Lösung für Windows.

Es soll eine lokale Spiegelung von Netzwerklaufwerken gemacht werden...

Ich mag kein Perl unter Windows installieren oder über Cygwin gehen... Python ist eh schon drauf...

Ich denke ich werde mal mit einer KISS Lösung anfangen und mal schauen, wie brauchbar das wird.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ich merke gerade, das es doch so einige Fragen, bei einer konkreten Implementierung gibt.
Zumindest dann, wenn der Backup Vorgang ein wenig effizient sein soll ;)

Ich hab folgendes vor:

Das Backup soll so ungefähr organisiert sein:

/BASIS_BACKUP_PATH/2015-12-28-211020/SRC/PATH/Dateien...
/BASIS_BACKUP_PATH/2015-12-28-221401/SRC/PATH/Dateien...
  • Also hier wurde /SRC/PATH/ zweimal gesichert. Heute um 21:10:20 und um 22:14:01
  • Mit "/Dateien..." am Ende ist natürlich ein normaler Dateibaum gemeint. Halt, das selbe, wenn man per Hand Dateien/Verzeichnisse kopiert...
  • Zu jeder Datei im Backup, wollte ich eine Text Datei "foobar.sha256" legen, mit dem SHA Wert des Inhalts.
Mal angenommen, es wird noch ein Backup vom gleichen Inhalt angelegt, dann: Nachsehen ist eine Datei mit selben Namen/Pfad in alten Backups vorhanden... Wenn selbe st_size und st_mtime muß ja der Inhalt auch verglichen werden... Doch wie das effizient machen?

Erste Idee:
Annehmen der Dateiinhalt ist unterschiedlich, also: Kopie Anlegen. Dabei den SHA Wert bestimmen. Wenn der wirklich unterschiedlich ist, ist alles gut. Wenn der gleich ist, kann man die neu erstellte Datei wieder löschen und os.link() nutzten...

Bei Größen Dateien wäre das ja ok... Bei vielen kleinen aber wohl Mist... Also, wenn Datei kleiner als X Bytes ist: In den Speicher laden und SHA Wert bestimmen...

Andere Ideen?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So... der erste gehackte proof-of-concept läuft ...

Ganz nach "Release early, release often", hab ich es schonmal auf github gepackt: https://github.com/jedie/PyHardlinkBackup/

Ich mache es genauso wie angedacht. Wobei ich direkt nach einer alten .sha512 Datei suche und darin den Hash vergleiche.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Noch eine Idee, die ich umsetzten will:

Eine SQLite DB pflegen, die SHA Wert zu Backup-Pfad festhält.

So kann eine Deduplizierung stattfinden kann. Also das man die zu dichernden Dateien wild verschieben/umbenennen kann und dennoch gleiche Dateiinhalte über Links platzsparend gespeichert werden können.


Aber pures SQL wollte ich nicht machen ... Also ein ORM nehmen. Doch welches?
Oder gleich Django: Da kenne ich mich mit aus und man könnte somit schnell eine Web-Oberfläche bauen... Sicherung auf entfernten Rechnern durchführen und kontrollieren...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@jens:
Das Django-ORM liese sich ja auch ohne HTTP-Stack drumrum nutzen. Und wenn Du eh in Richtung Verwaltungsoberfläche schielst, ist es selbstredend.

Allerdings frage ich mich, ob es nötig ist, rsync und Konsorten auf low level nachzubauen oder ob Du nicht mit einem Frontend zu etablierten Tools schneller zum Ziel kommst und auch noch eine performantere Lösung bekämest. Z.B. finde ich BackInTime auf dem Linuxdesktop ganz brauchbar, kA ob das unter Windows läuft.
Für Server setze ich auf good ol' rsync, welches per Cronjob root-Snapshots inkrementell im WAN spiegelt. Bei normalen Änderungen am Dateisystem innerhalb eines Tages läuft das in weniger als 3 Minuten durch (etwa 25min für 10 Server sequentiell). Wenn man eine dünne Leitung dazwischen hat, kann man rsync noch per z-Flag den Abgleich komprimiert vornehmen lassen - sehr praktisch in meinen Augen.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

In ersterlinie brauche ich eine Lösung für Windows. Es gibt rsync für Windows, aber das mehr aus "Dubiosen Quellen"... Klar kann man selbst kompilieren... Aber keine Lust...

Wenn man rsync hat, reicht eine kleine Batch, siehe: http://blog.oliver-arp.de/?p=49
Darauf beruht ja auch ursprünglich die Idee von der c't. Gibt es auch als Kommerzielles Projekt hier: http://www.lupinho.net/hardlinkbackup/

Außerdem legt meine Lösung sha512 Werte bei jedem Backup von jeder Datei an. So kann man das später vergleichen. Kann man natürlich auch im Nachgang an einen rsync lauf machen...


Egal... Ich hab mal angefangen Django-Models zu erstellen:

https://github.com/jedie/PyHardlinkBack ... cbad1e76f5

ich weiß allerdings nicht, ob das so einigermaßen Sinnvoll ist. Hab versucht möglichst keine Redundanzen zu speichern. Wobei ich jetzt auch nicht für den Pfad eine Baumstruktur gemacht hab.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ach, der wichtigste Vorteil gegenüber einer rsync Lösung:

Verschieben/Umbenennen von zu sichernden Dateien führt nicht zu Duplikaten. Denn ich verwende eine Datenbank der Hash-Werte. Somit werden gleiche Dateiinhalte auch gefunden, wenn Name/Pfad unterschiedlich sind.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So... So langsam wird es brauchbar:

https://github.com/jedie/PyHardLinkBackup#readme
https://pypi.python.org/pypi/PyHardlinkBackup/

Kommentare, Vorschläge und Kritik erwünscht...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Ich verstehe den Sinn immer noch nicht ganz. Was ist wenn ich auf eine 2. Festplatte (remote server) backups machen will? Werden dann die Files kopiert und in Zukunft nur Hardlinks erstellt?

Warum nicht sowas wie bup, weil du eine Lösung brauchst die auf Windows läuft (bup soll mit cygwin funktionieren)?
the more they change the more they stay the same
BlackJack

@Dav1d: Also mir fallen auf der verlinkten Seite mehrere Sachen auf weswegen ich ``bup`` nicht verwenden wollen würde. Siehe die Punkte unter „Reasons you might want to avoid bup“ und „Things that are stupid for now but which we'll fix later“. Humor haben sie ja, die Entwickler. ;-)
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

"bup" kommt für mich nicht direkt infrage, weil die erzeugten Backups nicht direkt nutztbar sind.

Ich möchte das im Dateisystem die gesicherten Dateien normal rumliegen. Sodas man direkt zugriff hat, ohne eine Software bemühen zu müßen.

Mein Szenario:

Daten, wie DSLR-Bilder, Videos und Dokumente liegen auf dem Server im Netz. Werden immer über das Netzlaufwerk genutzt.
Backups sollen vom Server auf dem lokalen Rechner (mherere Clients) gemacht werden. Also quasi eine dezentrale Spiegelung.

Der Server läuft mit Linux, die Clients mit Windows oder Linux.

EDIT: Hab die README noch ein wenig erweitert: https://github.com/jedie/PyHardLinkBack ... linkbackup
u.a. noch ein Beispiel eingefügt:

Code: Alles auswählen

$ phlb backup ~/my/important/documents
...start backup, some time later...
$ phlb backup ~/my/important/documents
...
So ähnlich sehen dann die erstellen Backups aus:

Code: Alles auswählen

~/PyHardLinkBackups
  └── documents
      ├── 2016-01-07-085247
      │   ├── spreadsheet.ods
      │   ├── brief.odt
      │   └── important_files.ext
      └── 2016-01-07-102310
          ├── spreadsheet.ods
          ├── brief.odt
          └── important_files.ext

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So... v0.1.8 ist raus... Nun läuft das ganze auch unter Windows... Sogar ziemlich einfach zu installieren:

Einfach https://raw.githubusercontent.com/jedie ... backup.cmd ziehen/speichern/starten und man erhält ein venv unter %APPDATA%\PyHardLinkBackup mit ein paar Hilfs-Batch Dateien zum benutzen...

Details: https://github.com/jedie/PyHardLinkBackup#on-windows

Ein Performance-Loch sind bei vielen kleinen Dateien, die SQLite Datenbank. Bei größeren Dateien fluppt es ganz gut, würde ich sagen...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Komisch... Ich dachte ich hätte gestern noch auf die neue v0.2.0 Aufmerktsam gemacht... Irgendwie ist der Post aber nicht mehr da... Komisch...

Na, jedenfalls decken nun die Unittests den Backup Prozess auch mit ab:
Bild -> https://coveralls.io/r/jedie/PyHardLinkBackup

Unter Linux laufen die Tests durch: Bild -> https://travis-ci.org/jedie/PyHardLinkBackup/
Unter Windows allerdings noch nicht: Bild -> https://ci.appveyor.com/project/jedie/p ... up/history

Ach, nebenbei: Eine reine Windows Lösung hat man mit der sog. "DeLorean Copy" in "Link Shell Extension" -> http://schinagl.priv.at/nt/hardlinkshel ... loreancopy


Aber das nächste Problem: Nach einem Lauf sehe ich unter Windows allerdings das:

Code: Alles auswählen

    os.link(abs_old_backup_path, self.path.abs_dst_filepath)
OSError: [WinError 1142] Es wurde versucht, mehr Verknüpfungen mit einer Datei zu erstellen, als das Dateisystem unterstützt: 'd:\\PyHardLinkBackups\\PyHardLinkBackup\\2016-01-14-081652\\Lib\\site-packages\\dev\\__init__.py' -> 'd:\\PyHardLinkBackups\\PyHardLinkBackup\\2016-01-14-081652\\Lib\\site-packages\\django\\middleware\\__init__.py'
:shock:

In der Tat:

Code: Alles auswählen

import os

stat=os.stat("d:\\PyHardLinkBackups\\PyHardLinkBackup\\2016-01-14-081652\\Lib\\site-packages\\dev\\__init__.py")
print(stat.st_nlink)
Zeigt 1024 an... Das ist eine ganze Menge... Wobei ext3/4 wesentlich mehr Links können:
https://en.wikipedia.org/wiki/Hard_link ... hard_links

Aber hier ist das Problem, der kompletten deduplizierung: Ich hab natürlich nicht de Backup Vorgang schon 1024 mal gestartet: Die __init__.py ist leer und kommt innerhalb von einem Backup schon oft vor. Die alle werden dedupliziert und so kommt die Zahl zustande.

Was tun?

Ich könnte nun in der Datenbank die Link Anzahl mit führen und dann auf einer weiteren Kopie der Datei zurück greifen...
Dann muß ich allerdings die Limits der Dateisysteme kennen. Oder ich sagt generell max. 1024 sind erlaubt. Dürfte ja nicht all zu oft vorkommen.

Bessere Ideen?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

jens hat geschrieben:Ich könnte nun in der Datenbank die Link Anzahl mit führen und dann auf einer weiteren Kopie der Datei zurück greifen...
Dann muß ich allerdings die Limits der Dateisysteme kennen. Oder ich sagt generell max. 1024 sind erlaubt. Dürfte ja nicht all zu oft vorkommen.

Bessere Ideen?
Fehler fangen und anstatt einen Hardlink anzulegen die Datei kopieren, dann reichts wieder für neue 1204 Backups.
the more they change the more they stay the same
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Gute Idee... Hab mal versucht das umzusetzten, mit: https://github.com/jedie/PyHardLinkBack ... 50dea1a6a5

Aber muß ich noch genauer Testen. Außerdem ist damit das hin und her umbenennen doof. Aber das braucht mehr Zeit für eine bessere Lösung...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, hab einen unittest gebaut, der das NTFS 1024 hardlink Problem testet: https://github.com/jedie/PyHardLinkBack ... 615de7b356

Ist natürlich ein wenig Resourcen verschwendend :P

Ich habe es in zwei Phasen unterteilt:

1. 1022 Dateien werden mit gleichen Inhalt erzeugt und ein Backup davon gemacht
2. Es werden 4 weitere Dateien "A", "B", "C" und "D" erzeugt, mit ebenfalls gleichem Inhalt und davon dann ein Backup gemacht...

Anhand der Error Log kann man sehen was passiert:
Can't link 'BAK2/foo A.ext' to 'BAK2/foo C.ext'
Can't link 'BAK2/foo B.ext' to 'BAK2/foo C.ext'
Can't link 'BAK1/file 0001.txt' to 'BAK2/foo C.ext'
...
Can't link 'BAK1/file 1020.txt' to 'BAK2/foo C.ext'
Can't link 'BAK1/file 1021.txt' to 'BAK2/foo C.ext'
Can't link 'BAK1/file 1022.txt' to 'BAK2/foo C.ext'
Dabei steht "BAK1/" die das Verzeichniss des ersten Backup laufs und BAK2/" für den zweiten...

Man kann sehen, das für "A" und "B" noch hardlinks erzeugt werden können. Dann sind es halt die 1024...
Für "C" wird dann ein links "gesucht"... Natürlich ein wenig doof: Ich probiere einfach solange, bis ich einen Link machen kann. Aber nur einmal! In der Datenbank werden nicht nutzbare Dateien markiert... Somit dauert das ganze nur einmalig 1024 Versuche :P

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So: Neues Release v0.6.0

Nun sind folge Backups um einiges schneller, gerade wenn es viele Daten sind und sich wenig geändert hat:

Jetzt wird das letzte komplette Backup herrangezogen: Alle alten Dateien werden nur noch per Änderungsdatum und Dateigröße vergleichen. Sind diese gleich, wird direkt ein Hardlink erzeugt. Wenn Unterschiedlich dann der Inhalt verglichen...

Generell wird das Backup sortiert nach Dateidatum gemacht: Von der neusten Datei zur ältesten.

Also: Bei einem zweiten Backup lauf passiert folgendes:

* Dateidatum der neusten Datei aus dem letzten Backup raussuchen. Nennen wird es mal Dateidatum-X.
* Alle Dateien die neuer als Dateidatum-X sind, werden generell über den Inhalt unterschieden.
* Ältere Dateien als Dateidatum-X, werden im alten Backup mit selben Pfad + selbe Dateigröße + selbe Änderungsdatum gesucht und direkt als Hardlink übernommen.


Damit wird es nun auch Praktisch nutzbar :D


Aber es gibt noch einiges zu tun, siehe: https://github.com/jedie/PyHardLinkBackup/issues

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ich frage mich gerade, ab welcher Dateigröße es wohl überhaupt Sinn macht einen Hardlink zu nutzen...

Zumindest bei 0-Bytes Dateien ist es vermutlich kontraproduktiv.

Und ich meine, das mache Dateisysteme sehr kleine Dateien auch direkt im 'MFT' speichern.
Bei NTFS z.B. werden alle Dateien die kleiner als 1KB direkt im MFT gespeichert, siehe: http://blogs.technet.com/b/askcore/arch ... rowth.aspx

ext4 speichert wohl kleinere Dateien direkt im inode, siehe: https://ext4.wiki.kernel.org/index.php/ ... nline_Data

Keine Ahnung wie viel ein Hardlink demgegenüber verbrauchen würde...

Hinzu kommt, das jeder Datei Eintrag in der SQLite Datenbank von PyHardLinkBackup ja Speicher verbraucht. Nicht viel, aber ein paar Bytes ja schon.
Mal so grob Kalkuliert: Hab 41.032 Dateien im Backup, die SQLite Datei ist 12.064.768 Bytes groß, also: ~300Bytes pro Eintrag.

Noch ein Punkt: Viele kleine Dateien verlangsamen auch den Backup Prozess, auf verschiedene Weise...

Wo also die Grenze ziehen?!? Bei 1024 Bytes, wegen NTFS?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten