MySQL-Backups mit AutoMySQLBackup erstellen

Ich möchte gerne eine produktive MySQL-Datenbank regelmäßig, mindestens täglich, sichern. Mein Hoster (RailHoster) würde das auch übernehmen für schlappe 5 Euro im Monat. Der Preis ist eigentlich ok, aber ich will erstmal schauen, ob ich das nicht selbst hinbekomme.

Bei einer kurzen Recherche bin ich über den Artikel 10 Ways to Automatically & Manually Backup MySQL Database gestolpert. Way Nummer 3 und das Tool AutoMySQLBackup finde ich interessant. Es soll die Backups bzw. Datenbankdumps zippen können und ältere Backups automatisch löschen können.

AutoMySQLBackup besteht aus Shell-Skripten für UNIX-Systeme sowie einer Konfigurationsdatei. Voraussetzung für den Einsatz ist als ein UNIX-System. Da ich zu meinem Webhosting-Server einen SSH-Zugang habe und dort Ubuntu läuft, ist das kein Problem.

Das Tool wird auf sourceforge.net entwickelt. Die Projektseite bietet leider nicht viele Infos, es werden nur die Features aufgelistet. Das Wiki enthält auch nicht mehr, im Issue Tracker haben alle Tickets den Status open und nur alle paar Wochen wird im Forum diskutiert. Das letzte Release ist schon über zwei Jahre alt. Das klingt alles nicht so gut, aber die Bewertung ist super. Die Nutzer scheinen zufrieden zu sein und deshalb will ich mir das Tool genauer ansehen.

Ich lade Version 3.0 RC6 herunter. Ist zwar mit RC benannt aber wie gesagt schon über zwei Jahre alt. Ich gehe mal davon aus, dass die Version stabil ist. Im heruntergeladenen ZIP befinden sich 6 Dateien: 3 die das Tools ausmachen (automysqlbackup, automysqlbackup.conf und install.sh) und 3 Textdateien (README, CHANGELOG, LICENSE). Es ist also überschaubar. Die Lizenz ist GNU General Public License (GPL), Version 2.0.

Die Datei README liefert das, was die Projektseite nicht hergibt: Installationshinweise, Verwendung, Konfiguration.

Installation und Konfiguration

Um das Tool kennenzulernen, will ich es zunächst lokal auf meinem OSX-Betriebssystem installieren. Um das zu tun, soll man zunächst die Datei install.sh ausführen. Die Ausführung ist erstmal ernüchternd:

### Checking archive files for existence, readability and integrity.

automysqlbackup ... exists and is readable ... md5sum failed :(

Das gleiche wiederholt sich auf meinem Webhosting-Server, auf den ich die Dateien kopiere. In diesem Ticket finde ich die Lösung. Der Grund ist, dass das Programm md5sum installiert sein muss. Das ist weder auf meinem lokalen OSX noch auf dem Webhosting-Server der Fall. Bevor ich nun anfange die Datei install.sh anzupassen, gehe ich the hard way, wie es in der README so schön heißt. Ich installiere AutoMySQLBackup also manuell und ohne das Skript.

Die Installationsschritte sehen vor, dass in Verzeichnisse wie /etc und /var geschrieben werden. Das will ich nicht, denn dann muss AutoMySQLBackup als root ausgeführt werden, was meiner Meinung nach nicht notwendig ist. Hinzukommt, dass ich auf dem Webhosting-System keinen root-Zugang habe und in diese Verzeichnisse nicht schreiben kann.

Ich wähle als abweichend von der README andere Verzeichnisse. Um es einfach zu machen, lege ich alles in einem Verzeichnis ab:

automysqlbackup
automysqlbackup.conf
CHANGELOG
database_backups
install.sh
LICENSE
myserver.conf
README
runmysqlbackup

myserver.conf ist die angepasste Konfigurationsdatei. Ich komme mit wenigen Konfigurationseinstellungen zurecht:

  • CONFIG_mysql_dump_username
  • CONFIG_mysql_dump_password
  • CONFIG_mysql_dump_host
  • CONFIG_backup_dir
  • CONFIG_db_names
  • CONFIG_db_month_names

In database_backups sollen die Backups erstellt werden. Die Datei runmysqlbackup, wie sie in der README unter Usage beschrieben wird, lege ich an, aber ohne die chown- und chmod- Befehle:

#!/bin/sh

/Users/bjoerne/Development/Tools/automysqlbackup-v3.0_rc6/automysqlbackup /Users/bjoerne/Development/Tools/automysqlbackup-v3.0_rc6/myserver.conf

Erstellen eines launchd-Jobs unter OSX

Auf OSX gibt es den Pfad /etc/cron.daily nicht, der in der README erwähnt wird. Ich muss sogar feststellen, dass cron nicht mehr das Programm der Wahl ist sondern launchd.

Die Seite http://nathangrigg.net/2012/07/schedule-jobs-using-launchd/ erklärt, wie die Einrichtung eines launchd-Jobs funktioniert. Im Verzeichnis ~/Library/LaunchAgents kann mal plist-Dateien anlegen, um periodisch auszuführende Jobs zu konfigurieren.

Ich lege die Datei com.bjoerne.automysqlbackup.plist mit folgendem Inhalt an:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.bjoerne.automysqlbackup</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/bjoerne/Development/Tools/automysqlbackup-v3.0_rc6/runmysqlbackup</string>
    </array>
    <key>StartInterval</key>
    <integer>86400</integer>
    <key>StandardErrorPath</key>
    <string>/Users/bjoerne/Development/Tools/automysqlbackup-v3.0_rc6/automysqlbackup.log</string>
    <key>StandardOutPath</key>
    <string>/Users/bjoerne/Development/Tools/automysqlbackup-v3.0_rc6/automysqlbackup.log</string>
</dict>
</plist>

Die Eigenschaften StandardErrorPath und StandardOutPath legen fest, was mit der Ausgabe gemacht werden soll. Standardmäßig erscheint diese im Programm Konsole im Systemprotokoll. Ich schreibe lieber in eine Log-Datei.

Nun führe ich aus:

launchctl load ~/Library/LaunchAgents/com.bjoerne.automysqlbackup.plist
launchctl start com.bjoerne.automysqlbackup

Ausgabe:

05.09.13 23:06:28,459 com.bjoerne.automysqlbackup: # Checking for permissions to write to folders:
05.09.13 23:06:28,460 com.bjoerne.automysqlbackup: base folder /Users/bjoerne/Anwendungsdaten/AutoMySQLBackup ... 
05.09.13 23:06:28,461 com.bjoerne.automysqlbackup: exists ... ok.
05.09.13 23:06:28,461 com.bjoerne.automysqlbackup: backup folder /Users/bjoerne/Anwendungsdaten/AutoMySQLBackup/DatabaseBackups ... 
05.09.13 23:06:28,462 com.bjoerne.automysqlbackup: exists ... writable? 
05.09.13 23:06:28,464 com.bjoerne.automysqlbackup: yes. Proceeding.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: checking directory "/Users/bjoerne/Anwendungsdaten/AutoMySQLBackup/DatabaseBackups/daily" ... exists.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: checking directory "/Users/bjoerne/Anwendungsdaten/AutoMySQLBackup/DatabaseBackups/weekly" ... exists.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: checking directory "/Users/bjoerne/Anwendungsdaten/AutoMySQLBackup/DatabaseBackups/monthly" ... exists.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: checking directory "/Users/bjoerne/Anwendungsdaten/AutoMySQLBackup/DatabaseBackups/latest" ... exists.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: checking directory "/Users/bjoerne/Anwendungsdaten/AutoMySQLBackup/DatabaseBackups/tmp" ... exists.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: checking directory "/Users/bjoerne/Anwendungsdaten/AutoMySQLBackup/DatabaseBackups/fullschema" ... exists.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: checking directory "/Users/bjoerne/Anwendungsdaten/AutoMySQLBackup/DatabaseBackups/status" ... exists.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: # Testing for installed programs
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: WARNING: Turning off multicore support, since pigz isn't there.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: mysql ... found.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: mysqldump ... found.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: # Parsing databases ... done.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: ======================================================================
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: AutoMySQLBackup version 3.0
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: http://sourceforge.net/projects/automysqlbackup/
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Backup of Database Server - localhost
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Databases - bjoerne
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Databases (monthly) - bjoerne
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: ======================================================================
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: ======================================================================
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Dump full schema.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Rotating 4 month backups for 
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: ======================================================================
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: ======================================================================
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Dump status.
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Rotating 4 month backups for 
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: ======================================================================
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Backup Start Time Thu Sep  5 23:06:29 CEST 2013
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: ======================================================================
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Daily Backup ...
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Daily Backup of Database ( bjoerne )
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Rotating 6 day backups for bjoerne
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: ----------------------------------------------------------------------
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Backup End Time Thu Sep  5 23:06:29 CEST 2013
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: ======================================================================
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Total disk space used for backup storage...
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: Size - Location
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: 84K /Users/bjoerne/Anwendungsdaten/AutoMySQLBackup/DatabaseBackups
05.09.13 23:06:29,072 com.bjoerne.automysqlbackup: ======================================================================

Es scheint zu funktionieren. Ich schaue mir das Backup an. Im Backup-Order sind mehrere Unterordner entstanden:

daily
fullschema
latest
monthly
status
tmp
weekly

In daily, fullschema und status befinden sich nun gz-Dateien. Die Datei in fullschema enthält nur die inserts für die Tabellenstruktur. In daily befinden sich Backups, die auch die Daten enthalten. In status ist eine Textdatei mit allen Datenbanken. Interessant finde ich erstmal nur die Datei in daily.

Installation auf Webhosting-Rechner

Bisher habe ich mich ja nur der lokalen Trockenübung gewidmet, jetzt wird es erst richtig interessant. Ich lege auf dem Server die Dateien analog zur lokalen Installation an und passe die Konfigurationsdatei an.

Bei der Ausführung treten nacheinander drei Fehler auf. Ich kann alle drei jeweils mit einem Workaround beheben. Die Fehler sind:

  • line 208: mktemp: command not found
  • bash: /dev/fd/62: No such file or directory
  • line 551: mysqlshow: command not found

Dass die Befehle mktemp und mysqlshow nicht zur Verfügung stehen, kann ich über den Support von RailsHoster lösen. Der ist wirklich gut und innerhalb von wenigen Stunden ist das Problem gelöst. Der Fehler bash: /dev/fd/62: No such file or directory bleibt noch bestehen. Die Workarounds zeige ich am Ende dieses Artikels.

CRON-Job anlegen

Mithilfe der Seite http://wiki.ubuntuusers.de/Cron lege ich einen CRON-Job an. Ich führe aus:

crontab -e

Es öffnet sich wie versprochen ein vi-Editor, in den ich folgende Zeile einfüge (<Home-Verzeichnis> entsprechend ersetzen!):

0 0 * * *   <Home-Verzeichnis>/automysqlbackup/runmysqlbackup >> <Home-Verzeichnis>/automysqlbackup/automysqlbackup.log

Es wird also jeden Tag um 0 Uhr ein Backup erstellt. Die Ausgabe wird in eine Log-Datei geschrieben. Ich teste mit einem kürzeren Zeitintervall und das ganze funktioniert!

Fazit

Das Tool funktioniert und ich bin froh. Die Darstellung und Dokumentation auf der SourceForge-Seite ist mir allerdings viel zu knapp. Ich hoffe, dass ich mit diesem Artikel einen kleinen Beitrag zum besseren Verständnis leisten kann.

Meine Workarounds bei Fehlern auf Webhosting-System

Wie bereits erwähnt traten bei der Ausführung von AutoMySQLBackup auf meinem Webhosting-Server Fehler auf. Zwei der Fehler konnte ich durch den Support von RailsHoster beheben lassen und die entsprechenden Workarounds sind für mich nicht mehr interessant. Trotzdem liste ich alle drei hier auf.

line 208: mktemp: command not found

Problem: Das Programm mktemp ist nicht installiert. Falls das Tool nicht installiert werden kann, ist folgender Patch vielleicht hilfreich:

@@ -205,7 +205,9 @@
 # @return:    returns false if creation of temporary file failed or it can't be removed afterwards; else true
 # @deps:    (none)
 chk_folder_writable () {
-  local temp; temp="$(mktemp "$1"/tmp.XXXXXX)"
+  local temp
+  temp="$1/tmp.$RANDOM"
+  touch $temp
   if (( $? == 0 )); then
     rm "${temp}" || return 1
     return 0

mktemp wird also durch Verwendung von $RANDOM und touch umgangen.

bash: /dev/fd/62: No such file or directory

Diese Problem konnte vom Support nicht gelöst werden. Mein Workaround ist also noch aktiv. Ausgelöst wird die Fehlermeldung von der Zeile

while read -r; do alldbnames[i++]="$REPLY"; done < <(mysql --user="${CONFIG_mysql_dump_username}" --password="${CONFIG_mysql_dump_password}" --host="${CONFIG_mysql_dump_host}" "${mysql_opt[@]}" --batch --skip-column-names -e "show databases")

Durch diese Zeile werden alle MySQL-Datenbanken in einen Array gelesen Zusammen mit den Konfigurationsdaten wird berechnet, welche Datenbanken bei der Erstellung der Backups berücksichtigt werden sollen.

Meine Lösung ist etwas rabiat. Ich verzichte auf die Berechnung und schreibe den Datenbanknamen hart in das Skript. Die entsprechenden Konfigurationsparameter sind damit nicht mehr relevant und die Funktion parse_databases() schrumpft auf drei Zeilen. Hier der Patch (<Name der Datenbank> ersetzen):

@@ -1056,29 +1056,8 @@
 #    CONFIG_db_month_names empty? -> set to alldbnames
 #
 parse_databases() {
-  # bash 4.x version
-  #mapfile -t alldbnames < <(mysql --user="${CONFIG_mysql_dump_username}" --password="${CONFIG_mysql_dump_password}" --host="${CONFIG_mysql_dump_host}" --batch --skip-column-names -e "show databases")
-  alldbnames=()
-
-  printf "# Parsing databases ... "
-  # bash 3.0
-  local i;i=0;
-  while read -r; do alldbnames[i++]="$REPLY"; done < <(mysql --user="${CONFIG_mysql_dump_username}" --password="${CONFIG_mysql_dump_password}" --host="${CONFIG_mysql_dump_host}" "${mysql_opt[@]}" --batch --skip-column-names -e "show databases")
-  unset i
-
-  # mkfifo foo || exit; trap 'rm -f foo' EXIT
-
-  ((! "${#alldbnames[@]}" )) && { let "E |= $E_db_empty"; error_handler; }
-
-  # -> remove excluded dbs from list
-  for exclude in "${CONFIG_db_exclude[@]}"; do
-    for i in "${!alldbnames[@]}"; do if [[ "x${alldbnames[$i]}" = "x${exclude}" ]]; then unset 'alldbnames[i]'; fi; done
-  done
-  # <- remove excluded dbs from list
-
-  # check for empty array lists and copy all dbs
-  ((! ${#CONFIG_db_names[@]}))    && CONFIG_db_names=( "${alldbnames[@]}" )
-  ((! ${#CONFIG_db_month_names[@]}))    && CONFIG_db_month_names=( "${alldbnames[@]}" )
+  CONFIG_db_names=( "<Name der Datenbank>" )
+  CONFIG_db_month_names=( "<Name der Datenbank>" )
   printf "done.n"
 }

line 551: mysqlshow: command not found

Problem: Das Programm mysqlshow ist nicht installiert. Das Programm wird im Rahmen von AutoMySQLBackup allerdings nur für die Ergebnis-Ausgabe verwendet. Durch das Auskommentieren der Aufrufe der Funktion dbstatus, in der mysqlshow verwendet wird, kann man das Problem umgehen. Patch:

@@ -1811,7 +1811,7 @@

         # monthly
         if (( ${CONFIG_do_monthly} != 0 && (${date_day_of_month} == ${CONFIG_do_monthly} || $date_day_of_month == $date_lastday_of_this_month && $date_lastday_of_this_month < ${CONFIG_do_monthly}) )) && (shopt -s nullglob dotglob; f=("${CONFIG_backup_dir}/status/status_monthly_${date_stamp}_"[0-9][0-9]"h"[0-9][0-9]"m_${date_month}.txt${suffix}"); ((! ${#f[@]}))); then
-          dbstatus "${CONFIG_backup_dir}/status/status_monthly_${datetimestamp}_${date_month}.txt"
+          # dbstatus "${CONFIG_backup_dir}/status/status_monthly_${datetimestamp}_${date_month}.txt"
           if (( $? == 0 )); then
             echo "Rotating $(( ${CONFIG_rotation_monthly}/31 )) month backups for ${mdb}"
             if (( $CONFIG_dryrun )); then
@@ -1829,7 +1829,7 @@

         # weekly
         if [[ ${CONFIG_do_weekly} != 0 && ${date_dayno_of_week} = ${CONFIG_do_weekly} ]] && (shopt -s nullglob dotglob; f=("${CONFIG_backup_dir}/status/status_weekly_${date_stamp}_"[0-9][0-9]"h"[0-9][0-9]"m_${date_weekno}.txt${suffix}"); ((! ${#f[@]}))); then
-          dbstatus "${CONFIG_backup_dir}/status/status_weekly_${datetimestamp}_${date_weekno}.txt"
+          # dbstatus "${CONFIG_backup_dir}/status/status_weekly_${datetimestamp}_${date_weekno}.txt"
           if (( $? == 0 )); then
             echo "Rotating $(( ${CONFIG_rotation_monthly}/31 )) month backups for ${mdb}"
             if (( $CONFIG_dryrun )); then
@@ -1846,7 +1846,7 @@
         fi

         # daily
-        dbstatus "${CONFIG_backup_dir}/status/status_daily_${datetimestamp}_${date_day_of_week}.txt"
+        # dbstatus "${CONFIG_backup_dir}/status/status_daily_${datetimestamp}_${date_day_of_week}.txt"
         if (( $? == 0 )); then
           echo "Rotating $(( ${CONFIG_rotation_monthly}/31 )) month backups for ${mdb}"
           if (( $CONFIG_dryrun )); then

 

2017-01-28T14:31:35+00:00 04.09.2013|Tags: , , , , , |0 Kommentare

Hinterlassen Sie einen Kommentar