Registrierung
leer
leer
newposts
Users
search
FAQ
Login
Start

Hallo Gast, und Willkommen im Forum. Sie müssen sich einloggen oder registrieren, um alle Funktionen nutzen zu können.


PN's Forum \ Computer \ Software \ Betriebssysteme \ Linux \ Überwachung einer Master-Master Replikation von MySQL unter Debian


 Poison Nuke  *

#1 Verfasst am 12.09.2010, um 17:37:20



Hallo,

hier im Thread ist beschrieben, wie man eine Master-Master Replikation mit MySQL konfiguriert:
http://forum.ultimateit.de/index.php?action=ViewThread&TID=3559


hier möchte ich jetzt auf die automatische Überwachung von dieser eingehen, damit im Notfall die Server selbstständig agieren und Datenverlust verhindern, da man unter Umständen nicht zeitnah so ein Problem erkennt, trotz Alarmierungsemail (weil man gerade unterwegs ist oder halt einfach gerade schläft z.B.)


Problem bei einer Master-Master Replikation: fällt diese aus, die Webserver selbst laufen aber beide weiter, dann gibt es eine sogenannte "Split-Brain" Situation. Beide Server agieren selbstständig und aus einem Forum werden zwei verschiedenen Foren, bei denen man zudem nichtmal von außen erkennt, auf welchem man gerade ist. Erkennen tut man das von außen daran, dass man z.B. ein Posting verfasst hat, das Forum schließt und wenn man es wieder öffnet, ist das Posting nicht mehr vorhanden, obwohl es auch keiner gelöscht hat. Oder man bekommt eine Benachrichtigung über eine neue private Nachricht, kann aber keine bei sich finden, da diese PM nur auf dem zweiten, nicht aber dem ersten Server ist.


Sollte es mal zu so einem Ausfall der Replikation kommen, muss also von einem von beidem Servern der Webserver gestoppt werden. Welcher von beiden Servern ist egal, wichtig ist nur, dass nur noch einer von beiden Servern von außen ansprechbar ist. Durch DNS-Round Robin schwenken die Clients automatisch auf den verbleibenden Server um und von den Usern merkt dies keiner.


Weiterhin muss auch überwacht werden, ob trotz laufender Replikation beide Datenbestände identisch sind. Es kann unter Umständen passieren, das fast zur gleichen Zeit auf beiden Servern ein Update Befehl ausgeführt wird, welcher eine komplette Tabelle beeinflusst. Im Extremfall kann es dazu kommen, dass beide Server die Updates in verdrehter Reihenfolge ausführen, was zu inkonsistenten Daten führt. Einfaches Beispiel: die Zähler für "ungelesene Beiträge" sollen auf dem einen Server für alle User um eins vergrößert werden, z.B. weil eine Ankündigung geschrieben wurde. Durch eine andere Maßnahme muss aber zeitgleich auf dem anderen Server die Anzahl auf Null gesetzt werden.

für einen User z.B.:

echo $counterUnreadPostings
-> 2


Server 1:
$counterUnreadPostings = 0
$counterUnreadPostings += 1
echo $counterUnreadPostings
-> 1

Server 2:
$counterUnreadPostings += 1
$counterUnreadPostings = 0
echo $counterUnreadPostings
-> 0


dieser Fall ist zum Glück sehr selten, das wirklich zeitgleich größere Updates durchgeführt werden, aber es kann passieren.




Das Problem bei der Überwachung der Replikation ist nun: wenn man automatisiert den Webserver stoppen will: wie verhindert man, dass beide Webserver sich einfach beenden? Denn auch wenn die Replikation gestoppt ist, wäre es unpraktisch die Seite komplett offline zu nehmen, weil dann bringt mir auch die Ausfallsicherheit durch die zwei Server nichts mehr :D



ich habe mich daher jetzt an einem kleinen Script versucht, dass die Probleme lösen soll, auch wenn es noch Optimierungspotential hat 8)





Was ist alles nötig für die Überwachung?
- Autologin für root in MySQL
- ein aufrufbares PHP Script auf den Webservern
- das eigentliche Script zum prüfen
- zwei kleine Dateien zur Unterstützung



Autologin in MySQL
normalerweise muss man sich in MySQL für den interaktiven Modus mit Passwort anmelden. Es ist aber auch möglich, einen Nicht-Interaktiven Modus zu nutzen für einzelne Kommandos, trotzdem braucht man auch hier normalerweise das Password. Da auf diese Weise eine Automatisierung nicht möglich ist, muss zuerst der Autologin aktiviert werden:
das Script MUSS als root ausgeführt werden. Daher folgende Datei als root erstellen und folgenden Inhalt reinschreiben:


Quellcode
cd ~
pwd #(sollte nun /root ergeben)
(
echo [client]
echo host=localhost
echo user=MySQLuser
echo password=pw
echo database=db
) > .my.cnf
chmod 400 .my.cnf




PHP Script zum prüfen der Checksummen von MySQL
der Server benötigt eine vhost Konfiguration, bei der ein bestimmtes Verzeichnis über sämtliche IPs des Servers erreichbar ist (localhost, interne IPs, externe IPs)
die Checksummenprüfung muss als PHP Script ausgeführt werden, damit die Server die Checksummen des anderen Servers bekommen können umd damit einen Vergleich zu den lokalen zu erstellen

bei mir heißt das Script "crc32.php" und ist mittels .htaccess und PHP auf die IPs gebunden, die es benötigen, es kann also niemand von außen überhaupt irgendwie ran.
Folgendes steht im Script drin:


Quellcode
<?php

//Umgebungsvariablen

//HauptIPs der beiden Server, Uber die sie aufeinander zugreifen. Da dieses Script durch DRDB/rsync auf beiden Server immer identisch ist, müssen beide IPs erlaubt werden
$raddr1='x.x.x.x';
$raddr2='x.x.x.y';

//MySQL Zugangsdaten fuer den lokalen Server, es sollte der root zugriff sein:
$muser='MySQLUser';
$mpass='pw';

//eine der Datenbanken auswaehlen, daraus 3 der Tabellen zur Ueberwachung. Diese sollten gross sein, aber sich nicht zu oft aendern, sonst gibt es unter Umstaenden ab und an fehlermeldungen
$mdb='datenbank';
$tb1='erstetabelle';
$tb2='zweitetabelle';
$tb3='drittetabelle';



if ($_SERVER['REMOTE_ADDR']!='127.0.0.1' AND $_SERVER['REMOTE_ADDR']!=$raddr1 AND $_SERVER['REMOTE_ADDR']!=$raddr2) die();
if (!$sql_link=@mysql_connect("localhost",$muser,$mpass)) die('MySQL down' );
if(!@mysql_select_db($mdb,$sql_link)) die('Datenbankproblem' );

$query=mysql_query("CHECKSUM TABLE ".$tb1." EXTENDED" );
$crc=mysql_fetch_assoc($query);
$tbl1=$crc['Checksum'];

$query=mysql_query("CHECKSUM TABLE ".$tb2." EXTENDED" );
$crc=mysql_fetch_assoc($query);
$tbl2=$crc['Checksum'];

$query=mysql_query("CHECKSUM TABLE ".$tb3." EXTENDED" );
$crc=mysql_fetch_assoc($query);
$tbl3=$crc['Checksum'];




echo "checksum=".($tbl1+$tbl2+$tb3)."\
";
?>



Dieses Script hat je nach Tabellengröße eine relativ hohe Ausführungszeit. Bei einer Datenbankgröße von ~100MB und einer CPU mit 3GHz pro Core kommt man auf ca. 1-2 Sekunden.
Es ist daher sehr schlecht, wenn das Script für andere erreichbar wäre, da sonst der Server gut überlastet werden könnte.



anlegen der zwei benötigten Dateien

Quellcode
cd /root
touch replicationstatus
(
echo Hallo,
echo 
echo dies ist das Ueberwachungscript von "master.poisonnuke.de"
echo Im Betreff wurde das entsprechende Problem genannt.
echo
echo eine umgehende Pruefung ist erforderlich, um Datenverlust zu vermeiden.
) > emailnotification.txt


den Inhalt der Email kann jeder so anpassen wie er mag, es geht nur darum, dass ein Text vorhanden ist und man halt ungefähr weiß, worum es geht.


das eigentliche Prüfungsscript

/root/slavecheck.sh
der Quellcode ist kommentiert, sollte etwas unklar sein, dann bitte nachfragen.

Quellcode
#!/bin/bash
#Poison Nukes Pruefscript fuer Master-Master Replikation
#last updated: 15.09.2010


#geben sie hier die IP des zweiten Servers ein. Bei vorhandener interner Verbindung die internen Adressen
#testIP sollte eine immer verfügbare IP im gleichen Rechenzentrum sein, zum Prüfen ob der Server selbst online ist. Alternativ einen DNS Anycast Cluster.
server2="10.0.0.32"
testIP="x.x.x.x"
hostname="master.domain.com"
emailaddy="myemail@address.com"




#es wird nun der Status der lokalen Replikation geprueft, d.h. ob dieser Server noch Updates vom dem anderen bekommt
#dazu muss der automatische lokale root-Mysql Login aktiv sein (/root/.my.cnf)
#es werden drei Statusausgaben von SHOW SLAVE STATUS geprueft, welche zeilenweise ausgegeben werden

slaveIO=`mysql -e 'show slave status \\G' | grep Slave_IO_Running`
lastErr=`mysql -e 'show slave status \\G' | grep Last_Errno`
slaveSQ=`mysql -e 'show slave status \\G' | grep Slave_SQL_Running`

pos=`expr index "$slaveIO" :`
len=`expr length "$slaveIO"`
slaveIO=${slaveIO:$pos+1:$len-$pos-1}

pos=`expr index "$lastErr" :`
len=`expr length "$lastErr"`
lastErr=${lastErr:$pos+1:$len-$pos-1}

pos=`expr index "$slaveSQ" :`
len=`expr length "$slaveSQ"`
slaveSQ=${slaveSQ:$pos+1:$len-$pos-1}


if [[ "$slaveIO" == "Yes" && "$slaveSQ" == "Yes" && "$lastErr" == "0" ]]
then 
	slavestatus="good"
else 
	slavestatus="bad"
fi




#im per IP erreichbaren Hauptverzeichnis der Server sollte ein Ordner "check" mit der Datei "crc32.php" liegen, welche 
#eine checksumme von drei Tabellen erzeugt und ausgibt. Hier werden die CHecksummen von beiden Server geprueft, um 
#die Konsistenz der Replikation zu pruefen.
#HINWEIS: die checksumbildung dauert ein paar Sekunden je nach Datenbankgroesse. Wenn in der Zeit bereits ein Update
#auf dem zweiten Server ausgefuehrt wurde, dann ergibt sich automatisch eine Inkonsistenz.
#daher wird in /home eine Datei gespeichert "Replicationstatus"
#in dieser wird bei jedem Check das Ergebnis der Pruefung gespeichert. Erst beim zweimaligen Auftreten wird ein Alarm ausgeloest.


wget -q -O - http://127.0.0.1:8080/crc32.php > /tmp/checksum & checksum2=`wget -q -O - http://$server2:8080/crc32.php`; checksum=`cat /tmp/checksum`
pos=`expr index "$checksum" =`
len=`expr length "$checksum"`
crc1=${checksum:$pos:$len-$pos-2}

pos=`expr index "$checksum2" =`
len=`expr length "$checksum2"`
if [ $len = 0 ]
then
	#der Server war nicht erreichbar oder die Datei wurde nicht gefunden,
	#es ist davon auszugehen, dass der Server nicht online ist
	server2status="offline"
else
	crc2=${checksum2:$pos:$len-$pos-2}
	server2status="online"
fi



if [[ "$crc1" == "$crc2" ]]
then 
	replication="consistent"
else
	replication="inconsistent"
fi

replicationold=`cat /home/replicationstatus`
echo $replication > /home/replicationstatus



#teste ob der Server selbst online ist
pong=`ping -c2 $testIP | grep -o [0-9].received`
count=${pong:0:1}
if [ "$count" == "2" ] 
then
	server="online"
else
	server="offline"
fi



#ist Apache am laufen?
apache=`ps aux | grep -c apache`
if [[ $apache -gt 2 ]]
then
	apache="running"
else 
	apache="stopped"
fi




#RAID Status pruefen
#3ware RAID mit tw_cli
#ctrl=`tw_cli info | grep -o c[0-9]`
#Rstatus1=`tw_cli /$ctrl show | grep ^u[0-9] | grep -c DEGRADED`
#Rstatus2=`tw_cli /$ctrl show | grep ^u[0-9] | grep -c INOPERABLE`
#if [[ $Rstatus1 == 0 && $Rstatus2 == 0 ]]
#then 
#	RAID="good"
#else 
#	RAID="bad"
#fi

#SoftwareRAID
Faultcount=`cat /proc/mdstat | grep -c _`
if [[ $Faultcount == 0 ]]
then
	RAID="good"
else
	RAID="bad"
fi



#DRBD pruefen
Faultcount=`cat /proc/drbd | grep -c Unknown`
if [ $Faultcount -gt 0 ]
then
	DRBD="bad"
else
	DRBD="good"
fi


#pruefe nun alle Bedingungen und fuehre notwendige Massnahmen aus
if [[ "$slavestatus" == "bad" && "$server" == "offline" && "$apache" == "running" ]]
then
        echo "Server offline, replikation lauft nicht, apache muss angehalten werden"
        /etc/init.d/apache2 stop
        mysql -e 'show slave status \G' > /root/emailnotification.txt
        mail -s "Replikation geht nicht auf $hostname" $emailaddy < /root/emailnotification.txt
fi

if [[ "$slavestatus" == "bad" && "$server2status" == "online" && "$apache" == "running" ]]
then
        echo "replikation lauft nicht anderer Server online, apache muss angehalten werden"
        /etc/init.d/apache2 stop
        mysql -e 'show slave status \G' > /root/emailnotification.txt
        mail -s "Replikation geht nicht auf $hostname" $emailaddy < /root/emailnotification.txt
fi


if [[ "$slavestatus" == "good" && "$server" == "online" && "$apache" == "stopped" ]]
then
        echo "alles ok, apache starten" > /root/emailnotification.txt
        /etc/init.d/apache2 start
        mail -s "apache auf $hostname gestartet" $emailaddy < /root/emailnotification.txt

fi

if [[ "$replication" == "inconsistent" && "$replicationold" == "inconsistent" ]]
then
        echo "Inkonsistenz!"
        echo "Master CRC:"$crc1", Slave CRC:"$crc2 > /root/emailnotification.txt
        mail -s "Replikation nicht konsistent auf $hostname" $emailaddy < /root/emailnotification.txt
fi

if [[ "$RAID" == "bad" ]]
then
        echo "RAID Problem"
        cat /proc/mdstat > /root/emailnotification.txt
        mail -s "RAID Problem auf $hostname" $emailaddy < /root/emailnotification.txt
fi

if [[ "$DRBD" == "bad" ]]
then
        echo "DRBD Problem"
        cat /proc/drbd > /root/emailnotification.txt
        mail -s "DRBD Problem auf $hostname !!" $emailaddy < /root/emailnotification.txt
fi


exit 0



und natürlich ein

Quellcode
chmod 700 slavecheck.sh

nicht vergessen.



jetzt als erstes mit

Quellcode
./slavecheck.sh

das ganze testen, ob es auch funktioniert. Wenn keine Fehlermeldung kommt, dann ist alles in Ordnung.


Wenn dem so ist, dann das ganze in der Crontab von root eintragen. Wie oft die Ausführungszeit ist, kann jeder für sich entscheiden.
Einmal pro Minute könnte bei großen Datenbanken schon merkbar die Performance beeinflussen. Ich persönlich führe es alle 15min aus. Sollte es genau in der Zeit zu einem Split-Brain kommen, dann sind ca. 50% der Beiträge aus dieser Zeit weg. Da so eine Situation normalerweise nur selten vorkommt, ist das Risiko für Datenverlust an sich sehr klein.


greetz
Poison Nuke

bearbeitet von Poison Nuke, am 22.06.2012, um: 14:15:13


 Poison Nuke  *

#2 Verfasst am 15.09.2010, um 11:53:11



Update des Slavecheck-scripts, zusätzliche Überwachung von DRBD Mountpoints und softwareRAIDs


greetz
Poison Nuke

 Poison Nuke  *

#3 Verfasst am 23.10.2010, um 10:03:26



eigenartiges Problem:

wenn die Partition, auf der /var/lib liegt, voll ist, z.B. wegen zu großer Log-Files, die durch Log-Rotate nicht automatisch gelöscht wurden, oder anderen Sachen, dann läuft mysql zwar noch auf dem Server, aber die Replikation stoppt, sprich man kann noch Daten abrufen und damit scheint es von außen noch so, als würde es laufen, aber es kann keine Updates mehr in die binlog schreiben, wodurch der Slave denkt, dass er selbst ein Problem hat und schaltet sich beim obigen Script ab...blöderweise ist damit dann alles offline, weil der Master keine Anfragen mehr verarbeiten kann (Da er keine Daten schreiben kann, und auch wenn man nur eine Seite öffnet, wird zeitgleich auch immer etwas in MySQL geschrieben.


hab noch keine Idee wie ich das Problem verhindern kann mit dem Script :Y

wenn einer von euch vllt eine gute Idee hat? :angel


greetz
Poison Nuke

EPIC

#4 Verfasst am 08.02.2011, um 23:41:01



Logs eventuell in eigenständige Partition schreiben lassen?



 Poison Nuke  *

#5 Verfasst am 08.02.2011, um 23:50:15



es gibt eine Vielzahl von Möglichkeiten, durch die eine Partition voll laufen kann. Man könnte 100 Partitionen erstellen und am Ende ist es trotzdem nicht besser. Ein sauberes Logrotate funktioniert da schon einwandfrei. Im schlimmsten Fall dreht MySQL bzw ein Script durch und bläht die Datenbank um zig GB auf bis die Partition voll ist, alles schon erlebt :Y



PS:
hatte vorhin erst festgestellt dass aus irgendeinem Grund hier weit mehr als die Hälfte von dem Eingangsposting einfach fehlte 8o
habe eben ein paar Stunden lang meine alten Backups durchwühlt eh ich eins gefunden hatte wo der komplette Beitrag drin war :cut


greetz
Poison Nuke

bearbeitet von Poison Nuke, am 09.02.2011, um: 00:04:35


 Poison Nuke  *

#6 Verfasst am 22.06.2012, um 14:18:46



ich habe einige kleine Verbesserungen am Überwachungsscript vorgenommen. Jetzt wird auch die Fehlermeldung in der Email mitgeschickt damit man sofort weiß was genau das Problem ist.

Und bei der Überwachungs der Konsistenz der Replikation wurde das Script so ausgelegt das beide Server wirklich zur selben Zeit abgefragt werden und damit keine Unterschiede in der Checksumme auftreten können. Bei stärker frequentierten Servern passierte dies leider ab und an, das sich die Checksumme so schnell änderte dass die Abfrage im Script verschiedene Werte bekommen konnte


greetz
Poison Nuke

PN's Forum \ Computer \ Software \ Betriebssysteme \ Linux \ Überwachung einer Master-Master Replikation von MySQL unter Debian


- Zurück zur Homepage - Eigene Beiträge - Neue Beiträge - Wer ist online? - Impressum - Datenschutz - Statistiken -



Board coded and provided by: Poison Nuke
Copyright 2007-2014, Robert Menger