Man kann’s ja mal versuchen … die Zweite

07. Dezember 2009 von Jonas Pasche

Ist das zu fassen? Meine deutliche Antwort an den Versender der ersten Mail, dass ich wohl kaum einem anonymen Dritten gegenüber Informationen über meine Geschäftsbeziehungen geben werde, war wohl nicht genug. Heute geht es weiter:

Vorausgesetzt auch Sie gehören zum großen Kreis derer, die Ihren unbezahlten Rechnungen nachtrauern, vermutete ich Interesse, an einer minimalen unterstützenden Zusammenarbeit, die dann auch bezahlt würde.
Bitte entschuldigen Sie, wenn ich Unmut in Ihnen auslöste. Dies war nicht meine Absicht.
Da die Klage noch in Vorbereitung ist, wähle ich diesen Kontakt über einen Drittaccount. Bei den gesuchten Informationen handelt es sich um keine staatsanwaltlich relevanten. Es wird eine zivilrechtliche Klage im unteren 4-stelligen Bereich, die außerhalb des Insolvenzverfahrens gegen $FIRMA liegt.

Bitte entschuldigen Sie nochmals die Verwirrung.

Habe ich das richtig verstanden und mir wurde hier – wenig unverhohlen – Bezahlung für eine „unterstützende Zusammenarbeit“ angeboten (die dann, ausgehend von der ersten Mail, wohl in der Preisgabe vertraulicher Informationen bestehen sollte)?

Interessanterweise wurde diese zweite Mail im Gegensatz zur ersten nicht über einen der großen Internetprovider versendet, sondern aus einem kleinen IP-Netz einer Firma, die in einer gut zu $FIRMA passenden Branche arbeitet. Ich gehe also mal ganz mutig davon aus, dass ich hier sowieso nicht mit dem Anwalt eines Geschädigten kommuniziere, sondern mit dem Geschädigten selbst. Es ist natürlich möglich, dass er dennoch auch Anwalt ist, beispielsweise aus einer hauseigenen Rechtsabteilung, aber mein Gefühl sagt mir etwas anderes …

Ich habe nun die Geschäftsführung der Firma, aus deren IP-Netz diese zweite anonyme Mail stammt, dazu aufgefordert, derartige anonyme Belästigung aus ihrem Haus bitte zu unterbinden.

Man kann’s ja mal versuchen …

06. Dezember 2009 von Jonas Pasche

Eben frisch in meiner Mailbox eingetroffen:

Sehr geehrter Herr Pasche,

ich bin auf der Suche nach Informationen über die Firma $FIRMA. Ein Mandant von mir klagt gegen diese wegen ausstehender Zahlungen.

Da uns leider noch Beweismaterial fehlt, recherchiere ich nun im Umfeld der Firma.
Mir wurde mitgeteilt, dass Sie die Homepage betreuen.

Stimmt das und wie stehen Sie zu dieser Firma?

Vielen Dank für eine Antwort!

Die Unterschrift habe ich nicht etwa weggekürzt – da war einfach keine. Auch ist der Absender eine nicht mit Klarnamen versehene Freemailer-Adresse. Da Absender ja den Eindruck macht, Rechtsanwalt zu sein, wenn er einen Mandanten vertritt, für den er eine Klage führt, erscheint das hoffentlich nicht nur mir reichlich fragwürdig. Was lässt denn ausgerechnet einen Rechtsanwalt (mal angenommen, das sei er wirklich) annehmen, ich würde freimütig einer anonymen Person Auskünfte über irgendwelche Kundenbeziehungen geben? Eine etwas merkwürdige Vorstellung von Datenschutz – aber man kann’s ja mal versuchen. Nur eben nicht bei mir.

Update: Es gibt inzwischen noch eine zweite Mail.

Angeschossen? Abgeknallt!

14. November 2009 von Jonas Pasche

Für einen Kunden adminstrieren wir einen Server, den er sich selbst bei Anbieter S gemietet hat. S ist einer der Großen der Branche; einer von denen, die in die c’t immer bunte Prospekte einlegen lassen, also nicht gerade ein Wald-und-Wiesen-Provider.

Da der fragliche Server mit einem Software-RAID1 bereitgestellt wird, fragen wir per Nagios regelmäßig den mdadm-Status ab, um rechtzeitig bei Plattenproblemen informiert zu werden. Vor wenigen Tagen war es dann leider auch tatsächlich soweit: Das RAID1 ist degraded, eine Platte fehlt. Da wir auf die Hardware keinen Zugriff haben, vereinbarten wir mit dem Kunden, dass wir uns direkt mit S in Verbindung setzen, um die defekte Festplatte auswechseln zu lassen – im Rahmen unserer Wartungsverträge mit Kunden, die bei externen Providern hosten, ist das für uns selbstverständlich.

Der Anruf bei der Hotline von S verlief jedoch gänzlich anders als erwartet. Ich versuche es mal aus dem Gedächtnis wiederzugeben und bitte für leichte Anpassung in der Dramaturgie um Verständnis – inhaltlich hat das Telefonat wirklich so stattgefunden.

Hotline: „Anbieter S, Kundenservice, guten Tag!“

Ich so: „Guten Tag! Bei einem Server, den wir für einen Ihrer Kunden warten, ist eine Festplatte ausgefallen. Das Gerät läuft aber noch, da es ein RAID1 hat. Wie schnell können Sie denn da die Festplatte tauschen? Geht das per Hot-Swap im laufenden Betrieb, oder muss da eine kurze Downtime erfolgen?“

Er so: „Nein, die Festplatte wechseln wir nicht aus. Wir stellen Ihnen einen neuen Server bereit.“

Ich so: „Ääääh … aber es ist doch nur eine der beiden Festplatten kaputt. Der Server an sich läuft ja noch …“

Er so: „Das ist egal. Wenn es Hardwareprobleme gibt, bekommen Sie einen neuen Server.“

Ich so: „Wieso tauschen Sie denn nicht einfach die defekte Platte aus und dann ist alles wieder gut?“

Er so: „Nein, sowas machen wir grundsätzlich nicht. Sowas macht überhaupt kein Internetprovider. Höchstens ein kleines Systemhaus.“

Ich so: „Entschuldigung, aber da muss ich Ihnen widersprechen. Wir betreuen im Kundenauftrag auch noch Server bei H1, H2 und bei P, und bei allen gab es im Lauf der Jahre auch schon mal Festplattenschäden. Ich kann Ihnen versichern: Jeder dieser Anbieter [nebenbei allesamt in der Liga von S] hat anstandslos die defekte Festplatte ausgetauscht, und dann lief das RAID1 wieder.“

Er so: „Das kann ich mir nicht vorstellen. Vielleicht früher mal. Versuchen Sie das heute mal. Das wird Ihnen keiner machen.“

Ich so: „Selbst wenn Sie Recht hätten: Ist denn der Umstand, dass andere Anbieter schlechten Service bieten, für Sie Entschuldigung genug, um auch selbst schlechten Service zu bieten?“

Er so: „Ich verstehe Sie nicht. Sie bekommen doch einen ganz neuen Server. Das ist doch gut für Sie!“

Ich so: „Nehmen wir das mal so hin. Wie wäre denn dann der Ablauf; wie gehen wir strategisch am Besten vor?“

Er so: „Sie versetzen Ihren bisherigen Server in den Neuinstallationsmodus und geben uns dann Bescheid. Wir stellen Ihnen dann den neuen Server bereit.“

Ich so: „Und was ist mit den Daten des Servers?“

Er so: „Ich verstehe die Frage nicht.“ [Kein Scherz! Das hat er wirklich gesagt!]

Ich so: „Naja, wenn Sie einen neuen Server bereitstellen, dann ist der ja erstmal nur mit dem nackten Betriebssystem installiert. Wie kommen denn die Daten von dem alten Server auf den neuen? Oder kopieren Sie die gleich mit rüber?“

Er so: „Welche Daten? Wenn Sie den alten Server in den Neuinstallationsmodus versetzen, dann werden alle Daten von der Festeplatte gelöscht. Insofern sind da ja keine Daten mehr zum Übertragen auf den neuen Server.“

Ich so: „Ach so, dann stellen Sie uns den neuen Server doch einfach vorab bereit, wir kopieren die Daten rüber, und wenn alles drüben ist, können Sie den alten Server abschalten.“

Er so: „Nein, das geht nicht.“

Ich so: „Bitte? Wieso das denn nicht?“

Er so: „Der neue Server bekommt ja die gleiche IP-Adresse. Wissen Sie, es kann grundsätzlich nicht zwei Server mit der gleichen IP-Adresse geben, das geht einfach technisch nicht.“

Ich so: „Stellen Sie sich vor: Das weiß ich. Aber Sie können ihm ja einfach vorübergehend eine andere, temporäre IP geben, damit wir die Daten übertragen können, und danach wird jene IP wieder freigegeben.“

Er so: „Nein, das können wir nicht machen.“

Ich so: „Dann verraten Sie mir doch bitte mal, wie ich die Daten vom alten auf den neuen Server bekommen soll, wenn der neue Server noch nicht da ist, während der alte noch läuft, und wenn der alte dann weg ist, sobald der neue Server läuft!“

Er so: „Sie können ja den bereitgestellten Backup-Speicherplatz benutzen, der bleibt ja bestehen.“

Ich so: „Ich muss also ein paar Dutzend Gigabyte per FTP auf einen anderen Server kopieren, was Stunden dauern wird, dann muss ich Ihnen Bescheid geben, dass Sie einen neuen Server bereitstellen, was Stunden dauern wird, dann darf ich das Betriebssystem von Hand wieder zurechtfrickeln, was Stunden dauern wird, weil das System, das damals installiert wurde, heute gar nicht mehr zur Installation angeboten wird, und dann darf ich die gleichen paar Dutzend Gigabyte wiederum per FTP zurückkopieren, was nochmals Stunden dauern wird?“

Er so: „Ja, das ist das normale Vorgehen in diesem Fall. Dieses Vorgehen ist das Ergebnis unserer internen Prozessoptimierung und Qualitätssicherung.“ [sic!]

Ich so (erregt): „Haben Sie eine Vorstellung davon, wieviele Stunden an Downtime das bedeuten wird, um ein Problem zu beheben, das jeder Ihrer Konkurrenten innerhalb einer Downtime von höchstens zehn Minuten lösen könnte? Wie kann das denn bitte ein Ergebnis von Prozessoptimierung sein?“

Er so: „Beim Austausch von einzelnen defekten Festplatten gibt es einfach zuviele Probleme.“

Ich so: „Im Moment machen eher Sie mir Probleme und nicht die defekte Festplatte. Können Sie mir denn dann bitte sagen, wofür Sie überhaupt ein RAID1 in diesen Server bauen, wenn Sie dann bei dem Problem, für das ein RAID1 einen Workaround bietet, nämlich den Ausfall einer Festplatte zu kompensieren, dann gleich dem ganzen Gerät den Gnadenschuss verpassen? Dann hätten wir uns das RAID1 ja auch gleich sparen können.“

Er so: „Nein, denn dann wäre der Server sofort ausgefallen und Sie hätten unter sofortigem Handlungsdruck gestanden. So können Sie sich den Zeitpunkt frei aussuchen, wann Sie die Neueinrichtung machen wollen, also eben dann, wenn es Ihnen gelegen kommt.“

Ich so: „Das RAID1 ist nur dazu da, dass ich mir den Zeitpunkt der stundenlangen Downtime selbst aussuchen kann, wann immer es mir, ich zitiere: „gelegen“ kommt? Ist das ihr Ernst?“

Er so: „Ja, genau.“

Ich so: „Ihnen ist aber doch klar, dass das Internet 24 Stunden täglich geöffnet hat und eine mehrstündige Downtime immer ungelegen kommt? Auf dem Server läuft ein Onlinespiel, das rund um die Uhr aktiv genutzt wird!“

Er so: „Also, die meisten unserer Kunden finden es gut, einen ganz neuen Server zu bekommen.“

Ich so: „Ja und? Offensichtlich haben die meisten Kunden keine wirklich wichtigen Sachen auf den Servern bei Ihnen. Dass ein einzelner Server keine hochverfügbare Lösung ist, ist natürlich klar, aber wir haben uns absichtlich einen Server mit RAID1 ausgesucht, um der wahrscheinlichsten Ausfallursache eines Servers, nämlich einer defekten Festplatte, entgegentreten zu können. Und nun machen Sie dies völlig zunichte, in dem Sie ohne Not eine stundenlange Downtime provozieren, die für uns mit jeder Menge Arbeit verbunden ist, nicht zuletzt für den darauf folgenden zu erwartenden Support. Gibt es da wirklich keine andere Möglichkeit?“

Er so: „Nein, die gibt es nicht.“

Es hat unsererseits gar keiner Empfehlung für diesen Schritt bedurft – unser Kunde kündigt den bei S betriebenen Server  von sich aus. Und für uns bleibt es unterm Strich ein lehrreiches Beispiel dafür, wie wir uns von Providern abgrenzen, für die „Prozessoptimierung“ wichtiger ist, als die für den Kunden optimale Lösung zu finden – und dafür dann eben auch mal einen Finger mehr krummzumachen. In diesem Sinne sind wir stolz darauf, in den Augen von S „höchstens ein kleines Systemhaus“ zu sein.

Synology-Netzwerksicherung: Doppel-FAIL

13. November 2009 von Jonas Pasche

Synology baut nette kleine NAS-Boxen für Daheim oder fürs Büro. Die laufen mit Linux, haben ein Webinterface drauf, damit man nicht mit der Shell in Berührung kommen muss, und sind von daher auch bei Einsteigern sehr beliebt.

Nun gibt es hier und da Bedarf, den Datenbestand der Syno an einem externen Ort zu sichern, und wie das nun mal so ist, bietet sich da Speicherplatz im Internet an. Die Syno unterstützt praktischerweise laut Dokumentation Backups auf „rsync-kompatible Server“. Nun kenne ich zugegebenermaßen keine Serversoftware, die „rsync-kompatibel“ wäre außer eben rsync selbst, aber was soll’s, rsync ist ja prima und macht seinen Job.

Die Syno möchte allerdings, dass man rsync als Daemon betreibt, und man muss bei der Einrichtung des Backup-Profils ein Modul benennen, das man in der rsyncd.conf angelegt hat. Nun muss ich zugeben, dass ich rsync noch nie als eigenständigen Daemon habe laufen lassen, und zwar schon aus einem Grund: Die Verbindungen sind unverschlüsselt. Lediglich die Zugangsdaten werden rudimentär verschlüsselt; so rudimentär, dass selbst die man page sagt, das sei „fairly weak“. Die übertragenen Daten selbst werden überhaupt nicht verschlüsselt. Von daher taugt diese Variante maximal für lokale Netze, und für die Übertragung via Internet hat sich eingebürgert, rsync über SSH laufen zu lassen. Dabei läuft serverseitig dann gar kein rsync-Daemon, sondern es wird aus der SSH-Verbindung heraus ein Quasi-rsync-Daemon für diesen User gestartet, der dann auch nicht die /etc/rsync.conf benutzt, sondern die ~/rsync.conf – falls vorhanden. Mit dem Ende der SSH-Verbindung wird auch der Quasi-rsync-Daemon wieder beendet. Sehr aufgeräumt, und kommt ohne root-Rechte aus.

Ich hätte auf die Dokumentation achten sollen, die ganz klein in der Liste der benutzten Ports verrät, dass die verschlüsselte Netzwerksicherung nicht nur Port 22, sondern auch Port 873 – den Standardport für den unverschlüsselt laufenden rsync-Daemon – benötigt. Das erschien mir völlig unlogisch, aber es stimmt: Ohne einen rsync-Daemon geht es einfach nicht. Nun wollte ich natürlich vermeiden, dass über die unverschlüsselte Verbindung die Zugangsdaten verschickt werden, und habe mir deshalb mittels tcpdump angesehen, was die Syno da eigentlich macht. Ergebnis: Sie checkt mittels der list-Funktion von rsync, ob das ausgewählte Modul vorhanden ist. Ergo habe ich mir überlegt: Dann packe ich doch einfach die reale Konfiguration des Backup-Moduls in ~/rsync.conf, und in die /etc/rsync.conf packe ich ein Modul gleichen Namens (damit der Wizard befriedigt ist), aber mit /var/empty/rsyncd als Pfad, read-only, und vor allem: ohne irgendwelche Zugangsdaten anzufordern. Mit Erfolg: Die Dyno schaut, ob’s das Modul gibt, ist zufrieden, und macht dann alles weitere per SSH. Die Gegenprobe lässt mir einen Schauer über den Rücken laufen: Fordert man in der /etc/rsyncd.conf Zugangsdaten an, schickt die Syno sie bereitwillig rüber, über die unverschlüsselte Verbindung auf Port 873, obwohl man im Webinterface Verschlüsselung aktiviert hat. Sowas geht einfach gar nicht. Betrug am User. FAIL #1.

(Apropos: Im deutschsprachigen Webinterface der Syno heißt die fragliche Option „Verschlüsselung von Sicherungskopien aktivieren“. Hand aufs Herz, für mich klingt das weniger nach einer verschlüsselten Verbindung, sondern vielmehr danach, dass die Dateien verschlüsselt würden – was nebenbei eine äußerst gute Idee wäre, wenn man diese bei einem fremden Provider unterbringen will, dem man nicht seine privaten Dateien zur gefälligen Unterhaltung nach Feierabend bereitstellen will. Leider ist dem nicht so. Es geht in Wirklichkeit nämlich doch um die Verbindung, und die Daten liegen auf dem Zielserver dann für den Provider lesbar.)

Nun ist dieser Part aber gemeistert: Die /etc/rsyncd.conf verlangt einfach kein Passwort mehr, die Syno ist glücklich damit und fabriziert das Backup dann auch tatsächlich über SSH. Damit ich es nicht vergesse: In der ~/rsyncd.conf muss man beim Modul zusätzlich zum Pfad noch einstellen:

use chroot = false
read only = false

chroot funktioniert nämlich nur, wenn rsync als Standalone-Daemon als root läuft, und read only ist standardmäßig true, was für einen Backup-Speicherplatz natürlich Unsinn ist – auf den will man ja gerade etwas schreiben können. Das mit dem fehlenden chroot ist zu verschmerzen, da ja die ganz normalen Unix-Berechtigungen dafür sorgen, dass der User nicht woanders reinkommt; da die User ohnehin Shell-Zugang per SSH haben, reißen wir uns hier somit auch keine größere Sicherheitslücke auf.

Nun ist nach dem Backup natürlich noch zu testen, wie es so mit dem Zurückspielen klappt. Also mal einen Testordner angelegt, ein paar Dateien hinein, Sicherung angelegt, alles prima. Nun ein Klick auf „Wiederherstellen“. Es öffnet sich ein Fenster mit einem Wizard. Lustigerweise muss ich sämtliche Daten noch einmal eingeben, was mich schon mal ein wenig erstaunt, denn die angelegten Profile definieren ja bereits, welchen lokalen Ordner man mit welchen Zugangsdaten auf welchen Server sichern will – da wäre es doch an sich am einfachsten, bei einem Restore einfach das gewünschte Profil zu wählen, denn dann hätte die Syno ja schon alle Angaben, um die Daten zurückzukopieren. Nun, ist aber offensichtlich nicht so: Bei einem Restore spielen die angelegten Profile einfach keine Rolle. Stattdessen fragt mich der Wizard auch hier wiederum nach Servername oder IP-Adresse, Backupmodul, Anwendername, Passwort und …

Und jetzt der Haken: Nichts „und …“. Es gibt beim Restore keine Möglichkeit, eine verschlüsselte Verbindung zu benutzen. Nein, ich habe nichts übersehen. Da ist einfach nichts. Keine Checkbox. Man kann also zwar Backups per rsync über SSH machen – aber man bekommt sie auf diesem Weg niemals wieder. Also, von Hand auf der Shell per SSH natürlich schon, klar, aber eben nicht über das Webinterface: Restores gehen einfach nur unverschlüsselt. Und da auf dem unverschlüsselten Port 873 ja nur mein Dummy-Modul hinterlegt ist, ist da natürlich nichts zu holen, denn auf dem SSH-Port versucht die Syno es gar nicht erst. FAIL #2.

Und das alles wegen der Verschlüsselung, als wenn der Wunsch nach vertraulicher Datenübermittlung nur etwas für Geheimagenten wäre. Ich gehe davon aus, würde ich rsync als Server laufen lassen, schön mit root-Rechten, alle Module zentral in /etc/rsyncd.conf erfassen und mir einfach keinen Kopf darum machen, dass meine Zugangsdaten dann leicht zu knacken und mein gesicherter Datenbestand gleich ganz unverschlüsselt durchs Internet übertragen würde … dann, ja dann wäre bestimmt alles ganz einfach.

Aber, Synology: Wir leben nicht mehr in den Neunzigern! Hier besteht wohl noch dringender Nachholbedarf.

Hexadezimal zählen lassen

13. November 2009 von Christopher Hirschmann

Manchem fällt es leicht, anderen nicht: das Arbeiten in verschiedenen Zahlensystemen. Ich selbst komme mit binären Zahlen und dezimalen recht gut klar, mit hexadezimalen Zahlen tue ich mich eher schwer. Spätestens bei IPv6 komme ich aber nicht mehr drum herum, weil dort die IP-Adressen nicht mehr dezimal sondern hexadezimal notiert werden.

Trotzdem darf mensch faul sein und zählen lassen. Ich suchte also nach einem bequemen Weg von einem Shell-Skript hexadezimal zählen zu lassen und wurde recht schnell im Internet fündig:

#!/bin/sh
# count from 0 to FFFF
declare -i i=0;
while ((i <= 16#FFFF)) ; do
H=$(printf "%X\n" $i);
echo $H;
((i=i+1));
done
exit 0;

Hierzu braucht man nur noch zu wissen, was 16#FFFF letztlich bedeutet: Zuerst wird die Basis angegeben, in diesem Fall 16, dann wird hinter dem # die gewünschte Zahl notiert. Ein bloßes i <= FFFFF genügt nicht, weil Bash als nicht typisierte Sprache sonst nicht weiß, ob sie es mit Zahlen oder Buchstaben zu tun hat. Wer binär zählen lassen will muß entsprechend 2#1111 oder etwas ähnliches verwenden. Aber: Damit ist nur die Zahl angegeben, wenn man sich das ausgeben läßt sieht man, daß Bash intern immer noch dezimal zählt. Damit die Ergebnisse auch hexadezimal notiert werden, braucht es die Konversion mit H=$(printf "%X\n" $i). Das Zauberwort lautet hier %X.

Wenn Xen seine Gastfreundlichkeit aufgibt

26. Oktober 2009 von Jonas Pasche

Ein ärgerlicher Vorfall erwischte uns, unpassenderweise auch noch mitten am Tag. Für einen Kunden betreiben wir ein Xen-Host, auf dem insgesamt drei praktisch identische virtuelle Maschinen laufen. Aus einem nicht weiter relevanten Grund rebootete der Kunde eine der drei virtuellen Maschinen – an sich ein normaler Vorgang.

Nicht normal war hingegen, dass die Maschine nicht mehr hochfuhr, und zwar mit einem Effekt, der uns völlig neu war: Der Kernel bootete ein bisschen und fror dann ein. Meistens, aber nicht immer an der gleichen Stelle. Google war absolut nicht hilfsbereit, denn egal an welcher Zeile die Instanz einfror, die Suchmaschine lieferte sie nur im Zusammenhang mit weiteren Bootmeldungen, und alles, was sich zu Suchbegriff-Kombinationen aus „xen“, „linux“, „kernel“, „freeze“ etc. bilden ließ, lieferte entweder zuviel oder wenig Ergebnisse.

Nun blieben nicht viele Möglichkeiten: Allgemein würde ich wohl vermuten, dass, wenn ein Kernel beim Booten einfriert, und dies an wechselnden Stellen, ein Hardware-Problem nicht übermäßig unwahrscheinlich wäre. Alternativ ist vielleicht „ein Bit auf der Platte gekippt“, wie Christopher einwarf. Um letzteres auszuschließen, haben wir verschiedene Kernel gebootet. Normalerweise bootet das Gastsystems mittels pygrub einen eigenen Kernel; es kann aber auch mit einem Kernel des Gastsystems booten. Keine der beiden Varianten funktionierte, was uns leicht nervös machte. Es gab seit dem letzten Reboot des physischen Servers keinerlei Updates des Xen-Kernels oder der Xen-Userspace-Tools. Die Instanz hatte so, wie sie vorlag, bereits erfolgreich gebootet. Wieso nun nicht mehr?

Auf dem Server befand sich zudem noch das „Master-Image“, von dem aus die anderen drei Instanzen erzeugt worden waren. Testweise erzeugten wir noch eine weitere Test-Instanz basierend auf dem Master-Image – und auch die bootete nicht.

Es musste also die Hardware sein, da waren wir sicher. Einen Reboot des Wirts durchzuführen scheuten wir uns, da ja noch zwei weitere Instanzen darauf liefen – und zwar völlig störungsfrei. Ein Reboot der physischen Hardware könnte die Erreichbarkeit dieser noch laufenden Instanzen ebenfalls beeinträchtigen – und das wollten wir nicht riskieren. Unser Plan sah daher vor, das Image der (gestoppten) Instanz auf andere Hardware mit einem Xen-Wirt zu kopieren und die virtuelle Maschine dort zu booten. Kapazitäten waren ausreichend vorhanden. Der Kopiervorgang strapazierte unsere Nerven und auch die unseres Kunden, aber es half letztlich ja nichts. Wenn überdurchschnittlich hohe Verfügbarkeit ein Muss ist, bauen wir gerne einen Failover-Cluster dafür auf; für den konkreten Anwendungsfall war das aber nicht gefordert.

Nachdem dann endlich das Image übertragen war, folgte Ernüchterung: Auch dort bootete die Instanz nicht – mit exakt den gleichen Symptomen. Nun machte sich wirklich Ratlosigkeit breit. Wenn nun wirklich nichts, aber auch gar nichts an den Gegebenheiten verändert wurde, weder auf dem Gast noch auf dem Wirt, wieso bootete dann die Instanz nicht einmal mehr auf anderer Hardware..? Noch verzweifelter wurden wir, als sich selbst völlig frische, mittels virt-install angelegte Images nicht booten ließen – virt-install zog sich vmlinuz und initrd.img aus dem Netz, bootete – und fror ein.

Da auf dem separaten Xen-Server ansonsten nur noch eine einzige Instanz lief, deren Verfügbarkeit nicht mit hoher Priorität gegeben war, haben wir daher beschlossen, die fragliche Hardware einmal zu rebooten, und wir verstehen es bisher noch nicht, aber: Nach dem Reboot lief alles wieder 1a. Das rüberkopierte Image bootete, es ließen sich mit virt-install neue Instanzen anlegen – alles wunderbar.

Dadurch ermutigt rebooteten wir auch den ursprünglichen Wirt. Das Risiko, zwei weitere Xen-Instanzen während dieser Zeit temporär unerreichbar zu machen, war angesichts der neuen Entdeckung geringer einzuschätzen als vorher. Und, welch Wunder: Nach einem Reboot fuhren alle drei Instanzen anstandslos hoch.

Eine vollständige Klärung haben wir bisher nicht erfahren dürfen. Wir wissen nun, dass es offensichtlich Situationen gibt, in denen sich der Xen-Hypervisor so verfährt, dass er Gäste nicht mehr hochfahren kann, und zwar ohne aussagekräftige Fehlermeldungen. Auf dem Wirt war weder per dmesg noch in irgendeinem Logfile irgendetwas zu erfahren, was ihm nicht passen würde. Das ist so natürlich äußerst unbefriedigend, denn ein Reboot ist so gesehen natürlich eigentlich nur die letzte Maßnahme. Zugegebenermaßen ist eine derartige Situation aber nun auch zum allerersten Mal eingetreten, und wir betreiben durchaus viele Xen-Instanzen, und das auch durchaus schon seit längerer Zeit. Zurück bleibt der unbefriedigende Geschmack, ein Problem gelöst, aber nicht begriffen zu haben. Wir werden das im Auge behalten – vielleicht haben wir Glück und eine unserer Testmaschinen verfällt ebenfalls in diesen Zustand, so dass wir die Situation ohne den Druck, eine Maschine wieder erreichbar machen zu müssen, analysieren können. Nachträge hierzu veröffentlichen wir natürlich gerne.

Spaß mit der Zeitumstellung

26. Oktober 2009 von Jonas Pasche

Okay, diese Stolperfalle habe ich mir letztlich selbst gebaut – woran man nicht alles denken muss.

Ein Kunde betreibt auf seinem Server einige Dienste, die eine Art „Lebenszeichen“ in Form eines Unix-Timestamps (für Unwissende: Die Zahl der Sekunden seit dem 1. Januar 1970) in einer MySQL-Tabelle hinterlassen. Da diese Dienste besonders wichtig für ihn sind, haben wir die Nagios-Überwachung seines Servers um einen Check für diese Dienste erweitert, der sich die Differenz zwischen der dort gespeicherten Zeit und der aktuellen zeit in Sekunden heraussucht. Hierbei darf ein gewisser Schwellenwert nicht überschritten werden, sonst gibt es einen Alarm. Und so sieht’s aus:

SELECT name,
       text,
       UNIX_TIMESTAMP(NOW())-text AS diff
  FROM setup
 WHERE name LIKE "%\_time"

Ich rechne hier die aktuelle Zeit mittels UNIX_TIMESTAMP() in einen Unix-Timestamp um, damit ich Sekunden mit Sekunden vergleiche – daraus ergibt sich dann einfach eine Differenz. Das klappte auch hervorragend.

Bis zur Zeitumstellung am Sonntag. Denn hier zeigt sich letztlich, dass die Zeit „2009-10-25 02:17:00“ durchaus nicht eindeutig ist: Es gibt sie nämlich um 2:17 Uhr vor der Zeitumstellung, und es gibt sie um 2:17 nach der Zeitumstellung, also um „gefühlt 3:17 Uhr“. Die Unix-Zeit, die ja einfach nur stupide Sekunden zählt, läuft aber eben einfach weiter. Das bringt einen zur konsequenterweise logischen, aber trotzdem erstaunlichen Situation, dass zwei Unix-Timestamps die gleiche lokale Zeit ergeben können. Konkret:

1256429820 => 2009-10-25 02:17:00 (vor der Zeitumstellung)
1256437020 => 2009-10-25 02:17:00 (nach der Zeitumstellung)

Daraus ergibt sich wiederum, dass sich aus der lokalen Zeit „2009-10-25 02:17:00“ nicht auf einen Unix-Timestamp schließen lässt, sondern gleich auf zwei, weil die lokale Zeit eben keine Angabe enthält, ob man sich gerade vor oder nach der Zeitumstellung befindet.

Nun befindet sich MySQL in meinem Script in genau der Situation: Es muss einen Unix-Timestamp aus „2009-10-25 02:17:00“ bilden, und das kann es nicht verlässlich. Der springende Punkt liegt aber schon beim NOW(): An sich weiß MySQL nämlich durchaus, ob es sich vor oder nach der Zeitumstellung befindet. Aber in dem Moment, wo NOW() diese aktuelle Zeit in einen String konvertiert hat, ist diese Information faktisch verloren – man sieht sie ja dem String nicht mehr an. UNIX_TIMESTAMP() kann also letztlich nur raten – und rät, die Zeitumstellung sei schon gelaufen.

Das führt zu der ärgerlichen Situation, dass das obige SQL-Statement in der fraglichen Stunde zwischen 2 und 3 Uhr plötzlich der Meinung ist, die zu checkenden Dienste seien bereits eine Stunde überfällig. Ab Punkt 2 Uhr liefert UNIX_TIMESTAMP(NOW()) nämlich bereits den Timestamp für 2 Uhr nach Zeitumstellung.

Um so ärgerlicher, weil ich viel einfacher auch einfach nur UNIX_TIMESTAMP() ohne Argument hätte schreiben können. Das liefert dann nämlich den aktuellen Timestamp, und dann sogar den richtigen, weil hier ja MySQL „die lokale Zeit“ (von der es weiß, ob sie vor oder nach der Umstellung liegt) in einen Timestamp konvertiert und keinen String, dem diese Information fehlt.

Aber ich denke, ich habe meine Strafe bereits bekommen: Am Sonntag um kurz nach 2 Uhr nachts von der Überwachung aus dem Schlaf geklingelt zu werden, dürfte hoffentlich ausreichend gewesen sein. Das Nagios-Check-Script ist dann inzwischen auch angepasst.

MySQL-Backups, aber wie?

26. Oktober 2009 von Christopher Hirschmann

Dieser Artikel ist der erste Teil einer kleinen Gruppe von Artikeln über MySQL-Backups. Die weiteren Artikel werden hier verlinkt, sobald sie erscheinen.

Hier ist der zweite Artikel: MySQL Replikation

Hier ist der dritte Artikel: Hinter der MySQL-Replikation aufräumen

Hier ist der vierte und letzte Artikel: MySQL mit daemontools

Wenn es um Backups geht, sind Datenbanken immer nochmal gesondert zu beachten. Zwar kann man die vom Datenbanksystem auf die Platte geschriebenen Daten mit den üblichen Backup-Tools sichern, aber in den meisten Fällen wird man so kein konsistentes Backup der im Datenbanksystem gespeicherten Daten erhalten. Dabei ist es auch relativ egal, ob man nun ein Backup aller im Datenbanksystem gehaltenen Daten haben möchte, oder nur von einzelnen Datenbanken oder Tabellen. In jedem Fall hat mein einige Hürden zu nehmen.

Man braucht also ein Extra-Tool und nicht selten muß man sich die Lösung dann noch zusammenskripten. Im Planet MySQL Blog findet sich eine schöne Auflistung der gängigsten Lösungsmöglichkeiten:

There are a couple of options available to get consistent backups from MySQL.

  1. Use mysqldump with FLUSH TABLES WITH READ LOCK
  2. Use a slave with STOP SLAVE and your favourite backup tool.
  3. For innodb, use the commerical backup tool ibbackup
  4. Use LVM (Logical Volume Manager) snapshots with FLUSH TABLES WITH READ LOCK
  5. Shutdown the database.

Man hat also immerhin verschiedene Optionen, unter denen man wählen kann. (Diese Liste erhebt keinen Anspruch auf Vollständigkeit.) Nebenbei wird aber offensichtlich, daß die Frage nach dem Backup bei der Konzeptionierung und Entwicklung von MySQL keine große Rolle gespielt hat – wie bei leider allzuvielen anderen Softwareprojekten auch, darunter viele andere Datenbanken.

Die Wahl die wir für unsere Anwendungsfälle treffen wird von folgenden Überlegungen geleitet:

  • #1 bedeutet dem Datenbanksystem wird befohlen, alle Tabellen konsistent auf die Platte zu schrieben und sich dann zu sperren. Dann zieht man mit mysqldump alle Daten aus dem Datenbanksystem und hebt zuletzt die Sperre wieder auf. Solange die Sperre besteht ist kein schreibender Zugriff auf die Daten möglich. Soetwas kann man folglich nur dann machen, wenn man ein regelmäßiges Wartungsfenster hat, in dem die Applikation die auf die Datenbank zugreift nicht (oder nur-lesend) benutzt wird. Bei einem Buchhaltungssystem mag das noch praktikabel sein, aber bei Internet-Diensten, die keine Ladenöffnungszeiten kennen kommt diese Vorgehensweise eher selten in Frage. Unabhängig davon tritt hier das Problem auf, daß das Sichern der Daten mit mysqldump Zeit beansprucht. Hier müßte also zusätzlich darauf geachtet werden, daß das Wartungsfenster eingehalten wird.
  • Für #5 gilt im Grunde das gleiche. Ein Anhalten des Datenbanksystems hat für die es benutzenden Applikationen sogar noch gravierendere Konsequenzen als eine Sperre, sie können nichtmal mehr lesend auf die Daten zugreifen. Hier würde das Backup nicht mit mysqldump geschrieben, sondern mit einem anderen Tool welches auf Dateisystemebene arbeitet. Auf Unix-basierten Systemen gibt es da eine sehr große Auswahl, die von tar über dump bis zu amanda reicht, um nur ein paar zu nennen.
  • #3 kommt für uns nicht in Frage, weil wir keine kommerziellen Tools benutzen wollen. Das ist sowohl eine philosophische Frage als auch eine praktische, denn zu einem kommerziellen Tool könnten wir unseren Kunden nicht den Support bieten, den wir bei freier und quelloffener Software leisten können.
  • #4 bedeutet auch, daß die Datenbank in einen konsistenten Zustand gebracht und dann gesperrt wird, allerdings nur solange bis ein LVM-Snapshot erstellt wurde. Das kann zwar auch etwas dauern (aber nur wenn die Hardware oder Software an ihre Grenzen stoßen, LVM-Snapshots gehen in der Regel schnell), geht aber in jedem Fall schneller als Methode #1. Hier gibt es aus unserer Sicht neben der (wenngleich kürzeren) Sperrzeit noch zwei Nachteile:
    1. Zum einen benötigt man bereits eine LVM-Umgebung. Sicherlich kann man auch unter einem laufenden System eine einrichten, aber das ist knifflig, kann schief gehen, dauert und geht nicht gänzlich ohne Downtime mindestens des Datenbanksystems. Nun mag man sich angewöhnt haben unter jedem System LVM einzurichten, um genau solchen Eventualitäten vorzubeugen. Es mag aber Situationen geben in denen man darauf verzichtet, weil die zusätzliche Komplexität unnötig ist oder gar störend wäre, z.B. wenn man ein System von ISCSI bootet und man statt mit LVM zu arbeiten einfach weitere ISCSI-Targets einbinden könnte.
    2. Die Wiederherstellung aus dem Backup ist zum Teil sehr aufwendig. Häufig wird man einen separaten MySQL-Dienst starten wollen oder müssen, über den man auf den Datenbestand im Snapshot zugreift. Wenn man innodb verwendet bleibt einem im Grunde nichts anderes übrig, da innodb einen großen Container für alle gemeinsam verwalteten Datenbanken verwendet, aus dem sich einzelne Tabellen nicht ohne weiteres extrahieren lassen.

Bleibt somit noch Methode #2. Hier wird ein zweiter MySQL-Dienst eingerichtet und als Slave konfiguriert, so daß er über die Replikationsfunktion von MySQL den Datenbestand des ersten spiegelt und sich aktuell hält. Für Backups wird dann auf dem Slave die Replikation vorübergehend angehalten. Ab diesem Punkt kann im Grunde jede beliebige Backup-Methode verwendet werden, man kann den Slave sogar für das Backup runterfahren. Uns genügt es aber, bei angehaltener Replikation alle Datenbanken und Tabellen des Slave einzeln mit mysqldump auszulesen und an einen Ort zu schreiben, an dem unser normales Backup sie findet und sichert.

Ein Slave der nur für Backups verwendet wird, erzeugt nicht sonderlich viel Last auf einem System und kann daher prinzipiell auch auf demselben Rechner laufen wie der Master, wenn man das denn möchte. Wenn man so vorgeht, sollte man jedoch bedenken, daß man einen zweiten MySQL-Server betreibt, der auch entsprechend zu sichern ist. Wenn der Slave nur für Backups dienen soll, muß man Nutzerzugriff darauf unbedingt verhindern, weil vom Nutzer gemachte Änderungen sonst durch die Replikation überschrieben werden können usw. Damit das möglich ist, kann die mysql-Datenbank mit den Nutzerdaten auf dem Slave vom Master abweichen. In so einem Fall empfiehlt es sich, diese eine Datenbank separat vom Master zu sichern (hierbei kann man aber getrost mit einem READ LOCK arbeiten, da diese Datenbank relativ klein ist und sich schnell sichern läßt.)

Wie man die Replikation einstellt, wird im nächsten Artikel beschrieben.

Defekte Quota-Dateien in Ordnung bringen

05. Oktober 2009 von Jonas Pasche

Dann und wann ist das Quota-System ein bisschen empfindlich. Spätestens wenn man in /var/log/messages sowas hier liest …

Oct  1 04:18:37 server4 kernel: VFS: find_free_dqentry(): Data block full but it shouldn't.
Oct  1 04:18:37 server4 kernel: VFS: Error -5 occured while creating quota.

… weiß man: Da ist mehr im Argen, als man mit einem einfachen quotacheck korrigieren kann. Nun lassen sich zwar mit quotacheck -c prima neue, frische Quota-Dateien anlegen, nur: Dann haben die User alle erstmal keine Quota mehr. Das ist natürlich schlecht.

Ich habe daher, da repquota noch brauchbare Ergebnisse lieferte, erstmal den Ist-Stand in einer Datei gesichert. Dann quotaoff, quotacheck, quotaon. Keine Fehler mehr, aber eben auch keine gesetzten Quotas mehr. Hier mein quick-and-dirty-Hack dafür, um aus der gesicherten repquota-Ausgabe noch Daten zu übernehmen (die fraglichen Usernamen begannen alle mit „web…“, jaja: Confixx natürlich, aber das Beispiel funktioniert natürlich auch woanders):

for LINE in `cat /root/repquota.2009-10-01 | grep ^web | sed 's/ \+/:/g;'` ; do
  setquota `echo $LINE | awk -F : '{ print $1,$4,$5,$7,$8 }'` / ;
done

Wieso ich hier die Leerräume durch Doppelpunkte ersetze? Ganz einfach: Die for-Schleife der Shell benutzt Leerräume als Trennzeichen, und die Leerräume zwischen den einzelnen Spalten würden genauso behandelt wie die Leerräume, die durch die Zeilenumbrüche entstehen. Die Schleife würde also nicht so oft laufen, wie Zeilen vorhanden sind, sondern so oft, wie Zeilen*Spalten vorhanden sind. Also: Doppelpunkte, weil die weder in den Usernamen noch in den Quota-Werten vorkommen. Und awk kann ganz hervorragend daran splitten.

I/O-Lastprobleme durch Logging

05. Oktober 2009 von Jonas Pasche

Ein Kundenserver machte Ärger. Dass bei einem iowait-Wert von konstant über 90% nicht mehr viel zu wollen ist, dürfte klar sein. Die Maschine ist aber nicht grundsätzlich überlastet: Die Tage davor schnurrte sie noch wie ein Kätzchen.

Mittels iostat war schnell herausgefunden, dass die I/O-Last nicht auf einen Plattendefekt o.ä. zurückzuführen war, sondern wirklich tonnenweise Daten auf die Platten geschrieben wurden.

Leider sind solche Probleme oft schwierig zu analysieren, weil es für I/O-Last im Standard-Linux-Kernel kein Accounting gibt – sprich, man kann nur sehen, auf welchem Blockdevice die Last stattfindet, aber nicht, wer sie verursacht.

Einigermaßen zügig konnten wir „irgendwas mit dem Webserver“ ausmachen, denn wenn der gestoppt war, lief die Maschine wieder ruhig. Also haben wir kurzerhand den server-status-Handler im Apache aktiviert und dazu ExtendedStatus eingeschaltet. Auf diese Weise bekommt man eine schöne Prozessliste der laufenden Requests zu sehen. Und wir hatten Glück: Ziemlich schnell ließ sich eine Site ausmachen, bei der Andreas ein error_log vorfand, das inzwischen rund 42 GB groß war – angesichts der bereits rotierten error_logs, die nur weniger MB groß waren, lag auf der Hand, dass es hier Probleme gibt. Über das error_log fanden wir folgende Zeilen PHP-Code:

...
$handle=fopen($filename,"r");
while(!feof($handle))
...

Das geübte Auge erkennt sofort: Es wird nicht geprüft, ob $filename überhaupt zum Lesen geöffnet werden konnte. Konnte es nämlich nicht, weil die fragliche Datei gar nicht existierte. Wenn es nun aber kein $handle gibt, kann feof() ergo auch niemals am Ende von $handle ankommen – die Schleife läuft somit endlos. Lustigerweise entspricht dieser vorgefundene Code-Abschnitt ziemlich genau dem Negativbeispiel aus der PHP-Doku, und die Kommentare der Seite sind voller Lösungen, wie man sowas richtig programmiert.

Zurück zum Lastproblem. Das entsteht natürlich aus dem Logging, denn für jeden Schleifendurchlauf meckert PHP an, dass man feof() auf etwas ausführt, das kein geöffnetes Filehandle ist. grep sagt uns, dass das etwa 40.000 Mal im error_log steht. Pro Sekunde, wohlgemerkt – kein Wunder, dass das error_log nicht mal den Vormittag über brauchte, um langsam aber sicher die Platte zu füllen und gleichzeitig den kompletten Server auszubremsen. Man kann ja vieles accounten: CPU-Zeit, RAM, Prozessanzahl … aber dummerweise nicht ohne weiteres, wieviel Log-Output ein Script so generieren darf.


Impressum