Docker und neue Möglichkeiten für Software-Entwickler

PossibleDocker ist derzeit omnipräsent. Die Virtualisierungstechnologie kann den Software-Entwicklungsprozess plattform- und umgebungsunabhängig machen.
 

Wie Docker den Entwickler-Werkzeugkasten bereichert
 

Zwei Beispiele aus der Praxis: Zum einen lassen sich mit Docker eigenständige, komplett isolierte und doch performante Integrationstests realisieren. Docker vereinfacht dabei die Pflege und den Betrieb der Tests, die sonst oft einen erheblichen Aufwand bedeuten. Zum anderen sorgt Docker für die nahtlose Einbindung von Anwendungen in den Build-Prozess von der Entwicklung bis zur Produktion. Denn es lassen sich produktionsnahe Deployment-Artefakte in Form von Applikations-Images bauen. Diese werden anstelle der Applikationsartefakte wie WAR- oder EAR-Dateien durch die Continuous Delivery-Pipeline geschoben, so dass diese letztendlich in der Produktionsumgebung landen. Der folgende Beitrag skizziert, wie Docker den Entwickleralltag in diesen beiden Bereichen entscheidend verbessern kann.

Anzeige

Hintergrund: Docker

Docker ist eine offene Plattform für Entwickler und Administratoren, um verteilte Applikationen zu bauen, auszuliefern und zu betreiben. Docker ist eine Open-Source-Software, die auf Basis von Linux Technologien eine leichtgewichtige Virtualisierung realisiert. Applikationen werden hierbei in sogenannten „Containern“ isoliert. Dies vereinfacht die Bereitstellung von Anwendungen, da die Container ein standardisiertes Format bereitstellen und sich somit auf vielen verschiedenen Plattformen installieren lassen. Auch gewährleisten Container die Trennung der auf einem Rechner genutzten Ressourcen, sodass ein Container keinen Zugriff auf Ressourcen anderer Container hat.

Beispiel 1: Integrationstests mit Docker
 

Die Erstellung und Wartung von Integrationstests stellt für Softwareentwickler eine besondere Herausforderung dar. Denn komplexe Tests besitzen zahlreiche Abhängigkeiten, die in den Tests berücksichtigt werden müssen. Dazu gehören Datenbanken und Web-Services, aber auch LDAP-, FTP-, Messaging- oder Secure Shell (SSH)-Server. Idealerweise verfügen Integrationstests über die folgenden, nicht-funktionalen Eigenschaften:

  • Robust: Ein Test sollte robust sein. D.h. er sollte entweder immer erfolgreich sein oder immer fehlschlagen, wenn sich an dem zu testenden Code oder am Test selbst nichts ändert. Dem gegenüber stehen die so genannten „flaky tests“, die nicht immer funktionieren und deren Fehlschlagen sich jedoch nicht reproduzieren lässt.
     
  • Autark: Tests sollten idealerweise autark lauffähig sein, sodass die volle und exklusive Kontrolle über alle Testparameter gegeben ist. Das schließt auch die Governance für externe Testsysteme ein, um den Aufwand für notwendige Abstimmungen gering zu halten.
     
  • Isoliert: Wenn auf einem System mehrere gleichartige Tests von z.B. verschiedenen Feature-Branches überlappend gestartet werden, sollten sich diese nicht gegenseitig beeinflussen. Insbesondere sollten sie nicht ihre Ausführungsumgebung beeinflussen und z.B. keine für alle sichtbaren Netzwerk-Ports öffnen.
     
  • Schnell: Natürlich sollten Tests zügig durchlaufen, was insbesondere in der Erstellungsphase für kurze Turnaround-Zeiten und damit schnelles Feedback sorgt.

Tatsächlich sind diese Eigenschaften nicht getrennt voneinander zu betrachten. Sie überlappen sich teilweise erheblich. Beispielsweise sind autarke und isolierte Tests oft auch schon sehr robust. Zudem ist die obige Liste sicher auch nicht vollständig, da Anforderungen wie eine ausreichende Testabdeckung fehlen. Sind jedoch diese vier Eigenschaften nicht erfüllt, ist dies mit einem erhöhten Wartungsaufwand der Tests verbunden. Die daraus entstehenden Kosten sind dann auch oft der Grund für einen gänzlichen Verzicht auf Integrationstests.

Zu den gängigen Verfahren für Integrationstests zählen zum einen externe Testsysteme, bei denen dedizierte Installationen der realen externen Abhängigkeiten auf Testumgebungen betrieben werden, etwa per Web-Services durch externe Anbieter. Auf die Verfügbarkeit dieser Testsysteme hat man oft keinen Einfluss, so dass man nur abhängig und in Abstimmung mit dem Betreiber Tests durchführen sollte. Auch existiert meist nur ein Testsystem für mehrere Nutzer, so dass parallele Testläufe nicht isoliert sind. Die Folge ist, dass Tests mit externen Testsystemen oft nicht robust sind. Der Vorteil externer Testsysteme ist aber, dass sie der Produktionsumgebung sehr nahe kommen und damit realistische Testergebnisse erzielen.

Oft gehören auch Mock-Testsysteme zu den eingesetzten Verfahren. Dabei werden externe Abhängigkeiten mit spezieller Software komplett simuliert. Der Vorteil der Verwendung von Mock-Systemen ist, dass man diese vollständig in den Build-Prozess integrieren kann. Dadurch sind die Tests weitestgehend autark. Sie werden während des Testlaufs gestartet und können mit einer individuellen Konfiguration etwa der Ports auch abgeschottet und isoliert werden. Auch deshalb ist diese Art von Tests robust. Der große Nachteil jedoch ist, dass Mock-Systeme nicht zwangsläufig dem Produktionssetup nahe kommen. Es hängt ganz davon ab, wie gut die Simulation umgesetzt ist und das reale System abbildet. Zudem muss die Mock-Konfiguration gepflegt und bei jeder Änderung der zu simulierenden externen Systeme angepasst werden.

Integrationstests mit Docker Images

Eine neue und sehr viel versprechende Variante externer Testabhängigkeiten sind Testumgebungen mit Docker-Containern, welche die entsprechenden externen Dienste enthalten. Per se sind Docker-Container isoliert, sodass mehrere Tests ohne Probleme parallel laufen können, ohne sich gegenseitig zu stören. Docker-Images werden zudem über eine Registry verwaltet und können dynamisch installiert werden. Somit benötigt der Build-Prozess außer einer Docker Installation nichts weiter und ist damit weitgehend autark beziehungsweise „self-contained“. Da der Lebenszyklus der Docker-Container vollständig vom Build selbst kontrolliert werden kann, sind diese Tests sehr robust und liefern reproduzierbare Ergebnisse. Die Kapselung ließe sich natürlich auch via kompletter virtueller Maschinen realisieren. Der Vorteil der Docker-Variante mit einer Virtualisierung auf Betriebssystemebene (OS-Level-Virtualisierung) ist der wesentlich geringere Ressourcen-Verbrauch und die Geschwindigkeit, mit der Docker-Container gestartet werden können. Zu beachten ist, dass Docker aktuell nur genutzt werden kann, wenn die externen Systeme unter Linux lauffähig sind. Das könnte sich in Zukunft aber noch ändern. Fazit: Die Docker-Variante vereint die Vorteile externer und Mock-Testsysteme: Sie erlaubt isolierte, autarke und robuste Test und kommt dem realen Kontext so nahe wie die Variante mit externen Testsystemen.

Beispiel 2: Deployment von Anwendungen mit Docker
 

Der zweite Bereich im Entwicklungsprozess, in dem Docker eine große Rolle spielen kann, ist das Deployment von Anwendungen. Bei der üblichen Softwareentwicklung ist die zentrale Entität, welche die Anwendung beinhaltet, für Java Anwendungen typischerweise ein Web- oder Enterprise-Archive (WAR oder EAR). Dieses wird lokal in der Entwicklungsumgebung getestet, bevor ein Continuous Integration-Server, wie zum Beispiel Jenkins, die Artefakte baut und in einem Repository speichert. Von dort beginnt das Artefakt seine Reise durch die Continuous Delivery-Pipeline mit verschiedenen Arten von weiteren Tests, bevor es letztendlich in der Produktionsumgebung landet.

Mit Docker kann diese Grenze nun verschoben werden: Statt der Applikations-Archive werden ganze Images durch die Continuous Delivery-Pipeline geschleust. Diese Images enthalten die Anwendung und die Laufzeitumgebung inklusive der Applikationsserver. Dadurch kann – zumindest aus Anwendungssicht – sichergestellt werden, dass die Umgebung in der Entwicklung identisch zu der in Produktion ist. Auch ist der Wechsel der Produktionssysteme aufgrund des standardisierten Docker-Image-Formats kein Problem: Man kann Docker zunächst auf eigenen Servern laufen lassen und später in die Cloud wechseln. Für den Entwicklungsprozess bedeutet das keinerlei Einschränkung und gibt mehr Flexibilität.

Nun gibt es mehrere Möglichkeiten, die Applikationsimages zu gestalten: Entweder erzeugt man reine “Datenimages”, die nur die Anwendungsartefakte wie WARs und EARs enthalten, und verknüpft diese später mit Containern, die die Applikationsserver enthalten. Oder aber man erzeugt gleich ein „merged Image“, das sowohl den Server als auch die Artefakte enthält. Welcher Ansatz sich letztendlich durchsetzen wird, wird die Zukunft zeigen.

Fazit
 

Mit Docker lässt sich die Entwicklung von Anwendungen also in zweierlei Hinsicht erleichtern: Integrationstests mit Docker sind quasi genauso umfangreich und stabil wie externe Testumgebungen und Mock-Testsysteme zusammen, dabei aber wesentlich einfacher zu konfigurieren und administrieren – und damit schlichtweg weniger zeit- und kostenintensiv. Gleiche Vorteile liefert Docker beim Deployment von Anwendungen im Build-Prozess. Dadurch, dass die Anwendungs- und die Laufzeitumgebung in ein und demselben Container „verpackt“ sind, ist die Entwicklung wesentlich schneller, flexibler und weniger fehleranfällig als beim klassischen Java-Entwicklungsprozess. Da bei Docker Images der Ausführungskontext in dem Container sowohl in der Entwicklungs- als auch Produktionsumgebung der gleiche ist, wird schließlich das Risiko minimiert, dass aufgrund unterschiedlicher Konfigurationen der jeweiligen Umgebungen Fehler erst in Produktion entdeckt werden.

Dr. Roland Huß, Software Architekt und Leiter der R&D Abteilung, ConSol Consulting & Solutions Software GmbH

www.consol.de

Anzeige

Weitere Artikel

Newsletter
Newsletter Box

Mit Klick auf den Button "Jetzt Anmelden" stimme ich der Datenschutzerklärung zu.