FTP Backups (bisher)
Mein Blog läuft auf dem Hoster ALL-INKL.com, den ich nebenbei gesagt nach mehreren Jahren immernoch empfehlen kann.
Bisher habe ich halbwegs (un)regelmäßig manuell, von Hand, FTP Backups erstellt. Also alle paar Monate habe ich über einen FTP Tool wie FileZilla bestimmte Ordner heruntergeladen, Datei für Datei, anschließend in ein Archiv gepackt und irgendwo verstaut. Dieser Prozess ist natürlich in vielerlei Hinsicht nicht zuverlässig, zeitintensiv und aufwändig.
Auch das Sichern der Ordner automatisiert mit Tools wie SyncBack (mein Artikel dazu) wird immer zeitintensiver, je größer die zu sichernde Datenmenge wird. Außerdem muss immer ein PC, auf dem die Tools laufen, an sein.
FTP Backups via PHP Skript
Sinnvoller ist es, diese Backups auf dem Server des Webhosters erstellen zu lassen. So wird der heimische PC nicht belastet.
All-Inkl bietet dafür ein recht einfaches PHP Skript zum Sichern eines einzelnen FTP Ordners oder des gesamten All-Inkl FTP Accounts.
Ich habe dieses Skript ausgebaut und um verschiedene Funktionen ergänzt:
- beliebig viele Ordner des All-Inkl Accounts in einzelne .tar.gz Archive sichern
- Einschränkung der Anzahl aufgehobener Backups – älteste Backups werden automatisch gelöscht
- detaillierte Ausgabe inklusive benötigter Zeit
- E-Mail Benachrichtigung
Screenshot
Code
Code anzeigenDen Code könnt ihr bequem mit den Links/Rechts Pfeiltasten horizontal bewegen.
<? // PHP-Konfiguration optimieren // if no errors are shown, please check htaccess restrictions by "php_flag display_errors off" // in this or parent folders @error_reporting(E_ALL); @ini_set("max_execution_time", 300); @ini_set("memory_limit", "256M"); header('Content-Type: text/html; charset=utf-8'); include "Archive/Tar.php"; $pfad = preg_replace('/(\/www\/htdocs\/\w+\/).*/', '$1', realpath(__FILE__)); $alltime = 0; // ########## EDIT THIS VARIABLES ################### $foldertobackup = array("bonnie", "tests", "locationmap", "blog"); // which root folders should get backed up? $backupfilemaximum = 2; // how many archives should be stored? $dir = $pfad."backup/"; // in which subfolder is this backup php file? this would be: "root/backup/" $sendmail = 1; // send notification mail when all backups are done - should be 1/"yes"/"ja" or 0/"no"/"nein" $sendmailto = "admin@yourdomain.com"; // valid mail address to send the mail to // ################################################## foreach ($foldertobackup as $verzeichnis) { $jobtime = time(); echo "<br><br>########################################<br>"; echo "<strong>Verzeichnis ".$verzeichnis." wird gesichert...</strong><br>"; flush(); // Name: [verzeichnis]_[Datum]_[Uhrzeit].tar.gz $archivname = $verzeichnis.date('_Y-m-d_His').".tar.gz"; // Name: [All-Inkl-Accountname]_[Datum]_[Uhrzeit].tar.gz //$archivname = preg_replace('/.+\/(.+)\/$/', '$1', $pfad).date('_Y-m-d_His').".tar.gz"; // Auszuschließende Ressourcen $ignorieren = array("*.sql.gz", "*.tar.gz", "usage", "logs"); // ######### create backup $archiv = new Archive_Tar($archivname, true); $archiv->setIgnoreList($ignorieren); $archiv->createModify($pfad.$verzeichnis, "", $pfad); $backuptime = time() - $jobtime; if (is_int($backuptime)) { echo "Backup fertig: ".$archivname." (Dauer: ".$backuptime." Sekunden)<br>"; } else { echo "Backup fertig: ".$archivname."<br>"; } echo "Aufräumen der Backups...<br>"; flush(); // integer starts at 0 before counting $i = 0; $backupfiles = array(); // ######### collect valid backup files if ($handle = opendir($dir)) { while (($file = readdir($handle)) !== false) { if ( is_int(strpos($file, $verzeichnis)) == true && preg_match('/\.tar.gz$/i', $file) && !in_array($file, array('.', '..')) && !is_dir($dir.$file) ) { $backupfiles[$dir.$file] = filectime($dir.$file); } } } echo count($backupfiles)." valide Backups dieses Ordners gefunden, "; echo $backupfilemaximum." Backups sollen behalten werden. "; $backupcountdif = count($backupfiles)-$backupfilemaximum; if ($backupcountdif<=0) { echo "Kein Backup wird gelöscht.<br>"; } else if ($backupcountdif==1) { echo "1 Backup wird gelöscht:<br>"; } else if ($backupcountdif>=2) { echo $backupcountdif." Backups werden gelöscht:<br>"; } flush(); // ######### sort and delete oldest backups // sort backup files by date arsort($backupfiles); // reset counter variable $i = 0; // delete oldest files foreach ($backupfiles as $key => $value) { if($i>=$backupfilemaximum) { echo $key." wird gelöscht...<br>"; if (unlink($key)) { echo "Datei erfolgreich gelöscht.<br>"; } else { echo "Fehler beim Löschen der Datei.<br>"; } } $i++; } $jobendtime = time() - $jobtime; if (is_int($jobendtime)) { echo "######################################## (Dauer: ".$jobendtime." Sekunden)<br>"; $alltime += $jobendtime; } else { echo "########################################<br>"; } } echo "<br><br>Die automatische Sicherung des FTP-PHP-Backup-Skripts '".pathinfo(__FILE__, PATHINFO_BASENAME)."' hat ".count($foldertobackup)." Verzeichnisse in insgesamt ".$alltime." Sekunden gesichert.<br><br>"; // ######### send mail if (!isset($sendmail) || $sendmail== 0 && in_array($sendmail, array("no", "nein"))) { echo "Benachrichtigungsmail wurde nicht verschickt."; } else { if(!preg_match( '/^([a-zA-Z0-9])+([.a-zA-Z0-9_-])*@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-]+)+/' , $sendmailto)) { echo "FEHLER: Mail konnte nicht versendet werden, da die Adresse ungültig ist!"; } else { mail( $sendmailto, "Automatische FTP Sicherung abgeschlossen", "Die automatische Sicherung des FTP-PHP-Backup-Skripts ".pathinfo(__FILE__, PATHINFO_BASENAME)." hat ".count($foldertobackup)." Verzeichnisse in insgesamt ".$alltime." Sekunden gesichert.", "From: backupscript@{$_SERVER['SERVER_NAME']}\r\n" . "Reply-To: backupscript@{$_SERVER['SERVER_NAME']}\r\n" . "Content-Type: text/html\r\n" ) or die("FEHLER: Mail konnte wegen eines unbekannten Fehlers nicht versendet werden"); echo "Benachrichtigungsmail wurde erfolgreich verschickt!"; } } ?>
Wichtige Anmerkungen
Bitte beachtet, dass einige Zeilen angepasst werden müssen und der Code nur bei dem Webhoster All-Inkl getestet wurde. Die Zeile
include "Archive/Tar.php"
, die ein externes Modul einbindet, könnte auf anderen Websern zu Problemen führen. Am besten testet ihr es einfach.
Thema Sicherheit: Wie Kenny in den Kommentaren korrekt angemerkt hat, müssen noch Anpassungen erfolgen, wenn das Skript als tatsächliche Backup-Lösung zum Einsatz kommen soll.
- Die Sicherungen landen bei diesem Skript direkt in dem Skript-Ordner. Die Sicherung(en) sollte(n) nach der Sicherung an einen anderen Ort, beispielsweise auf einen anderen Server oder ein NAS, kopiert werden. Oder ihr automatisiert den Download der Sicherungen nach dem Backupprozess.
- Sowohl das Skript als auch die Sicherungen können ohne weitere Maßnahmen direkt angesprochen werden. Ihr solltet natürlich den Backup Ordner beispielsweise durch eine htpasswd absichern und den Zugriff auf die Backups über die htaccess einschränken.
Ich werde dieses Online-Backup Thema in Zukunft vermutlich nochmal aufgreifen, erweitern und optimieren. Ich verlinke das dann hier.