Virtualisierung mit LXC

Aus LinuxUser 06/2012

Virtualisierung mit LXC

© Patrick Dignard, sxc.hu

Sandkastenspiele

Mithilfe von LXC lagern Sie mit wenigen Handgriffen unsichere Applikationen oder Testsysteme einfach in einen virtuellen Container aus.

Der Markt für Software zum Virtualisieren wächst seit Jahren stetig. Namen wie VMware, Virtualbox, Xen, Qemu oder KVM sind jedem ernsthaften Anwender geläufig. Mit den Linux Containers (LXC, [1]) gesellt sich der Gattung ein noch recht junger Vertreter hinzu, der einige Annehmlichkeiten bietet.

Das System gleicht dabei in etwa den sogenannten Jails unter BSD oder den Containern unter Solaris. Im laufenden System, dem Host, richten Sie damit für den Gast einen abgeschotteten und gesicherten Bereich ein, innerhalb dessen Sie einzelne Dienste oder ganze Systeme virtualisieren. Im LXC-Jargon heißt diese Umgebung wie bei Solaris Container.

Im Vergleich zu vielen anderen Lösungen arbeitet LXC dabei direkt auf der Ebene des Betriebssystems. Während VMware oder Virtualbox einen vollständigen PC samt BIOS und Hardware emulieren und sich daher ideal zum Installieren von anderen Betriebssystemen eignen, nutzt LXC die bereits vorhandene Hardware und den bestehenden Kernel, um entsprechende Container bereitzustellen.

Dieses Verfahren spart durch das Weglassen zahlreicher Abstraktionsebenen viele Ressourcen. Dafür ermöglicht es aber nur ein “Linux im Linux”, denn es stellt keine virtuelle Maschine bereit, sondern lediglich eine virtuelle Umgebung. Möchten Sie beispielsweise einen Windows-Gast innerhalb eines Linux-Hosts virtualisieren, müssen Sie auf andere Lösungen ausweichen [2].

LXC eignet sich sowohl für Umgebungen, die aus Sicherheitsgründen getrennte Instanzen mit eigenen Ressourcen erfordern, als auch für Bereiche, in denen es eine gewisse Flexibilität zu gewährleisten gilt. Bei Bedarf ziehen Sie einen Container mit wenigen Handgriffen auf einen anderen Host um. Der Ansatz eignet sich unter anderem für den Einsatz bei Providern, die kostengünstig virtuelle Server bereitstellen wollen, sowie für Software-Entwickler, die in definierten Umgebungen arbeiten müssen.

LXC bringt aber auch zwei Nachteile mit sich: Zum einen steigt durch das Virtualisieren auf Betriebssystemebene das Risiko, dass virtuelle Hosts bei Sicherheitslücken im Kernel oder bei schlechter Konfiguration einen Zugang zum eigentlichen Host erhalten. Zum anderen fallen das erste Einrichten und die Konfiguration bisweilen noch recht schwer.

Das Testsystem

Als Testsystem für diesen Artikel nutzten wir Ubuntu 11.10 “Oneiric Ocelot” in der 64-Bit-Variante für Server. Als Hardware diente ein Root-Server eines deutschen Hosters, ausgestattet mit Intel-Xeon-Prozessor.

Vorarbeiten

Einer der großen Vorteile von LXC liegt darin, dass neuere Kernel-Versionen (in der Regel ab 2.6.31) es direkt integrieren – ein aufwändiges Übersetzen von Modulen oder gar Patchen des Systems entfällt. Die Inbetriebnahme erfordert lediglich drei Schritte: Das Einrichten der Programme zum Verwalten (also der Tools, mit denen Sie die Container erstellen und bearbeiten), das Einrichten eines solchen Containers, und zu guter Letzt das Konfigurieren der Netzwerkanbindung.

Vor der eigentlichen Installation von LXC stellen Sie zunächst sicher, dass Sie das Host-System ausreichend abgesichert haben. Soll die virtuelle Maschine später online gehen, empfiehlt es sich, eine zusätzliche IPv4-Adresse bereit zu halten. Diese schalten Sie zwar nicht auf den Host selbst auf, haben sie aber idealerweise beim Einrichten der virtuellen Umgebung bereits zur Hand.

Sind diese Voraussetzungen erfüllt, installieren Sie die eigentlichen Pakete über die Kommandzeile mittels sudo apt-get install lxc cgroup-lite debootstrap libcap2-bin libvirt-bin bridge-utils. Anschließend zeigt der Befehl lxc-checkconfig, ob das System alle Anforderungen erfüllt.

Schutzverpackung

LXC kennt zwei Betriebsmodi: Das Virtualisieren eines einzelnen Programms wie etwa einer Bash-Shell oder aber die Installation eines kompletten Basissystems in einem Container. Ersteres eignet sich unter anderem dazu, um einzelne Dienste und Programme in einer gesicherten Umgebung (“Sandbox”) zu starten. Letzteres erweist sich insbesondere dann als ideal, wenn Sie verschiedene virtuelle Server planen.

Der Start eines einzelnen Programms innerhalb eines Containers zeigt sehr schön das Prinzip hinter LXC. Rufen Sie am Host-System den Befehl w auf, erhalten Sie – gesetzt den Fall, dass Sie keinen speziell präparierten Sicherheits-Kernel einsetzen – eine Übersicht aller angemeldeten Nutzer (Listing 1, oben). Zudem fördert ein beherztes ps auxw alle Prozesse zutage, die insgesamt auf dem System aktiv sind (Listing 1, unten).

Listing 1

$ w
 13:37:48 up 2 days,  1:36,  2 users,  load average: 0,02, 0,03, 0,05
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
floeff   pts/0    188-1-2-3-dynip. 13:31   23.00s  0.29s  0.29s -bash
mmuster  pts/16   188-4-5-6-dynip. 13:37    0.00s  0.27s  0.00s w
$ ps auxw
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
proftpd    561  0.0  0.0  98312  2128 ?        SNs  Feb19   0:00 proftpd: (accepting connections)
root       810  0.0  0.0  47272  9560 ?        Ss   Feb18   0:06 /usr/sbin/munin-node
daemon    1572  0.0  0.0  16776   380 ?        Ss   Feb18   0:00 atd
polw      1713  0.0  0.0  55104 12848 ?        Ss   Feb18   0:00 policyd-weight (master)
postfw    1718  0.0  0.0  62436 15756 ?        Ss   Feb18   0:03 /usr/sbin/postfwd
clamav    2005  0.0  0.8 195516 137080 ?       Ssl  Feb18   0:11 /usr/sbin/clamd
...

Nun geht es an das Einrichten einer virtuellen Instanz. Starten Sie nun (dazu benötigen Sie administrative Rechte) unter Zuhilfenahme des Befehls

# lxc-execute -n RestrictedShell /bin/bash

einen Container namens RestrictedShell, der nichts anderes macht, als /bin/bash aufzurufen. Sie befinden sich anschließend direkt auf der Kommandozeile dieser LXC-Instanz.

Der Container (der Gast) erbt vom System (dem Host) nahezu die gesamte Umgebung. Dazu zählen unter anderem das Dateisystem samt Benutzern, Gruppen und Quotas, der Kernel samt zugehöriger Module sowie der Zugriff auf die Hardware. Dennoch bleibt der Container in weiten Teilen vom übrigen System isoliert, wie ein Blick auf die vollkommene leere Übersicht beweist, die der Befehl w zutage fördert (Listing 2, oben). Ein ps auxw zeigt ebenfalls nicht gerade viel (Listing 2, unten).

Listing 2

# w
 14:24:47 up 2 days,  2:23,  1 user,  load average: 0,03, 0,11, 0,08
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
# ps auxw
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0  10496   632 pts/0    S    14:16   0:00 /usr/lib/lxc/lxc-init -- /bin/bash
root         2  0.0  0.0  30372  5552 pts/0    S    14:16   0:00 /bin/bash
root        70  0.0  0.0  22060  1244 pts/0    R+   14:25   0:00 ps auxw

Noch deutlicher erscheint das Ganze im Vergleich der Ausgabe von Htop: Der Container (Abbildung 1) sieht die Prozesse des Hosts (Abbildung 2) nicht. An dieser Stelle beenden Sie das Experiment durch die Eingabe von exit, um wieder zur Kommandozeile des physikalischen Systems zu gelangen.

Abbildung 1: Im Container zeigt Htop eine deutlich verkürzte Prozessliste …

Abbildung 1: Im Container zeigt Htop eine deutlich verkürzte Prozessliste …

Abbildung 2: … auf dem Host dagegen sehen Sie die vollständige Prozessliste.

Abbildung 2: … auf dem Host dagegen sehen Sie die vollständige Prozessliste.

Aller Anfang ist schwer

Als wesentlich vielseitiger und zudem sicherer erweist sich allerdings die Installation eines kompletten Basissystems im Container. Das grundlegende Prinzip gleicht dabei jenem des vorherigen Versuchs: Einen Teil des Systems teilen sich Container und Host, der Rest bleibt isoliert in der virtuellen Umgebung. Dazu erzeugen Sie einen Container, der ein komplett eigenes Dateisystem erhält, und somit ein eigenes Basissystem samt Benutzern und eigener IP-Adresse.

Als Dreh- und Angelpunkt dient dabei das Tool lxc-create (siehe Tabelle “LXC-Befehle”). Es greift auf sogenannte Vorlagen zurück, welche die Installation eines frischen Systems beschreiben. Auf unserem Testsystem hält das Verzeichnis /usr/lib/lxc/templates unter anderem Vorlagen für Debian, Fedora, OpenSuse und Ubuntu als virtualisierte Umgebung bereit, wobei im Test Letzteres zum Einsatz kam.

LXC-Befehle

Befehl Funktion
lxc-execute startet eine einzelne Applikation in der Sandbox
lxc-start startet einen kompletten Container
lxc-stop erzwingt das Beenden des Containers (nicht empfohlen)
lxc-destroy löscht einen Container samt aller enthaltenen Dateien
lxc-netstat führt den Befehl netstat im Container aus
lxc-ps listet Prozesse eines Containers auf
lxc-monitor überwacht den Status eines Containers
lxc-ls listet alle verfügbaren Container auf
lxc-info zeigt den Status eines Containers an
lxc-freeze hält einen Container an
lxc-unfreeze setzt einen Container fort

Zum Erstellen eines Containers für den Host vm1.domain.local legen Sie im Verzeichnis /etc/lxc eine Konfigurationsdatei gleichen Namens an und füllen diese mit dem Inhalt aus Listing 3. Damit weisen Sie der Maschine eine IPv4-Adresse mit entsprechender Netzmaske zu (Zeile 2), aktivieren diese und koppeln sie an das Netzwerkinterface eth0 (Zeile 3). Die Netzmaske hängt von der lokalen Konfiguration ab – im Test war /27 Voraussetzung beim Provider. Für Verbindungen zur Außenwelt nutzt der Container eine sogenannte virtuelle MAC-Adresse (Zeile 4), ebenfalls eine Vorgabe des Providers.

Listing 3

lxc.network.type=macvlan
lxc.network.ipv4=1.2.3.4/27
lxc.network.link=eth0
lxc.network.hwaddr=00:11:22:33:44:55
lxc.network.flags=up

Mit dieser Minimalkonfiguration erstellen Sie den Container unter Zuhilfenahme des schon bekannten Tools. Der Befehl

# lxc-create -t ubuntu -n vm1.domain.local -f /etc/lxc/vm1.domain.local.conf

installiert das Basissystem, das der Host unter /var/lib/lxc/vm1.domain.local/rootfs abbildet. Um den Container im Test tatsächlich ans Netz zu bekommen, brauchte es allerdings noch etwas Handarbeit.

Zum einen galt es, die nötigen Netzwerkrouten zu setzen. In vielen anderen Netzwerken entfällt diese Aufgabe. Übertragen Sie die Inhalte aus Listing 4 in die Datei /etc/network/interfaces im Container, die entsprechend als /var/lib/lxc/vm1.domain.local/rootfs/etc/network/interfaces am Host vorliegt.

Listing 4

auto lo
iface lo inet loopback
auto eth0
iface eth0 inet manual
up route add default gw 1.2.3.1
up route add -net 1.2.3.0 netmask 255.255.255.224 gw 1.2.3.1 eth0

Ganz gleich, ob beim Hoster oder im Heimnetzwerk – der Container benötigt zum Auflösen der Namen noch Zugriff auf einen DNS-Server, den Sie einfach in der Datei /etc/resolvconf/resolv.conf.d/head im Container eintragen. Das entspricht der Datei /var/lib/lxc/vm1.domain.local/rootfs/etc/resolvconf/resolv.conf.d/head am Host. Der Eintrag nameserver 8.8.8.8 bindet beispielsweise den Google-Nameserver ein.

Der erste Start

Nach diesen Konfigurationsarbeiten steht der Container für erste Experimente bereit. Sie starten dazu die virtuelle Maschine im Vordergrund über den Befehl

# lxc-start -n vm1.domain.local

Die Konsole zeigt also das Login des Containers an. Melden Sie sich dort als Benutzer root mit gleichlautendem Passwort an, und ändern Sie letzteres sodann mittels passwd.

Als nächstes stellen Sie sicher, dass der Zugriff auf das Netzwerk funktioniert: Ein Ping auf eine externe Seite wie www.linux-user.de und der Versuch, sich von außen per SSH auf die neue Maschine einzuloggen, sorgen für Klarheit.

TIPP

Im geschilderten Setup greift die Firewall des Host-Systems in der Regel nicht – der Container ist also völlig ungeschützt. Schnelle Abhilfe schafft beispielsweise die Installation von UFW und das Sperren nicht benötigter Ports im Gast.

Derzeit läuft die virtuelle Maschine noch im Vordergrund. Funktioniert alles wie gewünscht, beenden Sie den Container mittels halt. Achten Sie darauf, den Befehl im Terminal des Containers einzugeben. Dann starten Sie ihn erneut, diesmal aber mittels:

# lxc-start -d -n vm1.domain.local

Das zusätzliche -d sorgt dafür, dass LXC nun im Hintergrund läuft, im sogenannten Detached- oder Headless-Modus. Alles weitere richten Sie jetzt per SSH ein.

Spitze des Eisbergs

Der Gast steht jetzt zum Einsatz bereit. Es bestünde nun die Möglichkeit, ihn nach entsprechendem Anpassen der Konfigurationsdatei beliebig oft zu klonen. Verfügen Sie über drei weitere IP-Adressen, dann kopieren Sie dazu einfach die Konfiguration des Containers in /etc/lxc, passen jeweils die Angaben zum Netzwerk an, und erzeugen die gewünschte Anzahl an Containern mit lxc-create (siehe Kasten “Das doppelte Lottchen”).

Das doppelte Lottchen

Nach Anlegen des Containers befindet sich dessen Konfiguration unterhalb von /var/lib/lxc. Um nachträglich Parameter anzupassen, müssen Sie diese direkt dort ändern – die anfangs angelegte Datei in /etc/lxc dient nur als Vorlage zum Erstellen des Gasts.

Auf dem Testsystem galt es allerdings im Container noch einige weitere Anpassungen vorzunehmen – insbesondere die Datei /etc/hosts war unvollständig. Das Muster in Listing 5 kann als Beispiel für eigene Ergänzungen dienen. Zudem fehlten in unserem Template die Verzeichnisse /run/lock und /run/shm, was unter Umständen zu Fehlermeldungen führt (und vermutlich auf einen Bug zurückzuführen ist).

Listing 5

127.0.0.1 localhost
1.2.3.4 vm1.domain.local vm1
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts

Über die Konfiguration des Containers regeln Sie bei Bedarf noch wesentlich mehr, als dieses kleine Beispiel zu zeigen vermag: So definieren Sie eigene Mountpoints und Root-Dateisysteme, bilden falls nötig komplexe Netzwerkkonfigurationen ab und weisen über die Control Groups (Cgroups) des Kernels bestimmte Berechtigungen und Ressourcen zu.

Damit sollten Sie sich spätestens dann beschäftigen, wenn Sie virtuelle Maschinen für Dritte bereitstellen. In der Ubuntu-Standardkonfiguration darf beispielsweise jede virtuelle Maschine die Systemzeit des Hosts ändern und kann dadurch für ein kleines Chaos sorgen. Abhilfe schafft in diesem Fall übrigens der Parameter lxc.cap.drop=sys_time in der Konfiguration. Ausführlichere Informationen zu allen Konfigurationsmöglichkeiten erhalten Sie mittels man lxc.conf. Weitere Informationen liefern die Dokumentation unter /usr/share/doc/lxc und das Ubuntuusers.de-Wiki [3].

Funktionieren alle Container zu Ihrer Zufriedenheit, weisen Sie LXC noch an, sie bei jedem Reboot des Hosts automatisch zu starten. Dazu setzen Sie in /etc/default/lxc den Eintrag RUN auf yes. Für jeden Gast, den Sie beim Systemstart laden möchten, legen Sie anschließend einen symbolischen Link der Konfiguration in /etc/lxc/auto an (Listing 6).

Listing 6

# ln -s /var/lib/lxc/vm1.domain.local/config /etc/lxc/auto/vm1.domain.local.conf

Fazit

Die noch recht junge Virtualisierungslösung LXC überzeugt vor allem durch ein Ressourcen sparendes Verhalten und die direkte Integration in den Kernel. Die Konfiguration fällt zwar etwas umständlich aus, eignet sich aber prinzipbedingt ideal zum Einbinden in eigene Skripte, um etwa auf Knopfdruck neue Container zu erzeugen. Falls Sie es nicht scheuen, etwas Zeit zu investieren und auf grafische Oberflächen verzichten können, dann sollten Sie unbedingt einen Blick auf LXC wagen. 

Infos

[1] LXC: http://lxc.sourceforge.net

[2] Desktop-Virtualisierung: LU 04/2012, Schwerpunkt, S. 20 bis 47, https://www.linux-community.de/Internal/Artikel/Print-Artikel/LinuxUser/2012/04

[3] Ubuntuusers.de-Wiki: http://wiki.ubuntuusers.de/LXC

Der Autor

Florian Effenberger engagiert sich seit vielen Jahren ehrenamtlich für freie Software. Er ist Chairman of the Board der Document Foundation. Zuvor war er fast sieben Jahre im Projekt OpenOffice.org aktiv, zuletzt als Marketing Project Lead.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDF
LinuxUser 06/2012 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben