Serversynchronisation

Wir nutzen zwei Server um eine Vielfalt an Diensten zu nutzen und anzubieten. Die Dienste - Web, Mail, Subversion, WebDav, etc. - haben wir nicht zeitgleich auf beiden Servern laufen. So haben wir Mail beispielsweise auf foo und unsere „privaten“ Webseiten auf bar. Aber unsere Tracs haben wir wieder auf foo und so weiter.

Der Grund fuer zwei Server ist nicht in erster Linie die Verteilung von Last, sondern wir wollen sowas wie erhoete Verfuegbarkeit im Fehlerfall erreichen. Natuerlich muessen wir bei Ausfall einer Maschine noch etwas tun (Namensaufloesung, Dienste hochfahren), aber damit sind wir in der Regel schneller, als einen Server neu aufzusetzen. Denn dafuer muss man auch erst mal die Zeit haben.

Bei der Frage „Wo liegen welche Daten“ orientieren wir uns an dem sogenannten Filesystem Hierarchy Standard, den Dirk schon mal beschrieben hatte. So liegen also alle unsere, nicht fuer den Betrieb der Server benoetigten, Webseiten unterhalb von /srv/www, Mails haben wir in /var/vmail und so weiter. Die genutzte Struktur erlaubt es uns relativ unkompliziert und schnell eine Synchronisation der Daten zwischen beiden Systemen zu etablieren. So synchronisieren wir alle Webseiten alle Stunde von bar nach foo via /usr/bin/rsync -az –delete -b –backup-dir=/srv/bak/rsync/www -e „ssh -c arcfour“ /srv/www/ www-data@foo:/srv/www/. Mit –delete -b –backup-dir=/srv/bak/rsync/www stellen wir sicher, dass am Ursprungsort geloeschte Dateien auch im Ziel geloescht werden, aber auch eine Sicherung der Datei angelegt wird. Man weiss ja nie :)

Mail synchronisieren wir ebenfalls alle Stunde, mit im Grunde gleichem Aufruf wie fuer Webseiten. Etwas herausfordernder war die Synchronisation der MySQL-Datenbanken. Auf foo haben wir fuenf Datenbanken und auf bar sind es 21. Vorgabe fuer das zu erstellende Script war unter anderem, dass es ohne Anpassung, sowohl auf foo, als auch auf bar laueffaehig ist. Rausgekommen ist nachfolgendes.

#!/bin/bash
 
logger -t syncdbs.bash -i "Starting sync"
 
TIMESTAMP=$(date "+%Y%m%d")
SOURCE_DIR="/srv/bak/backup-md2"
DESTINATION_DIR="/srv/bak/sync"
DB_PREFIX="db."
DB_USERNAME=$(awk '/user/ {print $3}' /etc/mysql/debian.cnf | uniq)
DB_PASSWORD=$(awk '/password/ {print $3}' /etc/mysql/debian.cnf | uniq)
 
if [ "`hostname`" = "foo" ];then
	DB_TO_COPY="dbbar_01 dbbar_02 dbbar_03 dbbar_04 dbbar_05 dbbar_06 dbbar_07 dbbar_08\
	dbbar_09 dbbar_10 dbbar_11 dbbar_12 dbbar_13 dbbar_14 dbbar_15 dbbar_16 dbbar_17\
	dbbar_18 dbbar_19 dbbar_20 dbbar_21"
	REMOTE_SRV="bar"
elif [ "`hostname`" = "bar" ];then
	DB_TO_COPY="dbfoo_01 dbfoo_02 dbfoo_03 dbfoo_04 dbfoo_05"
	REMOTE_SRV="foo"
else
	logger -t syncdbs.bash -i "Aborting syncdbs. no proper hostname found. should be foo or bar."
	exit 9
fi
 
cd $DESTINATION_DIR
for i in $DB_TO_COPY
do
	mysqlshow -u $DB_USERNAME -p$DB_PASSWORD | awk '{print $2}' | grep ^$i$
	if [ $? -eq 0 ]
	then
		FILENAME=$DB_PREFIX$i-$TIMESTAMP*.sq*
 
		# retrieve file from server
		logger -t syncdbs.bash -i "retrieving $FILENAME"
		scp -q filecopy@$REMOTE_SRV:$SOURCE_DIR/$FILENAME $DESTINATION_DIR/
 
		# extract file
		bzip2 -d $FILENAME
 
		# import database
		logger -t syncdbs.bash -i "importing $FILENAME"
		mysql -u $DB_USERNAME -p$DB_PASSWORD $i < $FILENAME
		rm $FILENAME
	else
		logger -t syncdbs.bash -i "error: no database $i. skipping $FILENAME"
	fi
done
logger -t syncdbs.bash -i "Ending sync"

Basis fuer die Synchronisation ist ein entsprechendes Backup (mysqldump) auf der „gegenueberliegenden“ Seite. Das Script ist, wie man sehen kann, kein Voodoo. Aufwand entsteht, da die Variablen DB_TO_COPY gepflegt werden muessen. Die Anfangsidee „Les' doch einfach aus welche Datenbanken auf der anderen Seite sind“ war prima. Das haette man schnell mit einem ssh user@foo mysqlshow -u user -p hinbekommen. Das klappt aber nicht dauerhaft. Laueft der Job auf beiden Servern einmal, habe ich auf beiden Servern alle Datenbanken. Lasse ich den Job dann 12 Stunden spaeter auf foo noch mal laufen, wuerden alle Datenbanken von bar importiert und damit natuerlich auch die fuenf benoetigten Datenbanken auf foo mit einem alten Stand ueberschrieben.

Man koennte, um sich etwas mehr Automatismus zu erhalten, die Datenbanken als bak_* importieren und dann im Fehlerfall umbenennen. Die Idee hat fuer mich aber nicht so viel Charme, da ich beim Umschalten so wenig wie moeglich anfassen moechte.

Nicht zu vergessen ist natuerlich, dass es nur mit der Synchronisation von ein paar Daten und Datenbanken nicht getan ist. Im Ernstfall muss auch sichergestellt sein, dass die genutzten Anwendungen (Maildaemon, Webdaemon, etc.) auch auf der Backupseite verfuegbar sind. Und dann muss man natuerlich auch schauen, dass die Anwendung entsprechend konfiguriert ist. Einfach alle Webseiten kopieren und dann /etc/apache2/sites-enable zu vergessen ist ungeschickt.

Auch sollte nicht vergessen werden, dass die Rechte auf dem Backup gesetzt sind, bzw. noch zu setzen sind. So ist ein Import aller Datenbanken schon mal ein erster Schritt. Aber so lange die Anwendungen keine Rechte auf „ihre“ Datenbank haben bin ich noch nicht am Ziel.

Ja, machnmal waere es leichter einfach irgendwo einen „einfachen Server“ zu mieten. Aber wo bleibt da der Spass?

Zurück zur Uebersicht