Artikel mit ‘linux’ getagged

Wenn sonst nichts mehr beim kaputten RAID5 hilft

Donnerstag, 25. März 2010

Ein Kundeskunde ist stolzer Besitzer einer Synology CubeStation. Das sind kleine NAS-Server für daheim, auf Linux-Basis mit einem proprietären Webinterface, aber immer noch mit einer Shell. Je nach Modell sind unterschiedlich viele Platten verbaut; hier konkret vier. Jede Platte hat drei Partitionen, wobei die ersten beiden kleineren jeweils ein kleines RAID1 für die Root- bzw. Swap-Partition darstellen (und hier ergo dreifache Redundanz haben); die jeweils dritten – großen – Partitionen sind zu einem RAID5 ohne Spare zusammengefasst, das als /volume1 gemountet wird, was bei den Synology-Boxen offensichtlich für die eigentliche Ablage von Daten benutzt wird.

Eine der Platten ging kaputt. Kein Problem, dafür hat man ja das RAID. Beim Ausbau der defekten Platte wurde allerdings offensichtlich das Stromkabel einer der weiteren Platten gelockert. Wir wissen nicht genau, was passiert ist, aber der Zustand war der, dass die ersten beiden RAIDs problemlos zusammengesetzt werden konnten; das dritte aber nicht. /dev/sda3 und /dev/sdc3 waren da, /dev/sdb3 fehlte (weil ausgebaut), und /dev/sdd3 – die Platte mit dem Wackelkontakt am Strom – hatte zwar einen md-superblock, der aber nicht mit den anderen harmonierte. Zwar stimmte die UUID; allerdings war der Update-Timestamp nicht ganz synchron. Ein

mdadm --assemble /dev/md2 /dev/sd[acd]3

schlug von daher fehl. Nun ist das an sich kein großes Problem: Man kann mdadm einfach zwingen und die md-superblocks dann wieder neu schreiben. Also:

mdadm --assemble --force /dev/md2 /dev/sd[acd]3

Denkste. Zwar bereitet sich mdadm schon korrekt vor und informiert darüber, dass es den abweichenden update count der dritten Partition anzupassen gedenkt. Dann jedoch stirbt es mit einem Segmentation Fault.

Offensichtlich ein bekanntes Problem. Und zwar nicht spezifisch für die Syno-Box; auch der Ubuntu-Bugtracker findet:
mdadm seg faults when forced to assemble array. Das Problem scheint also spezifisch für mdadm 2.6.7 zu sein – schon bei der 2.6.7.1 soll es angeblich wieder gehen.

Nun ist auf der Syno-Box aber eben die 2.6.7 – und ein Update ist nicht in Sicht. Was also tun? Mein erster Gedanke dazu (und ich bleibe dabei, so schlecht war er nicht): IPKG installieren, und darüber dann eine hoffentlich wahlweise ältere oder neuere Version von mdadm installieren, die diesen Bug nicht hat. Und wenn’s keine gibt, bliebe immer noch die Möglichkeit, über IPKG einen Compiler zu installieren und mdadm 2.6.7.1 aus den Sourcen zu kompilieren.

Es gibt ein mdadm in IPKG. Ein 2.6 – also älter. Nur behauptet /opt/sbin/mdadm --examine (also das via IPKG installierte, im Gegensatz zum vorinstallierten /sbin/mdadm) bei allen Partitionen, dort keinen md-superblock vorzufinden. Hm, vielleicht zu alt? Also doch die Variante mit dem Compiler, und dann ein mdadm aus den Sourcen kompiliert. Noch eine neuere Version genommen. Und dann noch eine. Und noch eine ältere. Aber es half nichts: Alle Versionen fanden keinen md-superblock. Nicht mal eine aus den Sourcen kompilierte 2.6.7, obwohl das doch nun die identische Version zur offiziell installierten ist.

(Gängige Reparaturanleitungen empfehlen, die Platten aus der Syno in einen normalen PC zu bauen – wofür man erstmal entsprechend viele freie SATA-Ports bräuchte – und dann dort den Superblock von der little-endian-Byteorder der ARM-CPU der Syno auf big endian einer Intel-CPU zu ändern, damit das RAID auf einem Intel-PC wieder zusammengesetzt werden kann. Mein Bauchgefühl sagt mir, dass das Problem mit den überhaupt nicht mehr erkannten md-superblocks etwas damit zu tun haben könnte. Allerdings habe ich das mdadm ja auf der Syno kompiliert, also auch mit deren Headerfiles. Insofern müsste die Byteorder eigentlich stimmen. Hm.)

Lange Rede, kurzer Sinn: Nichts half. Bis auf das RAID-Wiki bei kernel.org, das den entscheidenden Tipp gab:

When an array is created, the data areas are not written to, *provided* the array is created in degraded mode; that is with a ‚missing‘ device.

Ich denke, man kann sich das in etwa so vorstellen wie eine defekte Partitionstabelle auf einer Festplatte: Es scheint alles weg; keine Partition ist mehr lesbar. Dabei ist klar, dass die Daten an sich alle noch auf der Platte vorhanden sind – man kommt nur ohne Partitionstabelle nicht mehr dran. Man muss es also „nur“ schaffen, die paar Bytes Partitionstabelle wiederherzustellen, und das Problem ist gelöst.

So auch hier: Wird bei einem mdadm --create ein Device als „missing“ angegeben, wird der Datenbereich der Platten in Ruhe gelassen und nur der Superblock neu geschrieben. Das ist natürlich nur was für echte Männer. Naja, oder für Männer wie mich, die sich vorher versichert haben, dass der Kunde Images der Platten gesichert hat, falls bei den Arbeiten am RAID irgendetwas schief geht. Auf jeden Fall muss man für so ein „Reparatur-create“ allerdings wissen, wie dieser Superblock auszusehen hat: Welcher RAID-Level? Wieviele Platten? Welche Platte in welchem RAID-Slot? Welche Chunk-Size? Welche Symmetrie? Das war in diesem Fall einfach: Alle drei Partitionen haben ja schließlich noch einen md-superblock, der all diese Parameter verrät. Letztlich war es also nur das hier:

mdadm --create /dev/md2 --chunks=64 --level=5 --raid-devices=4 \
  /dev/sda3 missing /dev/sdc3 /dev/sdd3

mdadm bemerkte korrekt, dass auf den Partitionen bereits etwas drauf sei, was nach einem RAID riechen würde. Okay, wissen wir ja. Nach einer entsprechenden Bestätigung wurden die Superblocks neu geschrieben. Keine Fehlermeldung. Ein

mount /dev/md2 /volume1

funktionierte anstandslos. Alle Daten vorhanden; auch nach einem Reboot erkennt die Syno-Box das RAID5 jetzt wieder von selbst und bindet es ein. Nach einer jetzt natürlich umgehend anstehenden Datensicherung wäre es von daher im Bereich des Möglichen, nun wieder die vierte, noch fehlende Platte einzubauen und einen Resync zu wagen.

Mein Kunde berichtet vom Kundeskunden, dieser sei „grad total hingerissen davon (echt jetzt)“. Schöne Sache, wobei es hier weniger um ein geschäftliches Desaster gegangen wäre, sondern „nur“ etliche Gigabyte an Fotos über den Jordan hätten gehen können. Aber die sind manchmal ja noch viel wertvoller. Ich gehe davon aus, ab heute macht der Kundeskunde Backups.

Levenshtein-Distanz mit MySQL

Dienstag, 15. Dezember 2009

Für ein Projekt benötigte ich eine Möglichkeit, eine Duplikatsprüfung durchzuführen. Anforderung war, nicht nur identische, sondern auch „ähnliche“ Datenbank-Einträge wiederfinden können. Dabei stieß ich auf den Algorithmus der Levenshtein-Distanz. MySQL kennt von Haus aus keine Funktion hierfür. Nach einigem Suchen bin ich allerdings auf ein kleines Plugin gestoßen, dass diesen Algorithmus als user-defined function nachrüstet.

Um es kurz zu machen – für die Installation auf einem CentOS 5 auf einer 64-Bit-Plattform habe ich folgende Schritte benötigt:

mkdir /usr/local/src/mysql_levenshtein_udf-1.0
cd /usr/local/src/mysql_levenshtein_udf-1.0
wget http://joshdrew.com/mysql_levenshtein_udf-1.0.tar.gz
tar -xzvf mysql_levenshtein_udf-1.0.tar.gz
gcc -I/usr/include/mysql -fPIC -O -pipe -o mysqllevenshtein.so -shared \
  -L/usr/lib64/mysql -lmysqlclient mysqllevenshtein.cc
cp mysqllevenshtein.so /usr/lib64/libmysqllevenshtein.so

Das -fPIC steht nicht in der README, wird aber für die 64-Bit-Plattform benötigt. Interessanterweise sucht mysqld die .so-Datei in /usr/lib64 und nicht in /usr/lib64/mysql, wie ich eigentlich erwartet hätte. Aber sei’s drum.

Auf der mysql-Shell schließlich noch als root:

CREATE FUNCTION levenshtein RETURNS INT SONAME 'libmysqllevenshtein.so';

Und das war’s auch schon. MySQL merkt sich solche vom Benutzer eingefügten Funktionen in der Tabelle mysql.func, so dass sie problemlos einen Restart überdauern. Sehr schick!

Und so wird’s benutzt, hier zum Beispiel vor dem Hinzufügen eines neuen Datensatzes, um zu schauen, ob es schon jemanden gibt, der möglicherweise so in etwa ebenfalls „Jonas Pasche“ heißt – nur dass eben auch „Ionas Paschke“ gefunden würde:

SELECT *
  FROM contact
 WHERE LEVENSHTEIN(n_family, "Pasche") < 2
       AND LEVENSHTEIN(n_given, "Jonas") < 2;

Synology-Netzwerksicherung: Doppel-FAIL

Freitag, 13. November 2009

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.

Spaß mit der Zeitumstellung

Montag, 26. Oktober 2009

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.

Defekte Quota-Dateien in Ordnung bringen

Montag, 05. Oktober 2009

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

Montag, 05. Oktober 2009

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.

rsync und der Doppelpunkt

Dienstag, 04. August 2009

Kleine Randnotiz: Das Synchronisieren von Dateien mittels rsync klappt nicht, wenn ein Dateiname einen Doppelpunkt enthält. Dann vermutet nämlich rsync, dass damit eine Angabe in der Syntax host:path gemeint ist. Beispiel, in dem eine Datei namens „a:b“ angelegt wird und nach „c“ synchronisiert werden soll:

$ touch a:b
$ rsync -v a:b c

Effekt: Geht schief, weil „a“ als Hostname betrachtet wird, der nicht aufgelöst werden kann. Dave Dykstra weiß Rat: Wenn man eine Pfadangabe davorsetzt, wobei schon „./“ ausreicht, wird der Doppelpunkt nicht mehr fehlinterpretiert. So geht’s also:

$ rsync -v ./a:b c
a:b

sent 63 bytes  received 31 bytes  188.00 bytes/sec
total size is 0  speedup is 0.00

Passwörter: Nicht mit SSH-Keys

Sonntag, 13. April 2008

Jetzt arbeiten wir schon seit vielen Jahren kaum noch mit traditionellen Passwörtern, sondern sind auf SSH-Keys umgestiegen. Im Rahmen eines Wartungsvertrags für einen dedizierten Server haben wir die vereinbarten Arbeiten durchgeführt, bis der Kunde verwundert fragte, wer uns denn das neue root-Passwort mitgeteilt habe. Nun ja: Niemand eben. Aus diesem Grund mal eine kleine Zusammenstellung von praktischen Links insbesondere für Windows-Nutzer (unter Linux und MacOS X ist die Nutzung von SSH ja ohnehin eher verbreitet):

Auf der Website von PuTTY bekommt man neben dem SSH-Client selbst auch einen Schlüsselgenerator für SSH-Key sowie einen optionalen SSH-Agent zum Cachen der entschlüsselten Keys. Kai Billen hat eine gute deutsche Anleitung für alles verfasst. Noch Fragen?


Impressum