Blog

Die Welt der Container: Einführung in Docker

Softwareprojekte erfordern mittlerweile weitaus mehr als nur das Schreiben von Code. So erhöhen unter anderem unterschiedliche Programmiersprachen, Frameworks und Architekturen die Komplexität von Projekten. Docker stellt Anwendungen mit all ihren Abhängigkeiten als Pakete in sogenannten "Images" bereit und ermöglicht es so, Arbeitsabläufe zu vereinfachen. Dieser Artikel soll als Einführung in das Thema dienen und Ihnen einen Überblick über die grundlegenden Konzepte von Docker geben.

Warum Docker?

Das lokale Testen von Anwendungen lief in der Vergangenheit in etwa so ab: ein Entwickler möchte die Anwendung seiner Kollegin testen. Dafür erhielt er von ihr eine Anleitung mit Voraussetzungen, in der z.B. beschrieben war welches Betriebssystem, welche Programme in welchen Versionen oder welche Datenbank benötigt wird. Das Testen war so für beide Parteien ziemlich mühsam, denn es erforderte die lokale Einrichtung einer Entwicklungsumgebung.

Mit der Einführung virtueller Maschinen (VMs) wurde das Ganze bereits deutlich vereinfacht. Auf der VM konnte die Entwicklerin aus dem Beispiel nun die Anwendung mit all ihren Abhängigkeiten installieren und diese dann ihrem Kollegen übergeben. Doch mit einer VM ist auch immer die Reservierung von Ressourcen verbunden; und das führte typischerweise dazu, dass sich mehrere Anwendungen dieselbe VM teilen mussten.

Docker ermöglicht es, Anwendungen inklusive ihrer Abhängigkeiten in einem Docker Image abzubilden und in Form eines Containers auszuführen. Der entscheidende Unterschied zwischen einem Docker Container und einer VM ist der, dass der Container kein eigenes Betriebssystem hat (s. Abbildung). Die Entwicklerin aus unserem Beispiel muss zum Testen der Anwendung mit Docker lediglich ein sogenanntes Dockerfile an ihren Kollegen übergeben, aus dem sich das Docker Image und der Docker Container ableiten. Die Anwendung kann dann im Container getestet werden.

Grundbegriffe

Ein Dockerfile ist eine Datei, die das "Rezept" für ein Docker Image enthält: es beinhaltet alle Befehle, um mit docker build ein Docker Image zu bauen. Die folgende Abbildung zeigt auf der linken Seite ein beispielhaftes Dockerfile. FROM definiert die Basis unseres Images: ein bereits bestehendes Docker Image. COPY kopiert Dateien und Verzeichnisse in das Dateisystem des Images. RUN führt einen Befehl auf der Kommandozeile während des Bauens des Images mit docker build aus. In unserem Beispiel wird eine vordefinierte Funktion aufgerufen, die ein R-Paket installiert. CMD definiert schlussendlich das Standardverhalten des Containers unmittelbar nachdem der Container mittels docker run gestartet wurde: in unserem Fall wird ein R-Skript ausgeführt. Die beschriebenen Optionen sind selbstverständlich nur beispielhaft, es existiert eine Vielzahl an weiteren Optionen für Dockerfiles.

Ein Docker Image wiederum ist ein Build Artefakt. Es bündelt Dateien, Abhängigkeiten und Programme, die von der Anwendung benötigt werden. Ein Docker Image besteht meist aus sogenannten Layern: Images basieren auf anderen Images und enthalten zusätzliche Anpassungen. So basiert in unserem Beispiel das r-batch-Image auf einem ubuntu-Image, dem eine R-Installation sowie individuelle Konfigurationen hinzugefügt wurden. Das Image kann in einer Docker-Registry gespeichert werden und von dort aus als Grundlage für weitere Dockerfiles verbreitet werden. Docker Hub ist die Standard-Registry für Open Source Images; auch unsere eigenen Images sind dort zu finden. Eine lokale Installation von Docker wird immer in dieser Registry nach verfügbaren Images suchen, wenn das benötigte Image lokal nicht gefunden werden kann. Der docker run-Befehl startet einen Container als Instanz eines Docker Images. Werden Änderungen am oder im laufenden Container vorgenommen, werden diese nach dem Löschen des Containers nicht gespeichert. Solche Änderungen müssen in das Docker Image (bzw. das Dockerfile, auf dessen Basis anschließend das neue Image gebaut wird) übernommen werden, damit sie beim nächsten Start des Containers vorhanden sind. Im allgemeinen gilt, dass Container ‘zustandslos’ sind, bzw. sein sollten. Das bedeutet, sie speichern keinerlei Daten im Container, die zu einem späteren Zeitpunkt noch einmal relevant sind. Das erlaubt es, Container beliebig zu replizieren bzw. zu skalieren - und das auf beliebiger Infrastruktur. In einer Zeit in der 'die Cloud' immer relevanter wird, ist dies eine der wichtigsten Eigenschaften eines Containers.

Interaktion mit Containern

Ein Container ist per default isoliert von anderen Containern und der Host Maschine. Die Verbindung mit einem Container ist über eines oder mehrere Netzwerke möglich, indem z.B. beim docker run Befehl ein Parameter für das Portmapping gesetzt wird:

Der Parameter -p 8080:80 gibt an, dass der Port 80 des Container auf den Port 8080 der Hostmaschine gemappt werden soll. Sollte beispielsweise ein Webserver innerhalb des Containers laufen, kann dieser nun im Browser unter http://localhost:8080/ aufgerufen werden.

Dateien oder Verzeichnisse des Hostsystems können mit Hilfe von sogenannten Volumes im Container verfügbar zu machen. Ein Volume ist ein Dateisystem, mit dem persistente Daten in den Container und aus dem Container heraus transportiert werden können. Eine Datei oder ein Verzeichnis kann beispielsweise mit

im Container verfügbar gemacht werden. Der Container selbst ist allerdings kein geeigneter Ort zum Speichern persistenter Daten, denn wenn ein Container gestoppt oder gelöscht wird, gehen alle Änderungen und Dateien, die nicht im Docker Image enthalten sind, verloren.

Zusammenfassung

Docker ist aus unserer Infrastruktur und unseren Data-Science-Projekten nicht mehr wegzudenken. Wir nutzen es in der lokalen Entwicklung, zum Testen und als Deliverable in Kundenprojekten. Zusätzlich machen wir uns seit einiger Zeit die Möglichkeit der Parallelisierung von Prozessen mittels Docker in Kombination mit Kubernetes zunutze. Insbesondere die gewonnene Unabhängigkeit von Betriebssystemen und die stark vereinfachte Übergabe von Code an Kunden haben wir in den letzten Jahren sehr zu schätzen gelernt. Auch wenn Docker ein wenig Einarbeitung erfordert macht es aus unserer Sicht durchaus Sinn, diese Zeit zu investieren, um langfristig von den Vorteilen die Docker bietet, zu profitieren. Sollten Sie Unterstützung bei Ihren ersten Schritten mit Docker benötigen, schauen Sie sich doch gern einmal unsere Einführungsschulung zu Docker an.