Aus Linux-Magazin 11/2015

LXC-1.0-Workshop

© Fotograf, 123RF.com

Mit LXC 1.0 erschien Anfang 2014 die erste stabile Version zur Verwaltung von Linux-Containern. Ist die leichtgewichtige Container-Lösung mittlerweile produktionsreif? Der Workshop macht den Selbstversuch.

Seit Kernel 2.6.29 lassen sich Linux-Container direkt mit einem Mainline-Kernel nutzen. Container gab es allerdings mit Virtuozzo [1] und Open VZ [2] unter Linux bereits lange vorher. Der Unterschied besteht darin, dass der Linux-Kernel heute alle notwendigen Komponenten zum Betrieb von Containern im Gepäck hat und keine Patches mehr benötigt. Kernel-Namespaces isolieren die Container untereinander, Cgroups limitieren die Ressourcen und kümmern sich um die Prioritäten.

Solide Basis

Seit Februar 2014 gibt es auch eine erste stabile Version des Userspace-Tools LXC [3], mit dem Admins Container verwalten. Ubuntu 14.04 hat LXC 1.0 an Bord, das die Entwickler als stabile Version empfehlen und für das sie bis April 2019 Support leisten (Abbildung 1). Wollen Sie LXC unter Ubuntu Trusty installieren, greifen Sie am besten zu den Server-Images der Version 14.04.1 [4]. Der hier eingesetzte Kernel 3.13 erhält fünf Jahre LTS-Support, auf die Kernel neuerer LTS-Upgrades trifft das nicht zu. Nach der Installation holen Sie die LXC-Tools nach:

apt-get install lxc

Dadurch landen unter anderem die Pakete aus Tabelle 1 auf Ihrem Rechner.

Tabelle 1

LXC-Pakete

Paketname

Einsatzzweck

»bridge-utils«

Erzeugt das virtuelle Netzwerkdevice (»veth« ).

»cgmanager«

Ermöglicht die Cgroup-Verwaltung über ein D-Bus-Interface.

»debootstrap«

Hilft beim Installieren Debian-basierter Container.

»dnsmasq-base«

Ermöglicht ein minimales Out-of-the-Box-NAT-Netzwerk für Container (»lxcbr0« ).

»liblxc1«

Das neue LXC-API.

»lxc-templates«

Templates, die einfache Container erstellen.

Der erste Container

Nach einer erfolgreichen Installation erstellen Sie den ersten Container mit nur einem schlichten Kommando:

lxc-create -t ubuntu -n ubuntu_test

Vergessen Sie nicht, zuvor Rootrechte zu erlangen, mit denen Sie die meisten der im Artikel vorgestellten Befehle ausführen müssen. Rufen Sie »lxc-create« anschließend erstmalig für eine bestimme Distribution auf, muss das Hostsystem die erforderlichen Pakete einmal herunterladen. Es speichert sie in »/var/cache/lxc/« zwischen. Geben Sie den Befehl »lxc-create« erneut ein, legt das System innerhalb von ein paar Sekunden einen Container an.

Abbildung 1: Über ein Webpanel auf Flask-Basis lassen sich die Container mit Hilfe der VM auf der Heft-DVD verwalten.

Abbildung 1: Über ein Webpanel auf Flask-Basis lassen sich die Container mit Hilfe der VM auf der Heft-DVD verwalten.

Tippen Sie »lxc-create -t ubuntu -h« ein, zeigt LXC Template-spezifische Optionen. Mit ihrer Hilfe wählen Sie etwa die Debian- oder Ubuntu-Release oder einen speziellen Mirror. Einen Container mit Debian Wheezy als Grundgerüst erzeugt der folgende Aufruf:

lxc-create -t debian -n debian_test -- -r wheezy

Standardmäßig legt Ubuntu als Wirtssystem Container direkt im bereits vorhandenen Dateisystem unter »/var/lib/lxc/Container-Name« ab. Im jeweiligen Container-Unterverzeichnis warten zumindest eine Datei namens »config« sowie ein Verzeichnis namens »rootfs« . Bei vielen Distributionen kommt noch die Datei »fstab« hinzu, die Mountpunkte für die Container verwaltet.

Wie der Name nahelegt, konfiguriert LXC 1.0 den Container mit Hilfe der »config« -Datei. Die Container-Verwaltung erlaubt zudem den Einsatz von Includes in der Konfiguration (über »lxc.include« ), was sehr minimalistische Standard-Konfigurationen für Container möglich (Abbildung 2) macht.

Abbildung 2: Die Standardkonfiguration für einen Ubuntu-Container.

Abbildung 2: Die Standardkonfiguration für einen Ubuntu-Container.

Befindet sich der Container auf der Festplatte, informiert Sie das Hostsystem darüber, wie der Standardlogin für Ubuntu (User: »ubuntu« , Passwort: »ubuntu« ) respektive Debian (User: »root« und Passwort: »root« ) lautet.

Sie aktivieren den Container anschließend mit dem folgenden Kommando, wobei die Option »-d« sicherstellt, dass er im Hintergrund startet:

lxc-start -n debian_test -d

Analog dazu hält das Kommando »lxc-stop« den Container wieder an. In der Regel schickt LXC hierbei ein »SIGPWR« -Signal an den Initprozess, der den Container sauber herunterfährt. Mit Hilfe der Option »-k« erzwingen Sie das Herunterfahren.

Weil sie keine eigenen Kernel benötigen, starten Container in der Regel innerhalb von wenigen Sekunden. Über »lxc-console -n debian_test« landen Sie beim Loginprompt. Alternativ bringt Sie ein »lxc-attach -n debian_test« ohne Passwortabfrage direkt auf die Konsole. Das Kommando »lxc-ls –fancy« gibt einen guten Überblick über die vorhandenen Container (Listing 1).

Listing 1

Vorhandene Container anzeigen

01 root@ubuntu:/var/lib/lxc# lxc-ls --fancy
02 NAME          STATE    IPV4        IPV6  AUTOSTART
03 --------------------------------------------------
04 debian_test   RUNNING  10.0.3.190  -     NO
05 debian_test2  STOPPED  -           -     NO
06 ubuntu_test   STOPPED  -           -     NO

Soll ein bestimmter Container beim Start des Basissystems automatisch hochfahren, aktivieren Sie dies in der Container-Konfiguration (Abbildung 2):

lxc.start.auto = 1
lxc.start.delay = 0

Wie im Listing zu sehen ist, lässt sich an dieser Stelle auch eine Startverzögerung integrieren.

Brücken bauen

Womöglich wundern Sie sich über das Device »lxcbr0« in der Container-Konfiguration oder über die IP aus dem Netz »10.0.3.0/24« eines Containers. Dieses Ubuntu-Feature erlaubt es Containern, sich über Layer 3 automatisch mit der Außenwelt zu verbinden. Das realisiert eine eigene Netzwerkbridge namens »lxcbr0« , hinzu kommen ein passender »dnsmaq« -Daemon sowie eine »iptables« -NAT-Regel. Die Bridge selbst verbindet kein Interface mit dem Host, Details zu ihr liefert Listing 2.

Listing 2

Die LXC-Bridge lxcbr0

01 root@ubuntu:~$ brctl show
02 bridge name     bridge id               STP enabled     interfaces
03 lxcbr0          8000.000000000000       no
04
05 root@ubuntu:~# cat /etc/default/lxc-net | grep -v -e "#"
06 USE_LXC_BRIDGE="true"
07
08 LXC_BRIDGE="lxcbr0"
09 LXC_ADDR="10.0.3.1"
10 LXC_NETMASK="255.255.255.0"
11 LXC_NETWORK="10.0.3.0/24"
12 LXC_DHCP_RANGE="10.0.3.2,10.0.3.254"
13 LXC_DHCP_MAX="253"
14
15 root@ubuntu:~# iptables -t nat -L POSTROUTING
16 Chain POSTROUTING (policy ACCEPT)
17 target     prot opt source               destination
18 MASQUERADE  all  --  10.0.3.0/24         !10.0.3.0/24
19
20 root@ubuntu:~# ps -eaf | grep dnsmas
21 lxc-dns+  1047     1  0 18:24 ?        00:00:00 dnsmasq -u lxc-dnsmasq --strict-order --bind-interfaces --pid-file=/run/lxc/dnsmasq.pid --conf-file= --listen-address 10.0.3.1 --dhcp-range 10.0.3.2,10.0.3.254 --dhcp-lease-max=253 --dhcp-no-override --except-interface=lo --interface=lxcbr0 --dhcp-leasefile=/var/lib/misc/dnsmasq.lxcbr0.leases --dhcp-authoritative

Verwendet ein Container das »lxcbr0« -Netzwerkinterface, weist ihm der »dnsmasq« -Daemon auf dem Wirtssystem beim Hochfahren über DHCP eine IP-Adresse zu. Über sie kontaktiert er dann die Außenwelt. Auf den Container selbst haben Sie jedoch nur vom Wirtssystem aus Zugriff. Dank »iptables« und einiger NAT-Regeln reichen Sie aber bei Bedarf einzelne Ports von außen an die Container durch. Das folgende Beispiel zeigt, wie das Wirtssystem den Port 443 in den Container weiterleitet:

sudo iptables -t nat -A PREROUTING -p  tcp --dport 443 -j DNAT --to-destination 10.0.3.190:443

Zum Testen ist das »lxcbr0« -Interface toll. Betreiben Sie aber mehrere Container auf einem Server, wird es unübersichtlich und umständlich. Dann bietet sich der Einsatz eines eigenen Bridge-Device ohne NAT an. Es bindet die »veth« -Devices in den Containern auf Layer-2-Ebene vollständig ans Netzwerk an.

Dafür richten Sie auf dem Wirtssystem zuerst eine Bridge in »/etc/network/interfaces« ein (Abbildung 3). Vergessen Sie dabei nicht, die bestehenden »eth0« -Interfaces auszukommentieren. Anschließend tragen Sie die neue Bridge noch in der Container-Konfiguration ein, die im Beispiel »/var/lib/lxc/debian_test/config« heißt:

Abbildung 3: Über die Datei »/etc/network/interfaces« lässt sich ein Bridge-Device ohne NAT einrichten.

Abbildung 3: Über die Datei »/etc/network/interfaces« lässt sich ein Bridge-Device ohne NAT einrichten.

lxc.network.link = br0

Container beziehen ihre IP nun entweder weiterhin über DHCP oder Sie richten eine fixe IP-Adresse ein. Letzteres erledigen Sie entweder in der Container-Konfiguration (»lxc.network.ipv4« ) oder besser direkt im Debian- oder Ubuntu-Container selbst, und zwar in der Datei »/etc/network/interfaces« . Neben dem gewöhnlich verwendeten »veth« -Interface gibt es noch weitere Netzwerkoptionen: »none« , »empty« , »vlan« , »macvlan« und »phys« .

Ressourcen limitieren

Um die Limitierung und das Accounting der Ressourcen eines Containers kümmern sich Cgroups. Seit Ubuntu 14.04 kommt hierzu der »cgmanager« [5] zum Einsatz, zuvor griff der Admin auf diese Werte über »/sys/fs/cgroup/« zu.

Einen guten Überblick über den aktuellen Verbrauch verschafft das Kommando »lxc-info« (Abbildung 4). Die Cgroup-Werte fragt »lxc-cgroup« ab:

Abbildung 4: Der Befehl »lxc-info« verschafft einen Überblick über die vom Container genutzten Ressourcen.

Abbildung 4: Der Befehl »lxc-info« verschafft einen Überblick über die vom Container genutzten Ressourcen.

root@ubuntu:~# lxc-cgroup -n debian_test memory.usage_in_bytes3985408root@ubuntu:~# lxc-cgroup -n debian_test  memory.failcnt0

Interessant ist der Counter »failcnt« , den der Kernel erhöht, sobald der Container mehr RAM anfordert als erlaubt. Wollen Sie auch den verwendeten Swapspace erfassen, müssen Sie das unter Ubuntu 14.04 mit einer Bootoption für den Kernel aktivieren (Listing 3).

Listing 3

Swapspace erfassen

01 root@ubuntu:~# cat /etc/default/grub | grep LINUX_DEFAULT
02 GRUB_CMDLINE_LINUX_DEFAULT=""
03 root@ubuntu:~# vi /etc/default/grub
04 root@ubuntu:~# cat /etc/default/grub | grep LINUX_DEFAULT
05 GRUB_CMDLINE_LINUX_DEFAULT="swapaccount=1"
06 root@ubuntu:~# update-grub2
07 [...]
08 root@ubuntu:~# reboot

Danach limitieren Sie auf Wunsch zusätzlich zum RAM auch den verwendeten Swapspace pro Container. Die Cgroup-Steuerdatei »memory.memsw.limit_in_bytes« beinhaltet dabei das gesamte Limit für den RAM inklusive Swap. Die gewünschten Limits setzen Sie in der Container-Konfiguration. Das folgende Beispiel erlaubt 100 MByte RAM und 100 MByte Swapspace:

lxc.cgroup.memory.limit_in_bytes = 100M
lxc.cgroup.memory.memsw.limit_in_bytes = 200M

Die Tabelle in Abbildung 5 stammt von [6] und liefert einen Überblick der verfügbaren Cgroup-Subsysteme. Eine umfassende und aktuelle Cgroup-Dokumentation liefert [7]. Zusätzlich zum RAM erweisen sich oft noch die Subsysteme aus Tabelle 2 als relevant.

Tabelle 2

Relevante Cgroup-Subsysteme

Subsystem

Funktion

»cpuset.cpus«

CPU-Core-Pinning.

»cpu.shares«

Je höher der Wert, desto mehr CPU-Zeit erhält der Container.

»devices.allow/deny«

Zugriff auf Devices des Basissystems.

»blkio.throttle.read_bps_device/write_bps_device«

Schreib- und Lesedurchsatz limitieren.

»net_cls«

Netzwerkdurchsatz mit Hilfe von »tc« steuern.

Abbildung 5: Die verfügbaren Cgroup-Subsysteme im Überblick.

Abbildung 5: Die verfügbaren Cgroup-Subsysteme im Überblick.

Den Festplattenplatz eines Containers limitiert das Cgroup-Feature indes nicht. Eine derartige Begrenzung realisieren wahlweise ein eigenes Logical Volume pro Container, eine eigene Imagedatei oder ein XFS Directory Tree Quota. Da die LXC-Tools LVM bereits gut unterstützen, ist der Weg über ein eigenes Logical Volume der empfohlene.

Kommandobrücke

Bisher sind Sie mit nur wenigen »lxc-*« -Kommandos ausgekommen. Container legen Sie mit »lxc-create« an, anschließend starten und stoppen Sie diese mit »lxc-start« und »lxc-stop« oder löschen am Ende wieder mit »lxc-destroy« . Über die Option »-n« geben Sie in der Regel den Container-Namen an.

Doch es existieren noch einige weitere »lxc-*« -Kommandos. Bei den meisten lässt sich erahnen, was sie bewirken. Eine komplette Liste erhalten Sie, indem Sie das Kommando:

ls /usr/bin/lxc-*

eingeben, wobei die in Tabelle 3 gezeigten Kommandos zu den weniger bekannten gehören.

Tabelle 3

LXC-Exoten

Kommando

Funktion

»lxc-autostart«

Steuert Container, die für den automatischen Start konfiguriert sind.

»lxc-checkconfig«

Prüft die Voraussetzungen des Kernels.

»lxc-device«

Reicht ein Device in den Container durch.

»lxc-execute«

Führt ein einzelnes Kommando in einem Container aus.

»lxc-freeze/lxc-unfreeze«

Friert die Prozesse eines Containers ein und weckt sie wieder auf.

»lxc-monitor«

Überwacht Statusänderungen.

»lxc-start-ephemeral«

Startet einen temporären Container-Klon, den LXC beim Stopp eines Containers wieder automatisch zerstört.

Neu bei LXC

Die erste stabile Version von LXC ist bereits Anfang 2014 erschienen und bietet einige wesentliche Neuerungen, auf die einen Blick zu werfen sich lohnt:

  • Container Nesting
  • Hooks
  • Unprivilegierte Container
  • Prebuilt-Container
  • Liblxc-API

Wollen Sie verschachtelte Container nutzen (Container Nesting), weisen Sie zuerst dem übergeordneten Container das Apparmor-Profil »lxc-container-default-with-nesting« zu. Teilen Sie den Ordner »/var/cache/lxc/« des Wirtssystems mit dem Container, klappt auch das Erstellen prompt. Anschließend installieren Sie im Container das LXC-Paket und erstellen den verschachtelten Container wie gewohnt, die VM auf der Heft-DVD liefert ein Beispiel. Das Basissystem listet die verschachtelten Container ebenfalls auf (Listing 4).

Listing 4

Container im Container

01 root:/# echo "/var/cache/lxc var/cache/lxc none bind,create=dir" >> /var/lib/lxc/ubuntu_test/fstab
02 root:/# echo "lxc.aa_profile = lxc-container-default-with-nesting" >> /var/lib/lxc/ubuntu_test/config
03 root:/# echo "lxc.mount.auto = cgroup" >> /var/lib/lxc/ubuntu_test/config
04
05 root@ubuntu:/# lxc-ls --fancy --nesting
06 NAME               STATE    IPV4                  IPV6  AUTOSTART
07 -----------------------------------------------------------------
08 debian_test        RUNNING  10.0.3.190            -     NO
09 debian_test2       STOPPED  -                     -     NO
10 ubuntu_test        RUNNING  10.0.3.191, 10.0.4.1  -     NO
11  \_ ubuntu_nested  RUNNING  10.0.4.197            -     NO

Mit Hooks automatisieren Sie den Container-Einsatz. Folgende Hooks stehen bereit: »lxc.hook.pre-start« , »-.pre-mount« , »-.mount« , »-.autodev« , »-.start« , »-.post-stop« und »-.clone« .

Lange haben die Entwickler an nicht-privilegierten Containern gearbeitet. Seit Version 1.0 ist es möglich, Container auf dem Basissystem auch ohne Rootzugriff zu betreiben (Abbildung 6). Die Entwickler setzen dies über den User-Namespace sowie eigene UID- und GID-Bereiche pro Container um. Weil nicht-privilegierte Container einige Operationen nicht erlauben, erleichtern vorgefertigte Container (Prebuilt) ihre Installation. Passende Templates liegen auf einem Jenkins-Server [8], die Technologie steht noch am Anfang. Mit »lxccontainer.h« gibt es nicht zuletzt ein C-API für LXC, das Projekt bietet zudem ein Python-Modul an.

Abbildung 6: Über die virtuelle Maschine auf der Heft-DVD lassen sich auch nicht-privilegierte Container ausprobieren.

Abbildung 6: Über die virtuelle Maschine auf der Heft-DVD lassen sich auch nicht-privilegierte Container ausprobieren.

Sicherheit

Wenn es um die Sicherheit von Containern geht, sind oft Schauergeschichten zu hören, nach denen ein Admin von einem Container aus das gesamte Hostsystem kompromittieren kann. Weil sich Container den Kernel mit dem Wirt teilen, gab es diese Probleme vor einiger Zeit tatsächlich. Heute sind sie aber dank Capabilities, Cgroups, Apparmor/SE-Linux, Seccomp und User Namespaces recht gut gesichert. Zurzeit gibt es keine bekannten Sicherheitsprobleme in Bezug auf Linux-Container.

Details zur App-Armor-Policy unter Ubuntu 14.04 nennt das Verzeichnis »/etc/apparmor.d/abstractions/lxc/« , die Seccomp-Policy für LXC steckt in »/usr/share/lxc/config/common.seccomp« . Einige Allow- und Deny-Regeln für Cgroups (»lxc.cgroup.devices.allow/deny« ) sowie Capabilities-Limitierungen (»lxc.cap.drop« ) stehen in den Konfigurationstemplates im Ordner »/usr/share/lxc/config/« .

Schnappschüsse und Klone

Der übliche Speicherort für Container befindet sich auf dem Hostsystem unter »/var/lib/lxc/Container-Name/rootfs/« . LXC kommt zugleich mit Dateisystemen wie Btr-FS und ZFS sowie mit LVM und Overlay-FS zurecht.

Auf der Grundlage von Overlay-FS können Sie einen Mastercontainer erstellen (Listing 5). Basierend auf diesem, erzeugen Sie anschließend mehrere Klone mit »lxc-clone« , von denen LXC jeweils nur die Deltas speichert. Das erweist sich vor allem für Testumgebungen als praktisch und platzsparend. Den Mastercontainer selbst sollten Sie in diesem Szenario allerdings nicht aktivieren.

Listing 5

Mastercontainer erstellen

01 root@ubuntu:/var/lib/lxc# lxc-create -t ubuntu -n ubuntu_master
02 root@ubuntu:/var/lib/lxc# lxc-clone -s -B overlayfs ubuntu_master ubuntu_overlay1
03 root@ubuntu:/var/lib/lxc# cat ubuntu_overlay1/delta0/etc/hostname
04 ubuntu_overlay1

Das Kommando »lxc-snapshot« erstellt, wenig überraschend, Schnappschüsse eines Containers (Abbildung 7). Dafür müssen Sie den laufenden Container zunächst stoppen. Ist ein Logical Volume Manager im Einsatz, erstellt LXC über »lxc-clone -s« einen LVM-Snapshot. Über die Option »-r« setzen Sie einen Container auf einen Snapshot zurück.

Abbildung 7: Platzsparende Snapshots lassen sich mit Overlay-FS anfertigen.

Abbildung 7: Platzsparende Snapshots lassen sich mit Overlay-FS anfertigen.

Troubleshooting

Sollte ein Container beim Start einmal Probleme bereiten, empfiehlt es sich, ihn im Vordergrund zu starten. Dazu lassen Sie beim »lxc-start« einfach die Option »-d« weg. Sind Sie dem Problem auf die Spur gekommen, mounten Sie das Dateisystem eines gestoppten Containers direkt. In dem eingehängten System arbeiten Sie dann an dem Problem oder setzen beispielsweise ein anderes Default-Runlevel.

Darüber hinaus unterstützen die »lxc-*« -Kommandos einen Logfile-Parameter (»-o« ) sowie eine zugehörige Log Priority (»-l« ). Zusätzlich könnten Sie sich auch für den Kernel-Ringbuffer sowie App-Armor-Meldungen des Basissystems interessieren. Innerhalb der Container sollten Sie das Kernel-Logging des »rsyslogd« deaktivieren, um mehrfaches Loggen zu vermeiden.

Fazit

Mit LXC 1.0 steht erstmals eine stabile und fünf Jahre offiziell unterstützte Version bereit. Es lässt sich somit ohne Bedenken für produktive Systeme einsetzen. Im Vergleich zu Docker hat sich um LXC allerdings bislang kein Hype entwickelt. Vielleicht ändert LXD [9], das auf LXC aufbaut, dies in Zukunft. Linux-Container sind auf alle Fälle die schnellste Möglichkeit, um Virtualisierung und Isolierung unter Linux zu erreichen. Das ist wohl mit ein Grund, weshalb Google mit Projekten wie Lmctfy [10] und Kubernetes [11] schon seit Jahren sehr intensiv auf Container setzt.

Infos

  1. Virtuozzo: https://openvz.org/Virtuozzo
  2. Open VZ: https://openvz.org
  3. LXC: https://linuxcontainers.org
  4. Ubuntu Server 14.04: http://old-releases.ubuntu.com/releases/14.04.0/
  5. Cgmanager: https://linuxcontainers.org/cgmanager/introduction/
  6. Cgroup-Werte von LXC-Containern: https://www.thomas-krenn.com/de/wiki/Cgroup_Werte_von_LXC_Linux_Containern
  7. Cgroup-Dokumentation: https://www.kernel.org/doc/Documentation/cgroups/
  8. Prebuilt-Container:https://jenkins.linuxcontainers.org
  9. Martin Loschwitz, “Erbfolgekrieg”: Linux-Magazin 05/15, S. 66
  10. Lmctfy (Let Me Contain That For You): https://github.com/google/lmctfy
  11. Kubernetes: http://kubernetes.io

Der Autor

Christoph Mitasch arbeitet bei der Thomas Krenn AG und hat “Computer- und Mediensicherheit” an der FH Hagenberg studiert.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 6 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben