My Universe Logo

Backups von ZFS Datasets

Posted by Jesco Freund at March 27, 2010 11:46 a.m.

Alle meine Jails und ihre Nutzdaten residieren auf ZFS Datasets. Dank ZFS Snapshots kann man sie so relativ leicht sichern, ohne den Betrieb des Servers dabei zu stören. Allerdings braucht man noch ein Werkzeug, das die Snapshots erzeugt, Backups erstellt, diese dann auf den Backupserver hochlädt und die Snapshots auch wieder entsorgt. Bisher habe ich das mit einem frickeligen Shell-Skript erledigt; allerdings war ich damit in letzter Zeit nicht mehr besonders zufrieden. Was ich eigentlich möchte, ist ein Tool, das mir folgende Möglichkeiten bietet:

  • aufgeräumte Crontab mit möglichst wenig Einträgen für Backups 8-)
  • verschlüsselte Backups (die UMASK auf den Backup-Servern meines Providers gefällt mir nicht)
  • möglichst keine lokalen temporären Dateien
  • Zusammenfassen von ZFS Datasets zu logischen Gruppen mit Pre- und Post-Snapshot Actions
  • Automatische wöchentliche Rotation der Backups auf dem Backup-Server

Also habe ich mich daran gesetzt, ein ordentliches Shellskript zu schreiben, das die o. g. Bedingungen erfüllt. Herausgekommen dabei ist zbackup. Die verlinkte Beta1 funktioniert immerhin schon so gut, dass ich sie produktiv nutze – ein Release folgt aber erst nach weiteren Tests, möglichst auch durch andere Tester.

Um zu verdeutlichen, was zbackup alles leisten kann, zeige ich hier mal an einem etwas komplizierteren Beispiel, wie man zbackup richtig konfiguriert. Die härteste Nuss für Backup-Tools ist mithin, konsistente Backups von Datenbank-Servern zu erzeugen. Anhand eines MySQL-Servers wird im folgenden beschrieben, wie man zu konsistenten Backups gelangt:

Zunächst das Szenario: Nehmen wir an, es gebe ein Jail, in dem ein MySQL-Server läuft. Das Jail sei auf vier ZFS Datasets verteilt: zpool/mroot beherbergt das von allen Jails genutze Basis-System und zpool/mysql das Jail-Skelett, wie im FreeBSD-Handbuch beschrieben. zpool/mysql/data beinhaltet die eigentlichen Nutzdaten, nämlich die MySQL-Datenbanken, und zpool/mysql/log die Logfiles wie z. B. die InnoDB-Logfiles.

Im ersten Schritt muss zbackup installiert werden. Als Abhängigkeit benötigt zbackup ncftp3, was sich glücklicherweise leicht aus den Ports installieren lässt:

cd /usr/ports/ftp/ncftp3
make install clean

zbackup selbst ist leicht zu installieren; man benötigt nur das oben verlinkte .tar.bz2 Archiv:

tar -xvf zbackup-0.1.0-beta1.tar.bz2
cd zbackup-0.1.0-beta1
make install

Nun geht es an die Konfiguration von zbackup. Um den Überblick nicht zu verlieren, bietet es sich an, alle Konfigurationsdateien in einem Verzeichnis aufzubewahren. Im Beispiel nutze ich dafür /usr/local/etc/zbackup, wie es sich auf einem FreeBSD-Rechner gehört. An Konfigurationsdateien wird folgendes benötigt:

  • eine Profildatei mit den Vorgaben für das Backup
  • eine Datei mit den FTP Zugangsdaten für den Backup-Server
  • eine Schlüsseldatei (schließlich wollen wir Postfach-Inhalte doch nicht unverschlüsselt auf einem fremden Backup-Server ablegen!)

Die Schlüsseldatei ist am einfachsten zu erzeugen, deshalb fange ich damit an. Ich verwende gerne pwgen2, um Schlüssel oder Passwörter zu erzeugen.

pwgen -s 32 1 > /usr/local/etc/zbackup/zback.key
chmod 0400 /usr/local/etc/zbackup/zback.key

Wichtig: von dieser Datei bitte unbedingt ein Backup auf einem USB-Stick oder einem sonstigen, möglichst lösch- und diebstahlsicheren Speichermedium anlegen!

Wie die Datei mit den FTP-Zugangsdaten auszusehen hat, beschreibt die Manpage zu ncftpput:

host sphygmomanometer.ncftp.com
user gleason
pass mypassword

Natürlich müssen hier die „echten“ Zugangsdaten rein, also FQDN oder IP des Backup-Servers sowie User und Passwort des Backup-Accounts. Daher sollte auch diese Datei aus Sicherheitsgründen die Berechtigung 0400 erhalten. Für das Beispiel nehme ich an, die Datei liege unter /usr/local/etc/zbackup/ftp.

Zu guter letzt wird noch die Profildatei benötigt; im Beispiel sei das /usr/local/etc/zbackup/jail.profile. Diese sieht wie folgt aus:

# Eintrag fuer Basis-System (gemeinsame Nutzung durch alle Jails)
zpool/mroot|||/usr/local/etc/zbackup/ftp|/backup
#
# Eintrag fuer MySQL-Jail Skeleton
zpool/mysql|||/usr/local/etc/zbackup/ftp|/backup
#
# Eintrag fuer MySQL Datenbanken
zpool/mysql/data|||/usr/local/etc/zbackup/ftp|/backup
#
# Eintrag fuer MySQL Logs
zpool/mysql/log|||/usr/local/etc/zbackup/ftp|/backup

Mit dieser Konfiguration lassen sich bereits verschlüsselte Backups anlegen:

zbackup -e -k /usr/local/etc/zbackup/zback.key -p /usr/local/etc/zbackup/jail.profile

Problem: So sind die Backups nicht konsistent. Die InnoDB Logfiles etwa werden zu einem späteren Zeitpunkt als die zugehörigen Datenbank-Dateien gesichert. Wenn der MySQL-Server die ganze Zeit über nichts tut, ist das kein Problem. Ein Backup von mehreren Gigabyte Datenbankdateien kann aber eine Weile dauern, so dass hier der Problemfall quasi vorprogrammiert ist. Als Lösung des Dilemmas könnte man entweder die Logs mit den Datenbank-Dateien auf ein gemeinsames ZFS Dataset packen - oder man fasst die verschiedenen Datasets zu einer logischen Gruppe zusammen.

Im Fall eins (gemeinsames Dataset) könnte man darauf vertrauen, dass der Snapshot schneller erstellt wird, als MySQL Inkonsistenzen erzeugen könnte. Arbeitet man hingegen mit einer logischen Gruppe, werden die Snapshots zwar unmittelbar nacheinander erstellt, aber eben nacheinander und nicht parallel. Ergo ist es zwingend erforderlich, in einem solchen Fall den MySQL-Server vor dem Erstellen der Snapshots zu stoppen und danach wieder anzuwerfen. Die Erstellung der Snapshots dauert nur wenige Sekunden, so dass dadurch so gut wie keine Ausfallzeit des Dienstes entstehen sollte. Um dies zu bewerkstelligen, kommen die Pre- und Post-Actions von zbackup ins Spiel. Das folgende Shell-Skript ist ein Beispiel dafür, wie man einen MySQL-Server innerhalb eines Jails stoppen kann, ohne dafür das ganze Jail anzuhalten:

#!/bin/sh

# Benennung des Jails in der rc.conf
jail_name="mysql"

ip=$(/usr/bin/grep ${jail_name}_ip /etc/rc.conf | /usr/bin/awk 'BEGIN {FS="\"";}{print $2}')
jid=$(/usr/sbin/jls | /usr/bin/grep $ip | /usr/bin/awk '{print $1}')

/usr/sbin/jexec $jid /usr/local/etc/rc.d/mysql-server stop

Analog dazu müsste für ein Start-Skript das stop durch start ausgetauscht werden. Beide Skripte (für Start und Stop) sollen im Beispiel unter /usr/local/etc/zbackup/actions/ abgelegt sein und mysql-start.sh bzw mysql-stop.sh heißen. Beide Skripte müssen ausführbar sein; d. h. die Mindestberechtigung wäre 0500.

Um zbackup dazu zu bekommen, die MySQL-Datasets als logische Gruppe zu betrachten und die Pre- und Post-Actions auszuführen, muss die Profildatei entsprechend angepasst werden:

# Eintrag fuer Basis-System (gemeinsame Nutzung durch alle Jails)
zpool/mroot|||/usr/local/etc/zbackup/ftp|/backup
#
# Eintrag fuer MySQL-Jail Skeleton
zpool/mysql|||/usr/local/etc/zbackup/ftp|/backup
#
# Eintrag fuer logische Gruppe MySQL Daten + Logs
zpool/mysql/data;zpool/mysql/log|/usr/local/etc/zbackup/actions/mysql-stop.sh|/usr/local/etc/zbackup/actions/mysql-start.sh|/usr/local/etc/zbackup/ftp|/backup

Nun wird der MySQL-Server gestoppt, bevor die Snapshots für Daten und Logs erstellt werden. Unmittelbar nach Erstellung der Snapshots wird der Server wieder gestartet, so dass der Dienst wieder erreichbar ist, während zbackup in aller Ruhe ein Sicherungsarchiv von den Snapshots erstellt, dieses ggf. verschlüsselt und auf den Backup-Space hochlädt.

Ach ja, ein paar Anmerkungen wären da noch zu machen:

  • Ein Profil kann beliebig groß sein. Es ist also problemlos möglich, alle ZFS Datasets auf einem Server in einem Profil abzufrühstücken.
  • Eine logische Gruppe kann beliebig viele ZFS Datasets umfassen, nicht nur zwei (wie im Beispiel gezeigt).
  • zbackup hat Probleme, wenn in einer Definitionszeile der Profildatei Leerzeichen auftauchen – darauf sollte man also besser verzichten. Das ist auch der Grund, warum ich im Beispiel separate Start- und Stop-Skripte verwendet habe anstelle eines parametrisierten Skripts.
  • Entschlüsseln kann man verschlüsselte Backups ganz leicht mit OpenSSL – siehe dazu die Manpage von openssl enc
  • zbackup arbeitet derzeit mit einer hart codierten Rotation: Backups bekommen einfach den Wochentag im Archivnamen eingebaut. So überschreibt z. B. das Backup von Montag das Backup des vorhergehenden Montags. Der Rotationsautomatismus funktioniert daher nur mit täglichen Backups einigermaßen. Bei wöchentlichen Backups etwa wäre immer nur eine Generation vorhanden; bei einem Abbruch des Backups wäre dann gar keine intakte Kopie erhalten.

Ein bisschen Potenzial nach oben ist also noch da - für meine Zwecke reicht zbackup jedoch (vorerst) aus und leistet gute Dienste.

No comments | Defined tags for this entry: backup, FreeBSD, root-tools, ZFS

Comments

No comments