Bevor man eine Webseite online stellt, ist es natürlich wichtig, sich Gedanken über die grundlegende Verzeichnisstruktur zu machen. Denn je übersichtlicher diese gehalten ist, desto einfacher ist das Backup.

Einige Grundideen:

Das Verzeichnis des Seitenbetreibers liegt beispielsweise unter:
/var/customers/webs/seitenbetreiber

Somit ist eine strukturelle Aufteilung der Webseiten des Seitenbetreibers sehr einfach möglich. Webseite A liegt nun zum Beispiel unter /var/customers/webs/seitenbetreiber/webseite-a und Webseite B unter /var/customers/webs/seitenbetreiber/webseite-b und so weiter und so fort. Das Backupverzeichnis ist demnach unter /var/customers/webs/seitenbetreiber/backups.

Daraus ergibt sich also folgende Struktur auf dem Server, wird das Verzeichnis des Seitenbetreibers aufgelistet.

1
2
3
4
5
6
7
server seitenbetreiber # ll
insgesamt 16K
-rwxr-xr-x 1 root  root  1,2K  4. Nov 18:29 backup-webseite-a.sh
-rwxr-xr-x 1 root  root  1,2K  4. Nov 18:29 backup-webseite-b.sh
drwxr-xr-x 3 root  root  4,0K  4. Nov 02:06 backups
drwxrwxr-x 4 10001 10001 4,0K  4. Nov 18:29 webseite-a
drwxrwxr-x 2 10001 10001 4,0K  4. Nov 00:00 webseite-b

Wie hier zu sehen ist, liegen auch in diesem Verzeichnis die Backupscripte für die einzelnen Webseiten. Ich persönlich bevorzuge es, die Seiten einzeln zu „verarbeiten“ da sie sich so auch separierter wieder herstellen lassen, wenn zum Beispiel ein Datenverlust nur eine Seite betrifft.

Das Backupscript

Dieses ist ein reines Bash-Script welches letztlich als Cronjob ausgeführt wird. Also nach einem festgelegten Zeitplan. Zur Übersicht wird durch das Script im Backupverzeichnis wieder eine eigene Verzeichnisstruktur erstellt, welche dem Schema backups/webseite/Jahr-Monat folgt. Also backups/webseite-a/2010-11 als Beispiel. Auch wird in diesem Script gleich die Datenbank der Seite mit berücksichtigt und dem Backuparchiv als mySQL-Dump hinzugefügt.

Hierfür müssen im Script zunächst einige Variablen definiert werden.

1
2
3
4
5
6
7
8
9
10
11
12
13
# setting variables
DATE=`/bin/date '+%Y-%m-%d'`
MONTH=`/bin/date '+%Y-%m'`
CUSTOMER_DIR="/var/customers/webs/seitenbetreiber/"
SOURCE_DIR="webseite/"
BACKUP_DIR="backups/webseite"
MYSQLDUMP_FILENAME="webseite-backup-datenbank.sql"
BACKUP_FILENAME="webseite-backup-filesystem.tar.gz"

DB_NAME=""
DB_USER=""
DB_PASSWORD=""
DB_HOST="localhost"

Diese variablen werden im Laufe des Scriptes benötigt, damit man nicht immer alle Pfadangaben hinschreiben muss. Da dieses Script durch einen Cronjob gestartet wird, muss man natürlich sicher gehen, dass es auch im richtigen Ordner ausgeführt wird.

1
cd $CUSTOMER_DIR

Damit werden alle im Script folgenden Operationen im CUSTOMER_DIR ausgeführt.

Als nächstes folgt die Datenbank. Dazu wird ein mySQL-Dump erstellt. Im gleichen Atemzug wird sicher gestellt, dass der Dump auch im UTF-8 Format ist und die Daten bei der Wiederherstellung ebenfalls als UTF-8 in die Datenbank geschrieben werden.

1
2
# dumping mysql and storing dump in $SOURCE_DIR
/usr/bin/mysqldump --opt -Q -u$DB_USER -p$DB_PASSWORD -h$DB_HOST $DB_NAME | sed s'/DEFAULT CHARSET=.*;/DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;/g' > $SOURCE_DIR$DATE-$MYSQLDUMP_FILENAME

Dar Dump der Datenbank wird hierbei direkt im Verzeichnis der Webseite abgelegt und ist somit im Backup enthalten. Nun können die Dateien zu einem Archiv zusammen gefasst und im Backupordner gespeichert werden.

1
2
3
4
5
6
7
# tar $SOURCEDIR
if [ ! -d "${BACKUP_DIR}/${MONTH}/" ];
then
    mkdir $BACKUP_DIR/$MONTH/
fi

/bin/tar -cpzf $BACKUP_DIR/$MONTH/$DATE-$BACKUP_FILENAME $SOURCE_DIR

Somit liegt nun eine .tar-gz-Datei im dafür vorgesehenen Ordner. Versehen mit Datum und allem was dazu gehört.

Im nächsten Schritt wird der mySQL-Dump, welcher ja immer noch im Verzeichnis der Webseite liegt, wieder gelöscht, denn der soll ja nicht einfach da, er wäre ja momentan downloadbar.

1
2
# removing mysql-dump from $SOURCE_DIR
rm -f $SOURCE_DIR$DATE-$MYSQLDUMP_FILENAME

Damit wäre das Script eigentlich schon fertig. Doch, was ist mit der Bereinigung von „Altlasten“? Also was ist mit den alten Backups? Natürlich können Backups, wenn man sie nicht mehr braucht gelöscht werden. So ist es zum Beispiel, wenn dieses Script täglich ausgeführt wird, nicht notwendig mehr als 3 Tage zu speichern. Also kann alles was älter ist doch gelöscht werden. Dateileichen auf einem Server mag man doch sowieso nicht. Auch eventuelle leere Verzeichnisse, die durch das Löschen der Dateien entstehen können entsorgt werden.

1
2
3
4
5
6
7
8
9
10
11
# deleting backups older than 3 days
if [ "$(find $BACKUP_DIR/ -type f -mtime +2)" ];
then
        find $BACKUP_DIR/ -type f -mtime +2 | xargs rm
fi

# removing empty directorys
if [ "$(find backups/ -type d -empty)" ];
then
    find $BACKUP_DIR/ -type d -empty | xargs rm -r
fi

Damit sind nun auch sämtliche Dateileichen entfernt.

Der Cronjob

Damit das Script auch wirklich dann zeitgesteuert ausgeführt wird, empfiehlt es sich, dies über einen Cronjob erledigen zu lassen. In diesem Beispiel wird das Script täglich um 1:30 Uhr nachts ausgeführt.

Der Aufruf um Cronjobs bearbeiten zu können ist

1
crontab -e

Danach gibt man nur noch die „Zeitsteuerung“ und das auszuführende Script an

1
2
######## File Backup ########
30 1 * * * sh /var/customers/webs/seitenbetreiber/backup-webseite.sh # Backup täglich um 1:30 nachts

Nachdem das nun abgespeichert wurde, muss man eigentlich nur noch bis 1:30 Uhr in der Nacht warten und kann den Erfolg begutachten.

Das komplette Script

Wem es nun zu mühsam ist, sich die Teile des Scriptes hier einzeln zusammenzubauen, hier das gesamte Script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/bin/bash

# setting variables
DATE=`/bin/date '+%Y-%m-%d'`
MONTH=`/bin/date '+%Y-%m'`
CUSTOMER_DIR="/var/customers/webs/seitenbetreiber/"
SOURCE_DIR="webseite/"
BACKUP_DIR="backups/webseite"
MYSQLDUMP_FILENAME="webseite-backup-datenbank.sql"
BACKUP_FILENAME="webseite-backup-filesystem.tar.gz"

DB_NAME="Name der Datenbank"
DB_USER="Datenbanknutzer"
DB_PASSWORD="Passwort"
DB_HOST="localhost"

# starting backup
cd $CUSTOMER_DIR

# dumping mysql and storing dump in $SOURCE_DIR
/usr/bin/mysqldump --opt -Q -u$DB_USER -p$DB_PASSWORD -h$DB_HOST $DB_NAME | sed s'/DEFAULT CHARSET=.*;/DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;/g' > $SOURCE_DIR$DATE-$MYSQLDUMP_FILENAME

# tar $SOURCEDIR
if [ ! -d "${BACKUP_DIR}/${MONTH}/" ];
then
    mkdir $BACKUP_DIR/$MONTH/
fi

/bin/tar -cpzf $BACKUP_DIR/$MONTH/$DATE-$BACKUP_FILENAME $SOURCE_DIR

# removing mysql-dump from $SOURCE_DIR
rm -f $SOURCE_DIR$DATE-$MYSQLDUMP_FILENAME

# deleting backups older than 3 days
if [ "$(find $BACKUP_DIR/ -type f -mtime +2)" ];
then
        find $BACKUP_DIR/ -type f -mtime +2 | xargs rm
fi

# removing empty directorys
if [ "$(find backups/ -type d -empty)" ];
then
    find $BACKUP_DIR/ -type d -empty | xargs rm -r
fi

Es sei hier noch gesagt, das dieses Script immer nur die in den Variablen angegebene Webseite sichert. Will man mehrere Webseiten sichern, so erstellt einfach mehrere Scripte und passt die entsprechenden Variablen an. Natürlich sind auch die Variablen für die Datenbank noch anzupassen :-)

Warum ich das so mache, habe ich eingangs ja schon erläutert. Ich finde es so einfach komfortabler.

Die Nutzung dieses Scriptes geschieht auf eigene Gefahr. Ich übernehme keinerlei Haftung oder Verantwortung für eventuellen Datenverlust.

Update

09. November 2010

Kleinen Syntaxfehler in der Abfrage der alten Dateien und leeren Verzeichnisse behoben.

Artikel / Seite weiterempfehlen

15 Meinungen zu “Backup der Webseite und Datenbank

  1. Der Artikel ist wirklich sehr hilfreich. Danke dafür!
    Doch leider zeigt er mir noch zahlreiche Arbeit auf, die noch vor mir liegt =/

  2. Gutes Script.
    Aber warum ersetzt Du den Zeichensatz generell durch UTF8? Wenn die Daten bisher in einem anderen Zeichensatz waren, z.B. den in älteren MySQL-Versionen standardmäßig verwendeten latin1, dann sollten sie doch auch so gesichert werden. Auf UTF-8 umstellen kann man den Backup bei Bedarf ja immer noch später.

    Gruß, Frank

  3. Hallo Frank,

    Das ist bei mir irgendwie ein Automatismus. Genau erklären kann ich das auch nicht .-)
    Ich hab mir einfach angewöhnt, bei meinen Projekten UTF-8 quasi zu „forcen“. Und wenn das dann doch mal zurück gespielt werden muss, kann ich diesen Schritt schon mal nicht mehr vergessen. In des Scripten stell ich das meist recht schnell um, doch die DB vergesse ich gerne mal dann. Vielleicht daher diese Angewohnheit.

  4. Hi :)

    Vielen Dank erst einmal für das Script.
    Ich habe es auf 2 meiner Seiten angewandt und bin bei einer davon auf ein (Sehr selten vorkommendes) „Problem“ gestoßen.

    Ändert sich zwischenzeitlich das Passwort der Datenbank, läuft das Script trotz dem misslungenen Login weiter und schreibt

    1
    $MYSQLDUMP_FILENAME

    als leere Datei.
    Klar, soweit kein Problem, und man muss Sorge dafür tragen, dass das Script aktuell ist, keine Frage.

    Aber gibt es für so Idioten, wie mich, eine Möglichkeit, dem Script bei einem Fehlerhaften Login-Versuch die .sql eben nicht zu erzeugen?

  5. Hi.

    nette sache, wirklich.

    Nur leider gibt es bei mir Fehler über fehler, wenn ich die Einstellungen mache, die für meine Site (DB-Infos, Website-Pfad, etc.) wichtig sind.
    Bin auch kein Experte, was cgi/pl Files angeht.

    Gruß Oliver

    • Hi Oliver,

      Es wäre interessant zu erfahren, welche Einstellungen Du genau vornimmst, und welche Fehler es gibt. Also was genau die Konsole ausgibt. Bei den Pfaden handelt es sich zum Beispiel um die Pfade auf dem Server selbst und nicht um die Pfade innerhalb der Webseite. Was die Datenbankinfos angeht, so sind das die Selben, die Du auch für die Webseite nutzt. Problematisch wird es natürlich, wenn Dein DB-Server nicht auf der gleichen Maschine liegt, wie Deine Webseite. Also wenn der nicht über localhost zu erreichen ist.

      Auch wichtig, es handelt sich hier um ein Bash-Script, welches in der Linux-Konsole des Servers ausgeführt wird. Windows selbst versteht dieses nicht. Auch ist dieses Script nicht über den Browser ausführbar.

  6. So eben bekam ich eine Mail von Oliver, die ich hier der Vollständigkeit halber anfüge:

    Hi.

    Denke mal, ist besser, als jetzt den Blog vollzutexten.

    Also, ich habe eine eigene HP bei Netpukis.
    Das „komplette“ Skript habe ich als CGI im CGI-BIN Verzeichnis und ein vorabtest mit der CONFIXX Oberfläche zeigt mir halt, das da Semikolons (,) fehlen, Befehle nicht gefunden werden und was nicht noch alles.

    Ich gebe Dir mal eben den Test-Output vom Perl-Debugger:
    Bareword found where operator expected at /html/cgi-bin/mybackup.cgi line 21, near „/usr/bin“

    (Missing operator before bin?)

    Unquoted string „bin“ may clash with future reserved word at /html/cgi-bin/mybackup.cgi line 21.

    Unquoted string „mysqldump“ may clash with future reserved word at /html/cgi-bin/mybackup.cgi line 21.

    Bareword found where operator expected at /html/cgi-bin/mybackup.cgi line 21, near „–opt“

    (Missing operator before opt?)

    Scalar found where operator expected at /html/cgi-bin/mybackup.cgi line 21, near „$DB_HOST $DB_NAME“

    (Missing operator before $DB_NAME?)

    Unquoted string „sed“ may clash with future reserved word at /html/cgi-bin/mybackup.cgi line 21.

    Can’t modify constant item in scalar assignment at /html/cgi-bin/mybackup.cgi line 4, near „`/bin/date ‚+%Y-%m-%d’`;“

    syntax error at /html/cgi-bin/mybackup.cgi line 21, near „/usr/bin“

    Substitution replacement not terminated at /html/cgi-bin/mybackup.cgi line 21.

    — ENDE

    Kannst Du was damit anfangen?
    Oder Brauchst du noch bestimmte Angaben?

    Vielen Dank schon mal. Möchte das echt umsetzen.

    Gruß Oliver

    Lieber Oliver,
    Ja, ich kann damit etwas anfangen, und bitte Dich noch Mals meine vorherige Antwort durchzulesen, besonders den zweiten Absatz, denn dort wird Folgendes erwähnt:

    Auch wichtig, es handelt sich hier um ein Bash-Script, welches in der Linux-Konsole des Servers ausgeführt wird.

    Dieser Sachverhalt ist im übrigen auch im Artikel selbst erwähnt:

    Dieses ist ein reines Bash-Script welches letztlich als Cronjob ausgeführt wird.

    Dein Problem ist nun Folgendes:
    Du versuchst ein Bash-Script durch einen Perl-Interpreter zu jagen. Der erste Fehler war, die Dateiendung einfach zu ändern. Der zweite Fehler war es, anzunehmen, dass dieses Script dadurch in einem fremden Interpreter ausgeführt werden kann. Grundsätzliche gilt: Bash != Perl/CGI

    Was ich Dir damit sagen will, Du kannst dieses Script nicht einfach in einem anderen Interpreter werfen und darauf vertrauen das es funktioniert.

    Nun zu meinem Grund, warum ich dies hier erwähne. Ganz einfach, es ist nicht die feine englische Art eine Diskussion in einen anderen Kanal zu tragen. Andere haben eventuell ähnlich gelagerte Probleme und erhoffen sich auch eine Lösung. Daher zurück von der Mail hier in den Blog :-)

    • okay. ;-)
      Geb Dir recht. Dachte, da nicht dran.

      Also, die „Variablen“ habe ich im Dateikopf verändert.
      von *.cgi nach *.sh bennant.
      Ein Cron-Tab gemacht und nun mal schauen, ob da Daten in dem Verzeichnis landen.

    • So, CRON läuft soweit.

      Jetzt erscheint aber immer:

      /bin/tar: Entferne führende „/“ von Elementnamen
      /bin/tar: /var/www/web131/html/backups/webseite/2011-02/2011-02-22-web131_fs.tar.gz: Datei hat sich beim Lesen geändert.

      Dieses sind die „Kopfdaten“:

      1
      2
      3
      4
      5
      6
      7
      DATE=`/bin/date '+%Y-%m-%d'`
      MONTH=`/bin/date '+%Y-%m'`
      CUSTOMER_DIR="/var/www/web131/html/"
      SOURCE_DIR="/var/www/web131/html/"
      BACKUP_DIR="/var/www/web131/html/backups/webseite"
      MYSQLDUMP_FILENAME="web131-1_db.sql"
      BACKUP_FILENAME="web131_fs.tar.gz"

Schreibe einen Kommentar

Ihre Email-Adresse wird nicht veröffentlicht. Pflichtfelder sind durch * markiert.

Sie können folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>