We all change, when you think about it. We're all different people all through our lives. And that's OK, that's good, you've got to keep moving, so long as you remember all the people that you used to be.

The Doctor

Ausangssituation

Wir arbeiten mit Release-Branches, vorgehen eigentlich immer so, dass ein Release-Branch vom Trunk erzeugt wird, wenn dieser dann fertig und abgenommen ist, wird der in den Trunk gemerged und live genommen, dann aus dem Trunk der nächste Release-Branch erzeugt.

  • /trunk
  • /branches
    • release-a

Jetzt begab es sich, dass der Release-Branch A soweit fertig war, aber noch nicht getestet und abgenommen, also noch nicht für den Merge in den Trunk bereit. Da der nächste Release-Branch B wieder aus dem Trunk erzeugt werden würde, könnten darin erstmal keine Anpassungen oder Weiterentwicklungen gemacht werden, die auf Funktionen im Release-Branch A basieren. Genau das sollte aber stattfinden.

Lösung:

Den Release-Branch-B nicht vom Trunk, sondern vom Release-Branch-A erzeugen. Soweit ist das noch nicht magisch, die Probleme gehen nämlich da an, wo man diesen Branch B dann in den Trunk zurück spielen will, weil der dann alles, was im Branch A passiert ist auch in den Trunk mitnimmt und das führt nur zu Konflikten und einer menge Ärger.

Die wahre Lösung

Nachdem dann Branch A in den Trunk gemerged wurde, macht man von der Revision, die den Merge in den Trunk hat einen Record-Only Merge in den Branch B. Der Branch lässt sich dann ohne Konflikte und komplett sauber mit --reintegrate am Ende seines Zykluses in den Trunk mergen.

TL;DR

$ svn cp $REPO/trunk $REPO/branches/a
# do work and commit
$ svn cp $REPO/branches/a $REPO/branches/b
# do work in branches a and b
# keep branch b in sync with branch a
$ cd $BRANCH-B
$ svn merge $REPO/branches/a
$ svn ci -m '$MERGEMESSAGE'
# branch a now ready for reintegrate in trunk
$ cd $TRUNK
$ svn merge $REPO/branches/a --reintegrate
$ svn ci -m '$MERGEMESSAGE'
...
Committed revision 1000.
# here comes the magic
$ cd $BRANCH-B
$ svn merge $REPO/trunk -c 1000 --record-only
$ svn ci -m '$MERGEMESSAGE'
# keep working in branch b, when ready, just reintegrate as normal
$ cd $TRUNK
$ svn merge $REPO/branches/b --reintegrate

XDebug installieren

Folgendes hab ich vor: Ich will PHP-Code sauber debuggen können, schön mit Debugger und Co.

Ausganssituation:

  • PHP 5.3 per CGI in Apache eingebunden
  • PHP 5.3 selber compiliert aus den Sourcen
  • Xubuntu 13.04
  • IDE: VIM

XDebug runterladen und compilieren

XDebug hab ich mir aus dem pecl gezogen und dann ein Debian Paket draus erstellt

$ wget http://pecl.php.net/get/xdebug-2.2.3.tgz
$ tar -xvzf xdebug-2.2.3.tgz
$ sudo /usr/bin/php53/phpize
$ sudo ./configure --with-php-config=/usr/bin/php53/php-config
$ sudo make
$ sudo checkinstall

checkinstall führt auch ein "make install" aus, allerdings erzeugt es gleichzeitig noch ein Debian Paket, dass man dann irgendwann bequem deinstallieren kann.

XDebug einbinden

Folgendes hab ich danach in die php.ini eingebunden um XDebug zu aktivieren

[xdebug]
zend_extension=/usr/share/php53/lib/php/20090626/xdebug.so
xdebug.remote_enable = 1 
xdebug.remote_port = 9000
xdebug.remote_host = localhost

Der Pfad muss bei dir evtl. angepasst werden, danach Apache neu starten, XDebug ist dann ab sofort einsatzbereit

XDebug mit vim nutzen

Für den VIM gibt es ein Plugin, so dass man VIM als Client für remote XDebug-Sitzungen nutzen kann, einfach runterladen und installieren.

Debug-Sitzung starten

Browser aufmachen und für example.com bspw. folgende URL eingeben:

http://example.com/?XDEBUG_SESSION_START=1

Dadurch wird im Browser ein Cookie gesetzt, dass eine Stunde gültig ist, und XDebug versucht bei jedem Pageload den Debugger zu finden, wenn er nicht gefunden wird, macht das auch nix, dann wird die Seite ganz normal geladen.

Jetzt VIM starten, F5 drücken, in den Browser wechseln Seite neu laden und los geht das lustige debuggen.

... oder zumindest verlangsamen

Neulich ist mir bei der Log-File Analyse eines Servers mal wieder aufgefallen, dass es wohl noch immer eine recht verbreitet Methode ist, per Brute Force oder Dictonary Attack zu versuchen sich Zugang zu Servern per SSH zu verschaffen.

Nachdem eine Logfileauswertung, auch automagisiert, immer nur zeitverzögert reagieren kann, habe ich weitergesucht und folgende Lösung gefunden. Ich limitiere die Anzahl SYN-Pakete am SSH Port, den jedes SYN Paket ist ein neuer Versuch, sich einzuloggen.

# cleanup
iptables -F
iptables -X SSH_CHECK

# set rules
iptables -N SSH_CHECK
iptables -A SSH_CHECK -m recent --set --name SSH
iptables -A SSH_CHECK -m recent --update --seconds 60 --hitcount 3 --name SSH -j DROP
iptables -A SSH_CHECK -m recent --update --seconds 3600 --hitcount 11 --name SSH -j DROP

iptables -A INPUT -p tcp -s 1.2.3.4 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j SSH_CHECK

Als erstes wird aufgeräumt, falls es Hinterlassenschaften gibt. Dann wird die SSH_CHECK Chain angelegt. Die erste Regel sagt, dass die IP-Adresse aller Datenpakete, die in diese Chain geleitet werden in die IP-Liste aufgenommen werden sollen. Regel zwei und drei schauen dann in dieser Liste nach, und wenn innerhalb der letzten 60 Sekunden drei oder mehr Hits existieren oder innerhalb der letzten Stunde zehn oder mehr, dann wird das Datenpaket einfach gedropt.

In der INPUT Chain legen wir dann noch einen Eintrag an, in diesem Fall meine eigene statische IP Adresse, von welcher ich Datenpakete direkt akzeptieren. Der nächste Eintrag schickt Datenpakete, die vom State NEW sind (also SYN-Pakete) und am Port 22 ankommen an die SSH_CHECK Chain, die wir oben angelegt haben.

Mit zehn versuchen pro Stunde macht Brute Force keinen Spaß ;-)

aka rsync FTW


Mit schöner Regelmäßigkeit suche ich nach der Lösung für ein altes Problem. Ich hab einen Dateitransfer per SCP angefangen und irgendwann steht da nur noch "stalled", aka "Nichts geht mehr".

Besonders ärgerlich ist das, wenn schon ein Großteil der Datei übertragen ist. Da kommt rsync ins Spiel:

# rsync --partial --progress -e "ssh -p 443" user@remotehost:/path/to/file localfile

rsync nimmt dann den Datentransfer wieder an der Stelle auf, wo er vorher abgebrochen ist :-)

Vor einiger Zeit habe ich darüber geschrieben, wie man mit KVM und LVM virtuelle Server unter Linux aufsetzen kann. Jetzt ist passiert, was früher oder später passieren musste: Die 10 GB Speicher reichen nicht mehr für die eine Maschine, also muss ich das nun aufbohren.

Dabei muss ich auf einige Dinge achten, das System ist recht komplex aufgebaut, ich haben ein LVM-Volume, in welchem eine virtuelle Festplatte steckt (komplett mit Partitionstabelle) und darin dann das Filesystem.

Erster Schritt: Du musst die VM runterfahren, danach das LVM-Volume vergrößern:

$ lvextend -L+5G /dev/vg0/vm02-clone 
  Extending logical volume vm02-clone to 15,00 GiB
  Logical volume vm02-clone successfully resized

Jetzt wird es heikel, denn du musst die alte Partition löschen und eine neue anlegen, die am selben Sektor anfängt. Hierfür solltest du fdisk mit dem -u Flag nutzen, weil es dann nicht auf Zylinder, sondern auf Sektoren arbeitet, dass macht das ganze leichter - um nicht zu sagen überhaupt erst möglich.

$ fdisk -u /dev/vg0/vm02-clone 

WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
         switch off the mode (command 'c').

Command (m for help): p

Disk /dev/vg0/vm02-clone: 16.1 GB, 16106127360 bytes
255 heads, 63 sectors/track, 1958 cylinders, total 31457280 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00070d2c

              Device Boot      Start         End      Blocks   Id  System
/dev/vg0/vm02-clone  1          2048    20971519    10484736   83  Linux

So sieht das bei mir aus, sollte bei dir dann auch so ähnlich sein. Start ist typischerweise bei Sektor 2048. Hier siehst du, dass die Platte in dem LVM-Volume 31457280 Sektoren hat, die Partition aber nur bis 20971519 geht. Um die Partition nun zu vergrößern musst du diese löschen und eine neue anlegen, die am selben Sektor beginnt. Die Daten auf der Partition werden nicht verändert, lediglich die Partitionstabelle, deswegen funktioniert der Trick.

Partition löschen:

Command (m for help): d
Selected partition 1

Neue Partition anlegen

Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 1
First sector (63-31457279, default 63): 2048
Last sector, +sectors or +size{K,M,G} (2048-31457279, default 31457279): 
Using default value 31457279

Ganz wichtig: Primäre Partition, Nummer 1, Erster Sektor: 2048 (hier nicht das default nutzen, sondern exakt den Wert, der oben bei der Auflistung unter Start stand) und letzten Sektor auf das höchste einstellen, muss auf jeden Fall höher sein, als was vorher da Stand.

Noch kurz prüfen, ob die Partitionstabelle nun passt:

Command (m for help): p

Disk /dev/vg0/vm02-clone: 16.1 GB, 16106127360 bytes
255 heads, 63 sectors/track, 1958 cylinders, total 31457280 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00070d2c

              Device Boot      Start         End      Blocks   Id  System
/dev/vg0/vm02-clone1            2048    31457279    15727616   83  Linux

Dann die Partitionstabelle speichern und fdisk beenden

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 22: Das Argument ist ungültig.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.

Die letzte Warnung kannst du vergessen, weil dein Kernel im Host System eh nicht weiß, dass da eine Platte drin steckt, nur der Gast interessiert sich dafür und der ist ja schon aus, wenn du den dann neu bootest, wird der die neue Partitionstabelle sauber einlesen.

Jetzt kannst du die VM wieder starten, wenn du alles richtig gemacht hast, fährt diese einfach wieder hoch. Das Schlimmste hast du hinter dir, du musst nämlich nur noch das Dateisystem anpassen. Um zu prüfen, ob die neue Partitionstabelle auch angekommen ist, kannst du nochmal fdisk -u starten und dir die Partitionstabelle anzeigen lassen, da müssen die gleichen Zahlen stehen wie oben, kurz bevor du die Partitionstabelle geschrieben hast.

Ob das mit dem vergrößern des Dateisystems einfach wird oder nicht, hängt primär von deiner Wahl des Dateisystems ab. Ich hatte mich anno dazumal für EXT3 entschieden. Der Vorteil davon ist, dass EXT3 on-line resizing kann. In meinem Fall also:

$ resize2fs /dev/vda1

resize2fs 1.41.12 (17-May-2010)
Filesystem at /dev/vda1 is mounted on /; on-line resizing required
old desc_blocks = 1, new_desc_blocks = 1
Performing an on-line resize of /dev/vda1 to 3931904 (4k) blocks.
The filesystem on /dev/vda1 is now 3931904 blocks long.

Fertig ist die Laube :)

Kurzer Tipp noch: Wenn du schon ein solches System aufgesetzt hast, solltest du erstmal die VM pausieren, dann einen Klone davon erzeugen und mit dem das ganze durchspielen, so kannst du sehen, ob es klappt. Wie immer gilt natürlich: Backup machen.

TL;DR

  • VM runterfahren
  • am Host einloggen
  • $ lvextend -L+5G /dev/vg0/vm02-clone
  • $ fdisk -u /dev/vg0/vm02-clone
  • Alte Partition löschen, neue anlegen, auf selben Startsektor achten!, Partitionstabelle speichern
  • VM wieder hochfahren
  • in der VM abhängig vom Dateisystem: resize2fs /dev/vda1