Vor einigen Tagen konnte ich einen Punkt von meiner Aufgabenliste streichen, welchen ich schon längere Zeit offen hatte und eigentlich schon lange einmal hätte umsetzen wollen und sollen: das Einrichten eines Backups für meine Webseite.
Bisher machte ich nur sporadisch entweder manuelle Backups der Daten der Webverzeichnisse und der dazugehörigen MySQL-Datenbanken oder ich habe bei meinem VPS-Provider ein Backup für die ganze Maschine durchgeführt – beides nicht gerade sehr sexy, das muss besser und eleganter gelöst werden, dachte ich mir.
Als erstes schrieb ich ein Shell-Skript, welches mir alle Datenbanken (ausser den System-Datenbanken) in Archiv-Dateien exportiert (alle Pfade und Benutzerangaben wurden durch Beispieldaten ersetzt):
#!/bin/bash
BACKUP_DATE="$(date +%Y-%m-%d)"
DB_BACKUP_ROOT="/zielpfad/zum/backup/mysql"
DB_BACKUP="$DB_BACKUP_ROOT$BACKUP_DATE"
DB_USER="Datenbank-Benutzer"
DB_PASSWD="Datenbank-Kennwort"
HN=`hostname | awk -F. '{print $1}'`
# Create the backup directory
mkdir -p $DB_BACKUP
# Remove backups older than 30 days
find $DB_BACKUP_ROOT -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;
# Backup each database on the system using username and password
for db in $(mysql --user=$DB_USER --password=$DB_PASSWD -e 'show databases' -s --skip-column-names|grep -vi information_schema);
do
BACKUP_FILE_PATH="$DB_BACKUP/mysqldump-$HN-$db-$BACKUP_DATE.gz"
mysqldump --user=$DB_USER --password=$DB_PASSWD --opt $db | gzip > $BACKUP_FILE_PATH;
# Set desired permissions
chmod -R 600 $BACKUP_FILE_PATH
done
Das obenstehende Skript macht folgendes:
Zuerst setze ich einige Variablen, wie das Zielverzeichnis für die Backup-Dateien. Ich lese das aktuelle Datum aus und speichere es in einer Variable und benutze dies, um für die täglichen Backups jeweils ein neues Zielverzeichnis innerhalb des festgelegten “DB_BACKUP_ROOT” zu erstellen.
Danach setze ich die MySQL-Zugangsdaten und ermittle den Host-Namen des Rechners, um diesen später auch zum Dateipfad hinzuzufügen.
Mittels des “find”-Befehls ermittle ich alle Backups, welche älter als 30 Tage sind und lösche diese.
Danach iteriere ich in einer Schleife über alle relevanten Datenbanken und speichere diese mittels “mysqldump” an den gewünschten Zielort, dabei pipe ich das Resultat von “mysqldump” zuvor noch an “gzip” und sorge damit, dass ich eine Archivdatei erhalte.
Zum Schluss ändere ich mittels “chmod -R 600 …” noch die Dateirechte der zuvor erstellten Backup-Datei, so dass nur noch der Besitzer der Datei diese lesen und ändern kann.
Nun habe ich die Datenbank gesichert. Mittels eines weiteren Shells-Skripts sichere ich nun noch die Dateien der einzelnen Webseiten:
#!/bin/bash
WWW_ROOT="/var/wwwdir/websites/"
WWW_BACKUP_ROOT="/zielpfad/zum/backup/www"
WWW_BACKUP="$WWW_BACKUP_ROOT`date +%Y-%m-%d`"
HN=`hostname | awk -F. '{print $1}'`
# Create the backup directory
mkdir -p $WWW_BACKUP
# Remove backups older than 30 days
find $WWW_BACKUP_ROOT -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;
# get arguments
while [ "$1" != "" ]; do
case $1 in
-s | --site ) shift
site=$1
;;
* ) echo "Unknown argument $1."
exit 1
esac
shift
done
# Backup website
if [ "$site" != "" ]; then
BACKUP_PATH="$WWW_BACKUP/www-$HN-$site-$(date +%Y-%m-%d).tar.gz"
WWW_PATH="$WWW_ROOT$site"
echo "Backing up site $WWW_PATH to $BACKUP_PATH"
cd $WWW_ROOT
tar -czf $BACKUP_PATH $site
chmod -R 600 $BACKUP_PATH
else
echo "You need to specifiy a site to backup with the -s argument."
exit 1
fi
Dieses Skript macht folgendes:
Als erstes setze ich wieder einige Variablen. Das Quellverzeichnis der Webseiten-Daten wird angegeben (WWW_ROOT) und das (Wurzel-)Zielverzeichnis für die Backup-Daten wird festgelegt (WWW_BACKUP_ROOT). Ausserdem lege ich den Ordernnamen für die täglichen Backups fest (WWW_BACKUP), dazu verwende ich wie schon beim MySQL-Backup das “date”-Befehl.
Mittels mkdir -p $WWW_BACKUP wird der tägliche Backup-Ziel-Ordner erstellt, es sei denn er ist schon vorhanden (dazu ist der “-p”-Parameter da).
Erneut suche ich mittels “find” alle Backup-Dateien, welche älter sind als 30 Tage und lösche diese.
Danach kommt eine “while”-Schleife, mit der ich die Argumente, welche dem Skript übergeben wurden auslese, dies ist nötig, da ich das Skript später mittels Cron-Job aufrufen möchte und jeweils die Quell-Webseite mittels eines Parameters angeben möchte. Wenn keine Website als Argument übergeben oder falls ein unbekanntes Argument übergeben wurde, wird das Skript beendet (exit 1).
Danach wird die eigentliche Backup-Datei erstellt. Dazu wird der Zielpfad der Archivdatei zusammengesetzt (BACKUP_PATH) und dann mittels dem “tar”-Befehl das Backup erstellt und gepackt.
Am Ende passe ich wieder die Dateirechte mittels “chmod” an, so dass nur der Besitzer die Datei lesen und ändern kann.
Nun haben wir zwei Skripts, welche die für mich relevanten Komponenten der Webseiten sichern können, jedoch müssten diese immernoch manuell ausgeführt werden. Damit automatisch täglich die Backups erstellt werden, richtete ich unter einem Account einige Cronjobs (Befehl: crontab -e) ein, welche die (ausführbaren) Skripte einmal am Tag laufen lassen:
Die Backups werden mit dieser Konfiguration also immer um 2:15 Uhr durchgeführt. “/dev/null 2>&1” bewirkt, dass die Ausgaben unterdrückt werden, sie landen im Nirvana.
In der letzten Zeile der obenstehenden Crontab seht ihr, dass noch ein weiteres Skript “copy_backup_to_different_server.sh” zu einem späteren Zeitpunkt aufgerufen wird.
Da die Daten bisher nur lokal auf dem Server gesichert werden, nützen sie mir nichts, falls es zu einem generellen Datenverlust auf dem Server käme. Zur Sicherheit kopiere ich also die Backup-Archive noch an einen weiteren Ort, auf einen anderen Server.
Das Skript kopiert via RSYNC alle neuen Dateien von “/zielpfad/zum/backup” auf den Server “remote.server.com” in den Pfad “/volumeXy/home/userMn/MyFiles/Backup/websites”. Die Verbindung wird im Beispiel mit dem Benutzer “user” durchgeführt.
Die Verbindung geschieht durch den SSH-Aufruf innerhalb des RSYNC-Befehls über eine verschlüsselte SSH-Verbindung.
“–delete” bewirkt, dass im Ziel die Dateien gelöscht werden, welche nicht mehr in der Quelle vorhanden sind.
Mittels “–owner=…” und “–group=…” setze ich den Besitzer und die Gruppe der neuen Dateien und Ordner auf dem Zielsystem, da diese nicht mit denen auf dem Quellsystem identisch sind.
Nun ist das Backup also eingerichtet. Es sichert täglich automatisch die Websites mitsamt der Datenbank und kopiert diese Dateien zur Sicherheit auch noch auf einen anderen Server.
Letzten Juni war ich mit meiner Freundin für einige Tage in Südfrankreich, in der Provence.
Wir machten einige Wanderungen, unter anderem eine von der Abbaye Notre-Dame de Sénanque nach Gordes und dann auf einem anderen Weg wieder zurück zur Abtei.
Ich habe die Strecke mit meinem HTC Hero S mittels Endomondo aufgezeichnet (am Anfang scheint es falsche Daten aufgezeichnet zu haben):
Distanz: 17107 m Maximale Höhe: 573 m Minimale Höhe: 258 m Gesamtanstieg: 1053 m Gesamtabstieg: -1063 m Durchschnittsgeschwindigkeit: 1.37 m/s Total Time: 05:18:29
Insgesamt waren wir etwas längers als 5 Stunden unterwegs und wanderten dabei ca. 16 Kilometer.
Wir hatten einen Wanderführer (DuMont Wanderführer Provence) was die Wegfindung erleichterte, allerdings auch nicht immer. An gewissen Stellen verliefen wir uns nur per Zufall nicht und ab ca. Kilometer 11 konnten wir den Weg gemäss Wanderführer nicht mehr ermitteln und orientierten uns an den Trampelpfaden und Wegweisern.
Los ging es auf dem Parkplatz der Abtei Notre-Dame de Sénanque, welche wir zuvor noch kurz besichtigten. Leider war gerade keine Blütezeit des Lavendels, diese hätte erst einige Wochen später angefangen und die Lavendelfelder hätten dann viel schöner ausgeschaut, aber auch so ist die Abtei schön anzuschauen.
Zuerst ging es nach Norden und der Weg wurde bereits ziemlich unwegsam (schmal und voll von Ginsterbüschen). Ein Wegweiser welcher im Wanderführer vermerkt war, bestätigte uns, dass wir noch auf dem richtigen Pfad waren.
Danach wurde das Gelände immer steineriger, bis schliesslich gar kein Weg mehr ersichtlich war. Wir wussten einfach, dass wir uns weiter hinauf bewegen mussten, bis wir in der Ferne ein grosses weisses Schild sehen würden, was wir dann auch taten.
Oben angekommen, wurde der Weg wieder breiter und flacher. Hier trafen wir auch das erste Mal wieder auf andere Menschen, welche ebenfalls wanderten, allerdings andere Wege einschlugen als wir.
Nach einer Weile ging der Weg dann flach bergab, führt durch einige kleinere Siedlungen mit schönen Häusern und auch teilweise für kurze Strecken über gut befestigte Strassen. Nach kurzer Zeit kamen wir dann an den Rand des Hochplateaus von dem wir eine gute Übersicht über einen Teil der Provence hatten.
Dann gingen wir weiter am Rand dieses Plateaus entlang und nach wenigen hundert Metern tauchten vor uns die ersten Häuser von Gordes auf, einem malerischen, an einem Hang gelegenen Ort. Bei Kilometer 7,5 hatten wir einen guten Blick auf die Ortschaft, man sieht schön die Häuser, Villas und Hotels, welche sich auf und am Hang befinden, wie nebenstehendes Bild zeigt.
Nach Gordes liefen wir in Richtung der “Village des Bories“, einem Freilichtmuseum mit Uralten Steinhäusern, welches wir dann auch noch kurz besichtigten (KM 9,4), bevor wir die Wanderung fortsetzten.
Auf dem weiteren Weg stiessen wir auf ein altes Autowrack, welches vermutlich vor Jahrzehnten hier illegal entsorgt wurde. An der Tür hatte es auch einige Löcher.. aber bestimmt keine Einschusslöcher und wenn, dann wahrscheinlich nur, weil das Auto später für Zielübungen verwendet wurde – ganz bestimmt.
Wie breits erwähnt verliessen wir ab Kilometer 11 den Weg gemäss unserem Wanderführer und versuchten uns selbst zu orientieren. Sollte das gut gehen?
Bei KM 13,8 verliefen wir uns dann für ein kurzes Stückchen, kehrten aber noch rechtzeitig um, da es langsam Abend wurde und wir uns in einer unwegsam und verlassenen Schlucht befanden. Wäre nicht so toll gewesen dort zu übernachten
An gewissen Stellen in dieser Schlucht mussten wir sogar einige Meter an einer Felswand hochklettern. Diese war zum Glück nicht senkrecht. Auch sonst wurde der Weg immer enger, verwachsener und abenteuerlicher.
Schlussendlich endete die Schlucht dann doch noch irgendwann und wir konnten sie bei KM 15,7 wieder verlassen und befanden uns nun auf der Rückseite der Abtei.
Alles in allem eine tolle Wanderung, welche teilweise über freies Gelände ohne Wege führt und auch manchmal nicht gut mit Wegweisern markiert ist, aber mit ein bisschen studieren und ausprobieren sollte man das Ziel finden Ein Wanderführer oder eine GPS-Route erleichert die Wegfindung.
Auf WordPress verwendete ich zuerst das “Embed RSS“-Plugin um Feeds in die Seite zu integrieren, jedoch kam dies nicht mit diesem ungültigen Feed klar. Einige RSS-Reader-Programme (z.B. “Vienna RSS“) hatten jedoch keine Probleme mit dem Feed. Vermutlich korrigierten sie intern die Fehler oder ignorierten diese einfach.
Zum Glück hat mich einmal ein guter Freund in die Welt von Python eingeführt und mir die nötigen Basics dieser tollen Programmiersprache beigebracht.
Mit Hilfe der Bibliotheken “feedparser” und “PyRSS2Gen” konnte ich ein Python-Skript schreiben um dieses Problem umgehen.
Zuerst hole ich mit feedparser den Feed ab und baue später mit PyRSS2Gen einen neuen Feed im “RSS 2.0″-Format neu auf. Abschliessend schreibe ich diesen neuen Feed als XML-Datei in das Verzeichnis des Webservers.
Das Skript habe ich so umgebaut, dass eine beliebige iStockphoto-Benutzer-ID als Parameter übergeben werden kann. Auch die Ziel-Datei kann als Paramter übergeben werden.
Hier das Skript, welches in einem Repository auf Github gehosted ist:
Genau genommen validiert der neue Feed immer noch nicht allerdings sind die Fehler nun scheinbar weniger tragisch und er kann nun von “Embed RSS” gelesen werden. Auch verwende ich nun zur Einbettung in WordPress “HungryFEED” anstelle von “Embed RSS”, da dieses etwas mächtiger ist und bessere Templating-Option mitbringt.
Damit ich für die Webseite immer einen relativ aktuellen Feed erhalte, lasse ich das Skript mittels Crontab alle vier Stunden laufen:
#m h dom mon dow command
0 */4 * * * python /usr/local/bin/scriptXy.py -i 8197370 -t /dir/subdir/targetfile.xml
Ich habe meine “Fotografie“-Seite nun überarbeitet und mit der Hintergrund-Geschichte zu meinem Hobby versehen. Ausserdem wird auch meine aktuelle Foto-Ausrüstung augeführt und etwas über Stock-Fotografie berichtet.
Ich habe beschlossen meine alte “qoo.li”-Seite aufzugeben und dafür den Namen für meinen Blog/meine persönliche Webseite zu verwenden.
Die Inhalte der beiden Seiten, also des alten Blogs (shihan-online.ch) und der alten Qooli-Seite werden hier vereint in neuer, aufgefrischter Form zu finden sein.
Gleichzeitig habe ich dem Blog nun ein neues Aussehen verpasst. Ich hoffe es gefällt euch.
Für diejenigen, welche technische Details interessieren, ein Hinweis bezüglich der Domänen-Umleitung:
Damit die alten Inhalte, welche via Suchmaschinen schon indexiert wurden, auch weiterhin über die alte URL gefunden und aufgerufen werden können, habe ich mit “htaccess” eine Rewrite-Rule eingerichtet. Diese sieht wie folgt aus:
Zeilen 5 und 6 sind neu und sorgen dafür, dass alle Aufrufe, welche nicht über “qoo.li” eingehen auf eben diese neue Adresse umgebogen werden, wobei der restliche Pfad des aufrufes erhalten bleibt. “301″ sorgt dafür, dass die Weiterleitung als permanent gekennzeichnet wird. Suchmaschinen wissen somit, dass sie ihren Index entsprechend anpassen sollten.
Ein paar Kollegen und ich machen ab und zu ein Texas Jailhouse Chili, ein doch eher fleischlastiges und scharfes Gericht, dass aus Texas stammt und ursprünglich dort in Gefängnissen serviert sein soll. Die lieblichen Zutaten, sollten wohl die Gefangenen besänftigen?
Wir verwenden dazu jeweils das folgende Rezept, welches wir auf Chefkoch.de gefunden haben (für 12 Portionen):
1500 g
Rindfleisch, 2 cm Würfel
500 g
Schweinefleisch, gehacktes
500 g
Wurst (Chorizo), in Scheiben
4
Zwiebel(n), grob gehackt
3 Zehe/n
Knoblauch, grob gehackt
6
Chilischote(n), mehr oder weniger, je nach Sorte
250 g
Tomatenmark
500 g
Tomate(n), Dose, gewürfelt
3 TL
Kreuzkümmel, gemahlen
1 TL
Estragon, gemahlen
1 EL
Zucker
1 EL
Salz
1 EL
Pfeffer, schwarz, gemahlen
2 EL
Oregano, gerebelt
3 EL
Chilipulver
3 EL
Petersilie, gehackt
1 EL
Worcestershiresauce
1 EL
Essig
150 g
Schokolade, zartbitter
3 Dose/n
Bier
4 Dose/n
Bohnen, rot oder Pinto
Mit 12 Portionen hat man, wir kochen das zu dritt, gleich noch was für die nächsten Tage auf Vorrat gekocht. Ausserdem schmeckt es aufgekocht meiner Meinung nach besser als direkt nach dem Zubereiten. Etwas weniger scharf und geschmacklich intensiver.
Die Angaben bezüglich Bier und Bohnen sind etwas schwammig. Gemeint sind vermutlich kleine Dosen (3,3 dl) und idealerweise Schwarzbier. Bohnen je nach belieben, werden ja nicht mit dem Fleisch gemischt und dienen hauptsächlich dazu die Schärfe wieder auf ein vernünftiges Mass zu herunterzubringen. Wir haben letztes Mal 3 Habaneros (mit Kernen) anstelle der Chilis verwendet. Das gibt schon eine ordentliche Schärfe
Damit sich auch die nächsten 1000 Generation an unseren Küchen-Verwüstungs-Küsten Kochkünsten ergötzen können, habe ich das auf auf Video festgehalten:
Wenn man eine SharePoint-Listenansicht mittels XSLT anpasst, so möchte man häufig auch relative Pfade verwenden.
Besonders wichtig, wenn man eine XSL-Datei für ein Feature deployed, welches in verschiedenen Webs aufgerufen wird und folglich keine absoluten Pfade sondern relative enthalten sollte.
Dies lässt sich innerhalb einer SharePoint-Seite mit der Verwendung des globalen XSLT-Parameters “ServerRelativeUrl” einfach lösen, wie nachfolgendes Beispiel (Auszug aus einer XSL-Datei) zeigt:
Man hätte natürlich auch z.B. “../Lists” oder “http://servername/…” voranstellen können, jedoch verliert man dann Flexibilität. Sobald der Servername geändert wird (man denke auch an “Alternate Access Mappings” und Extranet-Lösungen) oder das XSL auf einer Listen-Ansicht statt auf einer Page verwendet wird, geht der Link nicht mehr, da der Verweis dann einen ungültigen Pfad hat.
Damit der globale Parameter verfügbar ist, muss innerhalb der XSL-Datei die “Main.xsl”-Datei von SharePoint referenziert werden:
Ja, was ist Kunst? Das haben sich schon viele im Leben gefragt. Liegt natürlich wie so vieles im Auge des Betrachters. Denoch gibt es sicherlich ein allgemein gültiges Empfinden, was generell als Kunst anerkannt wird. Zum Beispiel Gemälde, die meisten Fotografien, welche nicht nur Schnappschüsse (Point and Shoot) sind, Literatur (ausgenommen Sachliteratur) oder Musik. Kunst darf auch durchaus aus kommerziellen Interessen entstanden sein und ich bin mir sicher, dass dies, zumindest heutzutags, sogar der Normalfall ist.
Teilweise sind die Grenzen natürlich verschwommen. Ist jede Musik Kunst, ist jeder Film Kunst? Ist Musik, die man in 5 Minuten in “Garage Band” zusammenklickt, ohne eigentliche Motivation und Lust an der Sache Kunst? Wohl kaum. Es ist ein dahingeklicktes gefrickel, dass jede Katze, welche zufälligerweise über eine Tastatur läuft, auch bewerkstelligen könnte. Das wäre dann zumindest Kunst, wenn es mit diesem Konzept so geplant wurde, ansonsten ist es Zufall.
Beim Theater scheiden sich ja auch häufig die Geister. Sind gewisse Vorführungen wirklich Kunst, hat der Autor nur einfach zu viele Drogen und Zeit zur verfügung oder dient ein Stück lediglich dazu, dass einige Schauspieler auch mal wieder etwas verdienen können, damit sie nicht verhungern müssen?
Ich muss zugeben, ich kenn den Kontext zu folgendem Theaterstück nicht, kann mir aber kaum vorstellen, dass dies eine vernünftige Person als Kunst betrachten kann. Vielleicht als lächerliche Kunst, die nichts mit einem schönen Gemälde oder einer stilvollen Architektur zu tun hat. Eines muss man diesem Stück aber sicher lassen, es ist sehr (unfreiwillig?) komisch.