verbessere Version: FTP-Backup-Script mit ncftp – So sichere ich meinen Webspace Part 3

In diesem Teil zeige ich euch meine aktuell im Test befindliche 2. Version des ehemaligen Shellscripts, mit dem ich meinen Webspace auf all-inkl.com auf meinen lokalen Server sichere. Ich habe dieses neu in Python implementiert.

Erforderliche Ordnerstruktur

Als lokalen Server habe ich einen Raspberry Pi erfolgreich getestet, da dieser sehr stromsparend betrieben werden kann.

Wichtig: Man sollte sich vorher ansehen, wie groß die auf dem Webspace das Hosting-Anbieters (bei mir all-inkl.com) im Filesystem gespeicherten Dateien sind. Es darf nicht dazu kommen, dass der Speicherplatz auf der SD-Karte im Raspberry Pi zu klein wird. Den freien Speicherplatz auf dem Raspberry Pi zeigt man sich wie folgt an.

# per ssh auf der raspberry pi angemeldet
# anzeige des freien Speichers in megabyte
df -hm

Damit das Script problemlos arbeitet, habe ich folgende Verzeichnisstruktur unter dem Home-Verzeichnis des Users pi eingerichtet. Wenn man diese anderes möchte, muss man das Script ein bisschen verändern.

#verzeichnis für website-backups
/home/pi/test/allinkl_backup/website
#verzeichnis für mysql-backups
/home/pi/allinkl_backup/website/mysql

Script

Das Script verwendet wiederum für den eigentlichen FTP-Dateitransfer die Spezialform für Scripte ncftpget. Mit diesem habe ich sehr gute Erfahrungen beim rekursiven Download bei FTP gemacht.

Diesen installiert man einfach über apt-get:

#installation des ncftp-client
sudo apt-get install ncftp

Meine Dateien der Web-Content-Mangement-Systeme (WordPress, Drupal, phpbb), liegen unter

/websites

Die mit dem mysqldumper erstellten tar.gz-Datenbankbackups liegen unter:

/MySqlDumper/work/backup/

Die einzelnen Stellen des Scripts sind kommentiert. So sieht man was es tut.

Vorteile des Scripts

  • Es können über 1 Script die verschiedenen Sicherungsarten abgefangen werden. Dies erleichtert die Wartbarkeit.
  • Das Script kann leicht um weitere Sicherungsarten erweitert werden um andere Verzeichnisse zu sichern.
  • Es können mehrere Sicherungsarten gleichzeitig ablaufen, da jede in ihrem eigenen Verzeichnis abläuft (keine critical section mehr).
  • Es werden mehrere Generationen von Sicherungen vorgehalten. Sobald ein individuell einstellbare Grenze (TIME_OFFSET_IN_SECONDS) überschritten wird, werden veraltete Sicherungen nach jedem Lauf automatisch bereinigt.
  • Da es in Python implementiert wurde, ist mehr Programmlogik möglich (ich verfüge leider noch nicht über die dazu notwendigen Shell-Scripting-Kenntnisse).
#!/usr/bin/env python
# -*- coding: utf-8

import os
import sys
import time
import subprocess
import shutil

#1. Variablen

#ftp-zugang
host="domain_ftp_server"
user="ftp_user"
password="passwort_ftp_user"

#zu sichernde Dateien - sicherungsarten
try:
    art=sys.argv[1] #übergabe der Sicherungsart als Argument in Kommandozeile
except IndexError:
    print "Geben Sie die Sicherungsart an (mysql, website)"
    sys.exit(1)
sicherungsarten=("mysql", "website")
if art not in sicherungsarten:
    print "falsche sicherungsart --> programm beendet"
    exit()

#lokales Ziel
ziel = "/home/pi/test/allinkl_backup"
ziel = os.path.join(ziel, art)

#Sicherungspfade festlegen
sicherungspfade=[]
if art == "mysql":
    #sicherung datenbankdumps
    sicherungspfade.append("/MySqlDumper/work/backup/")
elif art == "website":
    #sicherung webseiten
    sicherungspfade.append("/websites")

print("Sicherungspfade: ",sicherungspfade)

#maximales Alter Sicherung
TIME_OFFSET_IN_SECONDS = 60*60*24*7*4  # <= 60s * 60m * 24h * 7d * 4w

#Zeitangaben
datum = time.strftime("%Y%m%d")
uhrzeit = time.strftime("%H%M%S")

#Zielpfad festlegen
zielverzeichnis="backup_"+datum+"_"+uhrzeit
zielpfad=ziel+os.sep+zielverzeichnis

#2. Sicherung durchführen
def sichern():
    #sicherung durchführen
    for sicherungspfad in sicherungspfade:
        #sicherungsbefehl
        command = "ncftpget -R -u %s -p %s %s %s %s" % (user, password, host, zielpfad, sicherungspfad)
        print command
        try:
            ncftp=subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
            print "ncftp erfolgreich gestartet"
            #for line in iter(ncftp.stdout.readline, ''): print line
        except subprocess.CalledProcessError as e:
            ret = e.returncode
            if ret in (1, 2):
                print("the command failed")
            elif ret in (3, 4, 5):
                print("the command failed very much")
            print "Returncode:",ret
            return ret

#3. tar.gz erstellen
def tarErstellen():
    os.chdir(ziel)
    print "Wechsel in", os.getcwd()
    command ="tar -czvf %s.tar.gz %s/" % (zielverzeichnis, zielverzeichnis)
    print command
    tar=subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    for line in iter(tar.stdout.readline, ''): print line

    os.chdir(ziel)
    command ="rm -r %s" % (zielverzeichnis)
    print command
    rm=subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

#4. Bereinigen von alten Sicherungen (TIME_OFFSET_IN_SECONDS)
def remove_files(path):
    time.sleep(60)
    for root, _, files in os.walk(path):
            anzahlSicherungen=len(files)
            for filename in files:
                full_path = os.path.join(root, filename)
                print full_path,"-- Alter: ",(time.time()-os.path.getmtime(full_path))/(60*60*24)," Tage"
                if time.time() - os.path.getmtime(full_path) > TIME_OFFSET_IN_SECONDS:
                    try:
                        print "Sicherung veraltet: full_path=",full_path
                        if anzahlSicherungen>1: #mindestens eine Sicherungsdatei vorhalten
                            os.remove(full_path)
                            anzahlSicherungen-=1
                        else:
                            print "keine Bereinigung durchgeführt, da mind. 1 Sicherungsdatei erforderlich"
                    except Exception as e:
                        print('Datei "{}" konnte nicht gelöscht werden. Fehlermeldung: {}'.format(
                            full_path, e))

def main():
    sichern()
    tarErstellen()
    print ziel
    remove_files(ziel)

if __name__ == '__main__':
    main()

Das Script binde ich als cronjobs des Users pi ein und lasse es so automatisiert und regelmäßig über Nacht laufen. Dem Script wird immer die jew. Sicherungsart übergeben:

#als user pi crontab anlegen
crontab -e
#folgende Zeile einfügen, wobei es abhängt, wo das shell-script gespeichert ist
#damit wird jede Woche am sonntag um 1 uhr eine Sicherung gestartet
0 1 * * 7 /home/pi/scripts/main.py mysql
#damit wird jeden Monat um 4 Uhr ein Sicherungslauf gestartet
0 4 1 * * /home/pi/scripts/main.py website
#die datei wird gespeichert

Über dieses Script habe ich es geschafft, meinen kompletten sicherungsrelevanten Bereich bei meinem Webhostinganbieter lokal doppelt vorzuhalten. Es wird beständig verbessert und alle Änderungen in diesem Betrag eingefügt.

Schreib einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *


*