Pragmatische Backups für das Heimnetzwerk

Motivation und Ausgangslage

Ein- oder zweimal im Jahr kommt es bei mir vor, dass sich von irgend einem meiner Rechner mal wieder die Festplatte verabschiedet und sich entschließt, plötzlich und unerwartet vor ihren Schöpfer zu treten. Und das obwohl sich die Rechner kaum von der Stelle bewegen, nur durchschnittlich oft benutzt und immer sorgfältig behandelt werden. Da ich somit schon viel zu oft irgendwelche Daten verloren habe, musste ich irgendwann zu der Einsicht kommen, dass ein Backup nur dann ein gutes Backup ist, wenn es vollautomatisch und ohne jegliches Zutun von Anwenderseite durchgeführt werden kann. Insbesondere darf es nicht sein, dass ein Backup erst manuell angestoßen werden muss (zum Beispiel durch Einstecken einer Backupplatte), da dies in der Realität viel zu oft vergessen wird. Beim letzten Plattencrash hat es ausgerechnet meinen Hauptcomputer erwischt, wodurch ich ganze zwei Monate zurückfiel, weil eben genauso lange das letzte Backup her war.

Damit sich ein solcher Fall nicht nochmal ereignen kann, beschloss ich einen gerade günstig gekauften Minirechner (400 MHz VIA CPU, 10 EUR gebraucht) als vollautomatischen Backupserver zu konfigurieren. Das Prinzip ist einfach: Mit Hermes habe ich ohnehin einen Rechner, der rund um die Uhr läuft, den ich aber nicht als Backupserver verwenden will. Er weckt jede Nacht um vier Uhr den Backupserver auf (Cronjob) und sorgt per SSH dafür, dass ein vordefiniertes Backupprogramm angestoßen wird. Der Backupserver kümmert sich dann darum, alle Computer im Netzwerk hochzufahren, zu sichern und wieder herunterzufahren. Anschließend schaltet er sich von selbst aus.

Der Aufbau sieht also wie folgt aus:

Abb.: Systemlandschaft mit Backupserver
[Backupserver]---USB---[Festplatte 1.5 TB]
      |
      +---LAN-----+-------------+
                  |             |
               [Router]   [Heimserver]
                  |
      +--LAN--+---+---+-------+
      |       |       |       |
    [PC1]   [PC2]   [PC3]   [PC4]   ...

[Heimserver] weckt [Backupserver].

Backupserver weckt [PC1] und führt Backup durch

Backupserver fährt [PC1] runter, wenn niemand angemeldet

...

Backupserver fährt runter

Natürlich werden nur die Rechner aufgeweckt, die nicht schon ohnehin eingeschaltet sind. Der Heimserver muss also nicht aufgeweckt werden, da er immer an ist. Die Existenz der anderen Rechner kann mit Ping überprüft werden. Damit wird auch überprüft, ob nach einem Wake-On-Lan der Rechner tatsächlich hochgefahren wurde. Beim Abschalten gilt umgekehrt, dass der Heimserver nicht heruntergefahren darf, da somit die automatischen Backups nicht mehr angestoßen werden könnten. Alle anderen Computer dürfen im Prinzip heruntergefahren werden, jedoch nur, wenn kein Benutzer mehr angemeldet ist. Arbeitet noch jemand an dem Rechner, soll der Rechner also einfach eingeschaltet bleiben.

Hinzu kommt, dass manche meiner Rechner nicht wake-on-lan fähig oder nicht ans Netzwerk angebunden sind. Hierbei handelt es sich um historische Rechner mit DR-DOS oder ATARI TOS statt Linux. Aber auch die AW4416 Audio Workstation von Yamaha zählt hierzu. Diese Geräte können nicht vollautomatisch gesichert werden. Für sie muss der Zugriff auf den Backuprechner per FTP oder SFTP möglich sein, um manuelle Backups durchführen zu können.

Benannte Hosts aufwecken

Voraussetzung für den obigen Plan ist, dass alle Rechner mit einer festen IP-Adresse oder einem festen Domainnamen angesprochen werden können. Hierfür gibt es verschiedene Lösungsansätze; in meinem Fall ist es so, dass dnsmasq als kombinierter DHCP/DNS-Server jedem Rechner anhand seiner MAC-Adresse eine IP und eine Subdomain zuordnet.

Hinzu kommt ein eigenes Python-Script, welches das wakeonlan Kommando (muss unter Debian nachinstalliert werden!) einhüllt. Denn wakeonlan funktioniert nur mit einer MAC-Adresse und die kenne ich in der Regel nicht auswendig. Stattdessen greift das Skript auf eine zentral im Netzwerk abgelegte Konfigurationsdatei zurück, die jeder MAC-Adresse einen Namen zuordnet.

Zuordnung von Namen zu MAC-Adressen
192.168.178.004 00:50:45:5D:89:0C * jake
192.168.178.005 00:40:63:C9:BD:F3 * backup
192.168.178.020 00:A0:D1:9B:79:6E * blues-mobil

Demnach sind folgende Aufrufe identisch:

$ wake-on-lan  00:40:63:C9:BD:F3
$ ./wakehost backup

Hier der Quellcode des Skripts:

wakehost.py
#! /usr/bin/env python
#encoding=utf-8

# wakehost.py
# Simple wakeonlan wrapper in order to wake named hosts
#
# Copyright 2010 Dennis Schulmeister <dennis(at)developer-showcase.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.

# Configuration values
HOST_FILE_URL = "http://hermes/hosts.txt"
TIMEOUT = 10

# Imported modules
import subprocess, sys, urllib2

# Function definitions
def main():
    '''
    Main routine of this script. Returns system return code.
    '''
    try:
        host_file = urllib2.urlopen(HOST_FILE_URL, None, TIMEOUT)
    except urllib2.URLError as ex:
        print str(ex)
        return 1

    try:
        hostname = sys.argv[1]
    except IndexError:
        hostname = ""

    error = False

    if hostname:
        mac = ""

        for line in host_file:
            line = line.lstrip().rstrip()
            host = line.split(" ")

            if host[3] == hostname:
                mac = host[1]
                break

        if mac:
            try:
                subprocess.call(["wakeonlan", mac])
            except OSError:
                print "OS Error: Unable to run wakeonlan"
                error = True
        else:
            print "Unknown host: %s" % hostname
            error = True
    else:
        for line in host_file:
            line = line.lstrip().rstrip()
            host = line.split(" ")
            print "%s: %s" % (host[1], host[3])

    host_file.close()

    if error:
        return 1
    else:
        return 0

# Program start
if __name__ == "__main__":
    sys.exit(main())

Konfiguration des Heimservers

xxx

Konfiguration des Backupservers

xxx

Konfiguration der Clients

xxx


attachments

imageappend Append an Image
>