Serverkonsolidierung mit Jails
Dieser Artikel beschreibt Möglichkeiten, gewachsene Strukturen mit FreeBSD-Servern auf weniger Hardware zusammenzufassen. Doch oftmals lassen sich individuell konfigurierte Systeme nicht auf einen gemeinsamen Nenner bringen. Der perfekte Anwendungsfall für Virtualisierung, in diesem Fall mit FreeBSD-Jails.
Theorie
FreeBSD Jail stellen im eigentlichen Sinn keine Virtualisierung dar sondern mehr eine Methode zum Trennen von Prozessumgebungen. Alle Prozesse, die innerhalb eines Jail-Kontextes gestartet werden, können selbst mit root-Rechten nur auf andere Prozesse und Ressourcen aus dem gleichen Jail zugreifen. Auf einem Host können viele Jails nebeneinander existieren. Prozesse, die keinem Jail angehören, sehen alle Ressourcen in allen Jails.
Ressourcen
Ein Jail verfügt typischerweise über
- Eigenes Verzeichnis, welches innerhalb des Jails als "/" erscheint
- Eigene IP-Adresse als Alias auf einem Netzwerkinterface
- Eigene Benutzerrechte (root!), mit Einschränkungen
Alle Jails und alle Prozesse ohne Jail-Kontext teilen sich:
- Arbeitsspeicher, wobei hier kein "Übergriff" stattfinden kann zwischen den Jails.
- CPU
- Kernel
- Prozesstabelle, PIDs sind systemweit eindeutig.
Aus Sicht des Kernels haben zwar alle root-User die uid=0, da Prozesse im Jail nicht aus ihrem Jail heraus zugreifen können, ist dies zu verschmerzen. Innerhalb des Jails sind daher einige Zugriffsrechte auch für root beschränkt wie etwa das anlegen von device-nodes, setzen von Filesystem-Flags, mounten von Dateisystemen, Low-Level Zugriff auf das Netzwerk (raw-sockets etc), Hardware (ACPI, ... ), shutdown der Maschine etc.
Problematisch ist in diesem Zusammenhang derzeit Shared Memory zu nennen, da es bisher nur eine maschinenweite gemeinsame Verwaltung hierfür gibt. Aus diesem Grund ist es ratsam, Shared Memory in Jails nicht zu erlauben.
Umfang eines Jails
Ein Jail kann aus n (n>=1) Prozessen bestehen. Als Bug ist anzusehen, wenn ein Jail-Kontext nach Beenden des letzten Prozesses nicht beendet wird. Grund dafür sind allozierte Ressourcen, die nicht freigegeben wurden, z.B. halboffene Netzwerkverbindungen oder geöffnete Dateien.
Statt eines Prozesses kann man natürlich auch ein komplettes Userland innerhalb eines Jails starten. Typischerweise wird der Startvorgang eines Systems vom Script /etc/rc durchgeführt:
# jail /data/jail/192.168.11.100 testhostname 192.168.11.100 /bin/sh /etc/rc
Praxis
Ausganssituation
In einer nicht näher benannten Umgebung existieren noch einige FreeBSD-Installationen, die aufgrund von Nachlässigkeit oder niedriger Priorität nie upgedatet wurden, zum einen weil es möglicherweise zu komplex gewesen wäre, zum anderen weil Sicherheit hier keine primäre Rolle spielt. Never touch a running system also.
Moderne Hardware wird immer leistungsfähiger, bestehende Systeme haben jedoch gleichbleibenden oder verminderten Ressourcenbedarf. Alte Systeme 1:1 auf moderne Hardware umzuziehen ist eine Kostenfrage, falls so alte Systeme überhaupt auf moderner Hardware laufen.
Beispiel
Im Beispiel geht es um 3 Server:
- ServerA
- FreeBSD 4.10, Webserver mit SSL-Support und eigenem Zertifikat, Apache 1.3 und PHP 4.x, Verfügbarkeitsanforderung, aber niedrige Hitrate.
- ServerB
- FreeBSD 4.9, Webserver mit SSL-Support mit SnakeOil-Zertifikat, Apache 1.3 und PHP5.0.x, Hohe Verfügbarkeitsanforderung, viele Hits, wenig Ressourcenbedarf (statischer Content). Außerdem Fremdsoftware, die für dieses System maßgeschneidert wurde mit bekannten Problemen bei Updates (PHP Inkompatiblitäten), sehr unflexibel.
- ServerC
- FreeBSD 4.7, Samba-Server mit Apache und PHP-Uralt. Für Legacy-Applikationen mit teilweise noch urzeitlichem PHP-Code, register_globals=on, schwer wartbare Software, wirr gewachsene Verzeichnisstrukturen und verstverdrahtete Abhängigkeiten auf Pfade.
Problem
Es ist nahezu ausgeschlossen, diese Systeme mit vertretbarem Aufwand auf ein System zusammenzufassen, auch wenn dies in der Theorie kein Problem darstellen würde.
Umsetzung
Vorbereiten
Ein vorhandener Jailserver, hier mit FreeBSD 6.2, wird für 3 weitere Jails vorbereitet. Der Konvention folgend werden die Verzeichnisse /data/jails/ServerA/, /data/jails/ServerB/, /data/jails/ServerC/ leer angelegt. Weiterhin müssen jailhost-spezifische Einstellungen vorgenommen werden, etwa in /etc/rc.conf, /etc/fstab.ServerX, ...
Live-Transfer
Mittels ssh "dump" | restore werden die einzelnen Filesysteme der Urserver transferiert, alternativ auch durch transfer von einzelnen dump-Files (nicht empfehlenswert).
jailhost# mkdir /data/jails/ServerA jailhost# cd /data/jails/ServerA/ jailhost# ssh root@ServerA "dump -0ua -f - / | buffer " | buffer | restore -v -rf - jailhost# cd /data/jails/ServerA/usr/ jailhost# ssh root@ServerA "dump -0ua -f - /usr | buffer " | buffer | restore -v -rf - jailhost# cd /data/jails/ServerA/var/ jailhost# ssh root@ServerA "dump -0ua -f - /var | buffer " | buffer | restore -v -rf -
Entsprechend für alle Partitionen durchführen. Ist auf dem Zielsystem FreeBSD 5.x oder neuer, dann braucht dump noch die Option "-L", um auf einem Filesystem-Snapshot zu arbeiten.
Anpassungen
- Backup von ServerA/etc durchführen
- nicht benötigte Dateien und Verzeichnisse löschen, sofern vorhanden
jailhost# cd /data/jails/ServerA/ jailhost# rm -Rf usr/src usr/ports usr/obj/usr/src; mkdir usr/ports jailhost# rm -Rf boot/ kernel* jailhost# rm -Rf dev/ ; mkdir dev
- vorhandenes rc.conf sichern und bis auf die eigentlichen Dienste (apache_enable="YES", ... ) alles auskommentieren oder besser noch löschen, in Jails benötigt man kein Hardware- und Netzwerksetup, das leistet der Jailhost.
- vorhandene /etc/fstab löschen oder alles auskommentieren
Basissystem im Jail updaten
Das heikelste folgt nun, das Update des Basis-Systems. Im aktuellen Beispiel wird von 4.10 auf 6.2 upgedatet, jedoch "offline", d.h. aus dem unter 6.2 laufenden Jailhost heraus.
Auf dem jailhost liegt unter /usr/src das aktuell laufende Release, hier z.B. die Sourcen von 6.2-RELEASE-p6 Falls diese noch nicht kompiliert wurden, diese zunächst frisch kompilieren.
jailhost# cd /usr/src jailhost# make -j2 buildworld
Einen frischen Kernel benötigen wir nicht, daher ohne "buildkernel". Die gebaute Welt kann nun in das Jail installiert werden:
jailhost# make installworld DESTDIR=/data/jails/ServerA/
Wenn das durchgelaufen ist, dann dringend mit mergemaster updaten:
jailhost# mergemaster(8) -D/data/jails/ServerA/
Das ist der zweifelsohne aufwendigste Schritt, da so ziemlich alles, aber eben nicht alles, upgedatet wird. Besonders Augenmerk sollte hier auf /etc/master.passwd, /etc/motd, /etc/mail/* und alle anderen händischen Konfigurationen gelegt werden, unproblematisch ist /etc/rc.d/, da dieses komplett ersetzt werden sollte, d.h. alte Dateien löschen, neue Dateien 1:1 installieren.
Erster Startversuch
Wenn das Jail gemäß der lokalen Konventionen formal korrekt eingerichtet ist, kann es nun gestartet werden:
jailhost# /etc/rc.d/jail start servera
Inspektion
Per jls(8) und jexec(8) können wir in das Jail einsteigen.
jailhost# jls JID IP Address Hostname Path 1 10.11.12.13 ServerA /data/jails/ServerA jailhost# jexec 1 bash ServerA# (im Jail!)
ServerA konnte erfolgreich gestartet werden, Apache läuft soweit und antwortet auf Requests.