Blog

Von starken Hafenarbeitern und leichten Containern

Infrastructure as Code (IaC) ist ein mächtiges Werkzeug, das im Rahmen der DevOps-Bewegung die Entwicklung und den Betrieb näher zusammenbringt. Es ermöglicht vor allem das automatisierte Aufsetzen von Laufzeitumgebungen – von der Entwicklungs- bis hin zur Produktionsumgebung. Häufig werden bei der Umsetzung von IaC Virtuelle Maschinen und Werkzeuge wie Chef, Puppet und Vagrant zu deren Provisionierung eingesetzt. Im besten Fall gelangt man irgendwann an einen Punkt, an dem man mit nur einem Klick aus einem Betriebssystem-Basis-Image ein für den Produktionsbetrieb geeignetes VM-Image einschließlich Konfiguration und vorinstallierten Anwendungen erzeugen kann.

Nach dem Immutable Server-Ansatz wird bei Änderungen an einer Infrastrukturkomponente, sei es eine Konfigurationsänderung, eine Aktualisierung des Betriebssystems oder eine neue Anwendungsversion, einfach ein komplett neues Image nach der Bauanleitung bzw. dem so genannten Rezept des Konfigurationsmanagement-Tools erstellt.  Ein wichtiger Aspekt dabei ist, dass bestehende VM-Instanzen bei Konfigurationsänderungen nicht geändert (z.B. durch Einspielen einer neuen Anwendungsversion) sondern jedes Mal vollständig neu erzeugt werden. Damit wird sichergestellt, dass die ausgeführten Konfigurationsschritte sowie deren Reihenfolge für jeden Server und jede Umgebung stets reproduzierbar sind.

Große Images und leichtgewichtige Container

Der naheliegende Wunsch eines Entwicklers, automatisierte System- und Akzeptanztests in einer produktionsnahen Umgebung vollständig lokal auf dem Entwicklungsrechner durchzuführen, rückt mit Hilfe dieser Werkzeuge in greifbare Nähe. Wenn die Infrastruktur jedoch tatsächlich ausreichend produktionsähnlich, also komplex wird, mit verschiedenen interagierenden Anwendungsservern und Datenbanken auf verschiedenen Virtuellen Maschinen, dann reichen die Ressourcen eines Entwicklungsrechners zum Ausführen einer solchen Umgebung oft nicht aus. Das liegt mitunter daran, dass jede Virtuelle Maschine ein eigenes vollständiges Betriebssystem inklusive Kernel, Bibliotheken, Netzwerk-Stack usw. in den Arbeitsspeicher steckt.

Aber es geht auch leichtgewichtiger – zumindest, wenn man seine einzelnen Infrastrukturkomponenten auf Linux aufsetzen kann. Das LXC-Projekt verbindet eine Reihe von teilweise altbewährten Kernel-Funktionen, um einzelne Prozesse in einer vollständig isolierten Umgebung, einem so genannten LXC-Container, laufen zu lassen. Letzerer besitzt ein eigenes Dateisystem und virtuelle Netzwerkschnittstellen, nutzt jedoch den Kernel des Host-Systems. Auf einem solchen System lassen sich Container beinahe ebenso schnell wie reguläre lokale Prozesse starten und die virtuellen Netzwerkschnittstellen der laufenden Container flexibel verschalten. Dabei entsteht kein nennenswerter zusätzlicher Arbeitsspeicherverbrauch für den Container.

Das initiale virtuelle Dateisystem eines Containers (das sog. Container Image) ist mit dem Basis-Image einer virtuellen Maschine vergleichbar. Es ist jedoch deutlich kleiner, da der Kernel des Host-Betriebssystems genutzt wird und er somit nicht im Container Image selbst enthalten ist.

Und dann kam Docker

Docker ist eine auf LXC aufsetzende Engine, die die Erstellung, Verwaltung und Ausführung von LXC-Containern über eine handliche CLI- und REST-Schnittstelle ermöglicht.

Die Anweisungen zur Erzeugung eines Containers werden in sog. Dockerfiles beschrieben. Diese Rezepte bestehen aus einfachen Shell-Anweisungen, die während der Erstellung des Images im laufenden Container ausgeführt werden.

Beispiel

FROM dockerfile/java
# Install tomcat7
RUN apt-get install tomcat7

# Download a webapp and deploys it to tomcat
ADD http://my.private.repository/maven2/my/company\
/webapp/1.0.0/webapp-1.0.0.war \
$CATALINA_HOME/webapps/

# Make port 8080/tcp available for host-system
# and linked containers.
EXPOSE 8080

ENTRYPOINT tomcat run

Dieses beispielhafte Dockerfile setzt auf ein Ubuntu-Image mit vorinstalliertem Java7-JDK auf, und beschreibt die Installation eines Tomcat7 und das Deployment einer Web-Anwendung aus einem privaten Maven-Repository. Das Basis-Image dockerfile/java stammt aus dem so genannten Docker Index, einem öffentlichen Image-Repository in dem bereits eine Reihe vorkonfigurierter Docker-Images für verschiedene Anwendungen bereitstellt werden (darunter java, mongo, node, mysql uvm.). Ein Image-Repository kann auch unternehmensintern als nicht-öffentliches Repository betreiben werden (den Repository-Server gibt es übrigens auch als eigenes vorkonfiguriertes Docker-Image.)

Docker wurde ursprünglich vom Platform As A Service (PAAS)-Anbieter dotCloud entwickelt und wird seit dem März 2013 als Open-Source-Community-Projekt weitergeführt. Zukünftig soll Docker auch von anderen PAAS-Anbietern unterstützt werden. Es ist aber grundsätzlich auf jedem Linux-Host-System lauffähig, das über eine ausreichend aktuelle Kernel-Version verfügt. Auf Entwicklungsrechnern mit einem anderen Betriebssystem kann eine virtuelle Linux-Maschine als Docker-Host gestartet werden.

Der Funktionsumfang von Docker kann etwa so zusammengefasst werden:

  • Erzeugung von Docker-Images aus Dockerfiles
  • Herunterladen von Docker-Images aus einem Image-Repository
  • Erzeugung und Ausführen von Docker-Containern auf Basis eines Docker-Images
  • Verwalten des Lebenszyklus von Docker-Containern und Systemressourcen
  • starten, anhalten
  • maximalen Speicherverbrauch und CPU-Zeit steuern
  • Verdrahten von virtuellen Netzwerkschnittstellen zwischen Containern

Bei komplizierteren Setup-Routinen, die mit einfachen Shell-Anweisungen unhandlich abbildbar sind, können bei der Erstellung von Docker-Images wiederum Chef oder Puppet eingesetzt werden.

Versionierte Infrastruktur

Folgt man dem Immutable Server-Muster, ist das Aufsetzen einer Infrastrukturkomponente allein mit LXC-Containern eine aufwändige Angelegeneheit. Jede Konfigurationsänderung an einer Infrastrukturkomponente führt letztendlich zum Durchlauf des folgenden Prozesses:

  1. Starten eines neuen Containers mit einem initialen Basis-Image und Laden des Provisionierungs-Rezepts
  2. Gegebenenfalls Einspielen von Systemaktualisierungen
  3. Installation und Konfiguration eines Anwendungsservers
  4. Deployment und Konfiguration der Anwendung

Dieser Prozess beansprucht Zeit. Zeit, in der ein Entwickler auf Ergebnisse von Akzeptanztests einer neuen Anwendungsversion wartet. Oder Zeit, in der ein Betriebler auf Ergebnisse von Systemtests nach einer Konfigurationsänderung wartet. Leider verschwendete Zeit.

Docker geht hierbei einen anderen Weg: Betracht man die Konstruktion eines vollständigen unveränderlichen Container-Images auf Basis eines Rezepts als eine sequenzielle Abfolge von Konfigurationsschritten und das Ergebnis jeder Konfigurationsänderung (also eine Änderung am Dateisystem) als eigene Version, ist es ausreichend, das Ergebnis einer Konfigurationsänderung als inkrementelles Dateisystem-Delta zum vorhergehenden Konfigurationsschritt als eigene Version zu speichern. Bei Änderungen an dem Rezept einer Infrastrukturkomponente muss ein Image somit nicht vollständig neu erstellt werden, sondern lediglich ab der Image-Version des geänderten Konfigurationsschritts. Stellt man Konfigurationsschritte, die sich tendenziell häufiger ändern (wie z.B. das Deployment der eigentlichen Anwendungsartefakte) an das Ende eines Rezepts, können neue Container-Images sehr schnell erzeugt werden, da lediglich ein einzelner Konfigurationsschritt auf Basis der Vorgängerversion zur Erzeugung eines neuen Images ausgeführt werden muss.

Versionierung von inkrementellen Images

Versionierung von inkrementellen Images

Bemerkenswert an Docker ist auch, dass Images nie als Ganzes, sondern als Verkettung von Dateisystem-Deltas der Ausführungsergebnisse einzelner Anweisungen aus der zugrundeliegenden Dockerfiles erstellt und in einem lokalen Image-Repository gespeichert werden. Dieses Vorgehen spart insbesondere bei vielen sehr ähnlichen Containern enorm viel Speicherplatz.

Dockerfiles, Images und Container

Dockerfiles, Images und Container

Bei dem Start eines Container werden alle gespeicherten Deltas des Container-Images mittels UnionFS zu einem virtuellen Dateisystem zusammengefasst und dem Container zur Verfügung gestellt. Das Dateisystem des Containers ist zur Laufzeit veränderlich (beschreibbar), die zugrundeliegenden Container-Images werden hingegen nicht verändert und bieten einen jederzeit reproduzierbaren und neu instanzierbaren Ausgangszustand unseres Immutable Servers.

Ausblick

Docker ist eine junge und sehr vielversprechende Technologie. Unsere Gehversuche verliefen nach kleineren Startschwierigkeiten erfolgreich. Die manuelle, etwas hakelige Konfiguration der Startreihenfolge und Netzwerk-Verdrahtung (Orchestrierung) mehrerer Docker-Container bieten auf jeden Fall Raum für Automatisierung. Da Docker jedoch einfache REST-APIs zur Verfügung stellt, stehen hierfür bereits eigene Orchestrierungs-Erweiterungen wie maestro-ng oder crane in den Startlöchern.

Wer neugierig geworden ist, sollte sich das interaktive Docker Tutorial und den Talk The future of linux containers von Solomon Hykes anschauen. Wir werden die Entwicklung von Docker auf jeden Fall im Auge behalten und sind gespannt auf das erste produktionsreife Release!

Holisticon AG — Teile diesen Artikel

Über den Autor

Ein Kommentar

  1. Docker ist vor allem interessant, wenn man die Applikation auf die Docker Container so schneiden kann, dass diese sich auf kleine in sich abgeschloßene Services reduzieren. Am besten sollten diese stateless sein. Also „state“ im container ist problematisch IMHO.

    Ich versuche Docker auch als leichtgewichtiree alternative zu VMs. bei der entwicklung auf meinem Laptop ;)

Antwort hinterlassen