Muss es wirklich virtuelle Hardware sein?: Linux-VServer
Um das abstrakte Thema Virtualisierung zu verstehen, schafft sich der menschliche Verstand Bilder. Eines dieser Bilder ist, dass das zu virtualisierende Betriebssystem auf einer virtuellen Hardware betrieben wird. Einige der existierenden Virtualisierungslösungen verfolgen genau diesen Ansatz: Es wird jede notwendige Komponente eines PCs virtuell nachgebildet, um darauf ein Betriebssystem laufen zu lassen und die Illusion einer eingenständigen und unabhängigen Umgebung zu schaffen.
Linux-VServer verfolgt einen grundlegend anderen Ansatz, der in der Fachliteratur gerne als „Container-based Virtualization“ bezeichnet wird. Die Idee ist nicht neu: Linux-VServer ist bereits seit 2001 frei verfügbar [1] und wer beispielsweise FreeBSD Jails oder Solaris Zones kennt, wird mit dem Konzept vertraut sein.
Innerhalb des Betriebssystems wird eine Umgebung geschaffen, in der eine Menge von Prozessen isoliert vom Rest des Systems laufen kann. Ein solcher „Container“ wird in Linux-VServer schlicht „Guest“ genannt. Einem Guest wird eine numerische Kennung (Context ID oder kurz XID) zugeordnet: Anhand dieser wird im Kernel über Virtualisierungs- und Isolationsmaßnahmen entschieden, die insbesondere die Zugriffsrechte auf das Filesystem, den Netzwerk-Stack, die Rechte innerhalb des Kernels, das CPU Scheduling und die Zuteilung von RAM zwischen konkurrierenden Guests regeln.
Linux-VServer bietet hier eine Fülle von harten und weichen Limitationsmechanismen, die für jeden Guest individuell eingestellt werden können. Es sei an dieser Stelle nochmal explizit angemerkt: Der Guest hat keinen eigenen Kernel, so wie es bei Usermode-Linux der Fall ist. Ein einziger Kernel dient allen Guests als Host. Er allein verwaltet das gesamte System und bietet dadurch im Vergleich zu anderen Virtualisierungstechniken einen deutlichen Effizienzvorteil, da grundlegende Funktionen nur ein einziges Mal verwaltet werden müssen. Beispielsweise passiert ein IP-Paket nur einen Kernel und Filesystem-Caches werden nur einmal verwaltet, anstatt pro Guest entsprechende Ressourcen in Anspruch zu nehmen.
Der Gastgeber
Linux-VServer besteht aus einem Kernel-Patch und einem Satz von Userland-Tools, um die Guests bauen, starten, stoppen und anderweitig verwalten zu können. Zusätzlich existieren Web-Frontends zum Management der Guests. Die Userland-Tools sind in allen gängigen Distributionen bereits enthalten. Ein Kernel kann mit dem Linux-VServer Patch selbst gebaut werden. Alternativ bedient man sich eines vorbereiteten Kernel-Paketes aus der Community. Viele sind auf der Projekt-Homepage [2] verlinkt. Debian Sid enthält bereits ein vorbereitetes Paket.
Die Konvertierung eines Debian Sid zu einem VServer-Host lässt sich in zwei Schritten erledigen:
# apt-get install linux-image-vserver-686 util-vserver [...] # reboot # :-)
Listing 1
Alternativ bietet Sid auch hybride Kernel mit Linux-VServer und XEN: Beide Technologien ergänzen sich wunderbar.
Zur Überprüfung des Kernels auf Tauglichkeit für VServer gibt es ein Shellskript mit dem Namen „testme.sh“. Im Falle von Debian wird dieses sogar im Paket selbst mitgeliefert und befindet sich unter „examples“ im Dokumentationsverzeichnis:
# sh /usr/share/doc/util-vserver/examples/testme.sh-0.15 Linux-VServer Test [V0.15] Copyright (C) 2003-2006 H.Poetzl chcontext is working. chbind is working. [...]
Listing 2
Die Ausgabe von „testme.sh“ darf keine Fehler anzeigen, sonst stimmt etwas mit dem Kernel nicht. Im Fehlerfall ist die Ausgabe des Skripts die beste Möglichkeit, eine präzise Problembeschreibung zu erzeugen und so Support auf der VServer-Mailingliste oder im IRC [3] zu erhalten.
Gastfreundschaft
Linux-VServer ist so robust, dass die meisten Distributionen als Guest unverändert lauffähig sind. Selbstverständlich können verschiedene Distributionen als Guest nebeneinander auf einem Host laufen. Dabei wird die Installation verschiedener Guest-Distributionen direkt von util-vserver unterstützt: Als Installationsmethoden kennt das Commandline-Tool „vserver“ unter anderem skeleton, debootstrap und yum [4]. Es ist ratsam, die aktuellste Version der util-vserver zu verwenden. Debian Sid ist diesbezüglich vorbildlich organisiert. Der eigentliche Build-Vorgang eines Guests ist ein Einzeiler. Je nach Komplexität des Guest-Setups kann die Zeile jedoch recht lang werden. Die Parameter können später im Konfigurationsverzeichnis nachgetragen werden, um jedoch schnell und einfach in den Genuss eines funktionierenden Guests zu kommen, kann wie folgt vorgegangen werden:
root@derjohn:~# vserver testme build -m debootstrap --netdev=eth0 --interface=192.168.123.123/24 --hostname testme --context 666 -- -d etch
Listing 3
Die Parameter sind weitgehend selbsterklärend. Als XID kann eine Zahl von 2 bis 49151 gewählt werden. Mit der Doppelminus-Konstruktion dürften nur erfahrene Administratoren vertraut sein: Sie ist keine Erfindung von Linux-VServer, sondern ein Verfahren, um auf einer Kommandozeile mehrere Mengen von Parametern angeben zu können. Im Beispiel oben wird der Parameter „-d etch“ an die die Option „-m debootstrap“ weitergegeben.
Ist der Build-Vorgang abgeschlossen, kann man den Guest starten und tiefer in den Context eintauchen.
root@host:/# vserver testme start [...] root@host:/# vserver testme enter root@testme:/# hostname testme
Listing 4
Fallstricke
Ist der erste Guest aufgesetzt, kann dieser nach Belieben konfiguriert und Software nachinstalliert werden. Einer der gängigsten Fallstricke entsteht erfahrungsgemäß bei der Installation von SSH: Die Installation scheint reibungslos zu laufen, wenn man jedoch mittels SSH eine Shell im Guest öffnen möchte, landet man auf der Shell des Hosts, nicht auf der Shell des Guests. Das hat einen vollkommen logischen Grund: Die dem Guest zugeordnete IP wird mit dem Start des Guests auf dem Kernel (auf dem Host) hochgefahren. Dieser unterliegt keinen Restriktionen. Ein auf dem Host laufender SSH-Dienst wird folglich auf Port 22 unter allen verfügbaren IP-Adressen lauschen, sofern er nicht auf eine spezifische Adresse eingeschränkt wird. Bei OpenSSH wird dies durch die Option „Listen“ in der sshd_config erreicht. Dieser Fallstrick gilt selbstverständlich für alle etwaigen Dienste, die auf dem Host laufen, jedoch ist der Betrieb von Diensten auf dem Host selbst im Allgemeinen nicht zu empfehlen. Letztendlich sollte das Konzept eines Hosting-Systems mit Linux-VServer beinhalten, alle Dienste in Guests zu packen. Dies erhöht die Sicherheit des Gesamtsystems erheblich. Jedoch gibt es eine Reihe von Diensten, die innerhalb eines Guests als problematisch bekannt sind. Eine ganze Klasse von ihnen arbeiten mit dem Linux Capability System. Capabilities sind eine Menge von besonderen Rechten, die einem Prozess entzogen werden können [5]. Die Idee dahinter ist, dass ein Prozess beim Start alle nicht benötigten Capabilities aufgibt. Sind diese einmal aufgegeben, können die Rechte innerhalb des Prozesses nicht wiedererlangt werden, was im Falle eines Einbruchs in diesen Prozess den Rest des Systems schützt. Beispielsweise ist der „raw access“ auf Netzwerkinterfaces eine Capability. Ist diese entzogen, kann innerhalb des Prozesses auf dem Interface nicht mehr „gesnifft“ werden, auch wenn der Prozess von root ausgeführt wird. Da Linux-VServer den Guest standardmäßig sicher konfiguriert, werden dem gesamten Guest alle „gefährlichen“ Capabilities entzogen. Möchte sich ein Dienst in einem Guest seinerseits Capabilities sichern, wird der Kernel ihm dies negativ quittieren, da die Capability bereits entzogen ist. Schlecht implementierte Dienste werten dies als Fehler und starten nicht. Beispiele hierfür sind Bind9 und Pure-FTPd. Eine mögliche Lösung ist das Übersetzen der Dienste ohne Unterstützung für Capabilities, die meisten bieten eine Option dazu im Skript „./configure“ an. Eine andere Möglichkeit ist, dem Guest vor dem Start die entsprechenden Capabilities zu geben, wovon aus Sicherheitsgründen abzuraten ist. Dies erfolgt in der Datei „bcapabilities“ [6] im Konfigurationsverzeichnis des Guests. Die Experimental-Quellen des Devel-Branches von Linux-VServer enthalten bereits ein neues Feature, das dem Guest die entsprechenden Capabilities vorgaukelt, sodass die Dienste ohne zusätzliche Maßnahmen starten.
Wenn die IP-Adressen knapp sind
Gerade im Testbetrieb oder auf gemieteten Servern beim Provider kommt es häufig vor, dass mehrere Guests auf einer Maschine mit IP-Adressen aus einem privaten Bereich (RFC1918, beispielsweise 192.168.0.x) betrieben werden. Da Linux-VServer die Netfilter-Funktionalität des Kernels aus Performancegründen nicht virtualisiert hat, lässt sich „iptables“ innerhalb von Guests nicht nutzen. Die NAT-Regeln müssen demzufolge auf dem Host gesetzt werden. Das Kommando kann etwa so aussehen:
# iptables -t nat -I POSTROUTING -s 192.168.0.0/24 ! -d 192.168.0.0 -j SNAT --to %%externe_IP_des_Hosts%%
Listing 5
Je nach Einsatzzweck bietet sich auch eine andere Lösung an: Linux-VServer erlaubt es, mehrere Guests mit derselben IP zu starten. Es ist also durchaus zulässig, die (einzige) IP eines dedizierten Servers mehreren Guests zuzuordnen – es darf eben nur jeweils ein Guest auf einem Port lauschen. Ein solches Setup könnte zum Beispiel sein, eine „Apache-Umgebung“ in einem Guest auf Port 80 und Postfix in einem anderen Guest auf Port 25 laufen zu lassen. Wichtig hierbei ist, auf den Guests das Flag „nodev“ zu setzen, damit das System nicht versucht, die IP mehrfach zu konfigurieren – der Host hat dies beim Start ja bereits erledigt.
Performance und Ressourcen
Häufig wird die Frage nach den Performance-Einbußen gestellt, die vermeintlich durch Linux-VServer entstehen können. Diese Frage wird bei allen Virtualisierungslösungen diskutiert [7] und kann hier nicht abschließend geklärt werden. Sie hängt auch stark mit der Frage nach passenden Benchmarks und Nutzungsprofilen der Guests zusammen. Messungen in der VServer-Community haben ergeben, dass die Einbuße an Performance eines Systems als Guest im Vergleich zur Performance desselben Systems ohne VServer im Stable-Branch bei weniger als einem Prozent liegt, im Devel-Branch ist nahezu keine Einbuße messbar. Ein interessantes Detail ist, dass mit zunehmender Anzahl von Guests auf einem Host im Vergleich zum Ressourcenbedarf der einzelnen Guests eine gewichtete Performance von mehr als 100 Prozent erreichbar ist. Dies kann aufgrund von gemeinsam genutzten Ressourcen wie Caches oder gar einem gemeinsam genutzten Root-Verzeichnis für die Guests erreicht werden: Bei den Unification beziehungsweise Hashification genannten Verfahren können alle Guests dieselbe Referenzinstallation einer Linux-Distribution verwenden und nur die Unterschiede pro Guest belegen auf dem Filesystem wirklich Platz. Auch ein globales Update der Referenzinstallation ist so möglich, ohne alle Guests einzeln updaten zu müssen. Dies ist ein deutlicher Unterschied zu anderen Virtualisierungstechniken. Natürlich treten bei vielen konkurrierenden Guests auch Engpässe bei den Ressourcen auf.
Der Ressourcenbedarf der Guests lässt sich mit „vserver-stat“ feststellen und im proc-Filesystem finden sich unter „/proc/virtual“ noch detaillierte Kennzahlen.
# vserver-stat CTX PROC VSZ RSS userTIME sysTIME UPTIME NAME 0 51 271.8M 33.2M 29m44s27 18m25s99 3d14h53 root server 121 6 17.1M 4.3M 0m01s63 0m01s84 3d14h53 bastel 123 14 85.8M 27.8M 10m17s30 35m44s71 3d14h53 jabber 127 32 380.1M 151M 4h48m24 9m06s77 3d14h52 vubuntu
Listing 6
Um hier passende Limits einzustellen, sind Kenntnisse vom Aufbau eines Betriebssystems nötig. Schließlich kann mit Linux-VServer die Zuteilung der meisten Ressourcen sehr feingranular eingestellt werden. Wem Begriffe wie VSZ, RSS, cpusets und Jiffies nicht fremd sind, wird sich sicher schnell zurecht finden.