Open Source Entwicklung XML

Docker – getting started

Willkommen zum Start unserer Veröffentlichungsreihe „Docker vs. OpenShift“. In den kommenden Wochen werden wir Docker und OpenShift themenorientiert gegenüberstellen und vergleichen.



Den Anfang machen zwei „Getting started“-Beiträge, um einen Überblick zu beiden Tools zu geben. Anschließend warten unter anderem Themen wie

  • Container-Builds
  • persistente Datenhaltung
  • Rollout- und Update-Strategien
  • Security in Containern

und viele mehr, um weiter in das Thema Containerisierung einzutauchen. 

Zu Beginn unserer Einführung kommen wir an ein paar Grundlagen nicht vorbei:

Docker & Definitionen

Was ist Containerisierung mit Docker?
Docker fasst bekannte Verfahren wie control groups und namespaces zur Prozessisolierung zusammen, reichert sie an und vereint sie unter dem Begriff der Containerisierung. Dabei ist Docker nicht die einzige Software, die dies macht. So gibt es zum Beispiel auch Podman oder für größere Umgebungen Orchestrierungs-Tools wie Openshift oder Kubernetes.

Wieso sollte man Container überhaupt einsetzen? Hierzu einige Überlegungen:

  • Containerisierung isoliert Anwendungen/Prozesse. Container ermöglichen es, eine bestimmte Version einer Anwendung zu betreiben, deren Abhängigkeiten zusammenzufassen und mögliche Sicherheitsrisiken zu isolieren
  • Das Testen neuer Anwendungen oder Anwendungsversionen ist mit Containern schnell und problemlos möglich, ebenso wie das Rollback auf eine vorherige Version
  • Portierbarkeit: Hat man einen funktionierenden Container erstellt, ist es einfach diesen auf einem anderen System genauso wieder zu erstellen und zu nutzen – damit wird auch das bekannte Problem „works on my machine“ teilweise reduziert/gelöst

Inzwischen setzen viele, bekannte Unternehmen wie Alphabet (also Google und Youtube), Netflix und Paypal auf Container. Gerade im Fall von Google und Youtube werden Container nicht „einfach nur“ mit Docker genutzt, sondern in Kombination mit einer Container-Anwendungsplattform (hier: Kubernetes). Dies ermöglicht unter anderem eine flexible Skalierung, um auf erhöhten Ressourcenbedarf schnell reagieren zu können.

Verschiedene Editionen für verschiedene Zielgruppen

In diesem „Getting started"-Guide wird mit der Community Edition von Docker, kurz Docker CE, gearbeitet. Dabei handelt es sich um die frei verfügbare Version. Docker CE bietet keinen Support seitens des Unternehmens Docker und läuft unter Linux. Die Docker Enterprise Edition (Docker EE) hingegen bietet Support sowie zusätzliche, nützliche Features. Sollte Hilfe bei der Installation der Docker CE benötigt werden, bietet Docker weitere Information dazu an: Get Docker und Install Docker sind hilfreiche Quellen.

Für Mac und Windows gibt es „Docker-Desktop“, worauf in diesem Guide aber nicht weiter eingegangen wird. Jedoch sind die grundlegenden Prinzipien gleich zu der Docker CE/EE.

Weiterhin werden für diesen Guide nur die sogenannten Ad-hoc Befehle genutzt. Sie ermöglichen es, schnell und einfach einen Container über die command line zu erstellen. Für komplexere Anwendungsfälle bieten sich Dockerfiles an. Mehr Informationen dazu bietet ebenfalls Docker an. 

Nun zu ein paar Begriffen, die für den Umgang mit Docker (und Containern im Allgemeinen) wichtig sind:

  • Docker Engine
    • Das „eigentliche“ Docker, welches Container ausführt, Images verwaltet usw.
  • Container
    • ist eine (aktive) Instanz eines Images
  • Image
    • nur lesbares Template für einen Container
    • enthält alle benötigten Dateien einer Anwendung
    • besteht aus mehreren Layern
  • Layer
    • eine Schicht innerhalb eines Images
    • beinhaltet alle Änderungen an einem Image ab der Erzeugung des letzten Layers bis zur Erzeugung des aktuellen Layers
  • Repository
    • Sammlung aller Versionen eines Images
    • Versionen werden anhand von Tags (z.B. „latest“) identifiziert
  • Registry

Docker Images

Erstellen eines Containers

Für unseren ersten Container nehmen wir als Beispiel einen Webserver, um genau zu sein der Webserver des Apache-Projekts oder kurz: „httpd“.

Dazu werden zunächst Informationen darüber benötigt, wie das entsprechende Repository heißt (sofern es existiert). Die Docker-eigene Registry erreichen wir über http://hub.docker.com/. Dort suchen wir nach „Apache httpd“ und erfahren, wie es geladen werden kann:
$ docker pull httpd

Damit wird die Docker Engine das Image des httpd herunterladen. Tagging wird verwendet, um eine spezielle Version des Images auszuwählen. Entsprechend sollte dem Pull-Request auch immer ein Tag mitgegeben werden, der die Versionsnummer enthält. Wird kein Tag angegeben, so wird als „fall back“ der Standardwert „latest“ genutzt. Tags werden dabei - immer durch einen Doppelpunkt getrennt - an den Namen des Images angehangen. Die verfügbaren Tags finden wir im Repository des Images.  

Überblick über verfügbare Tags für httpd

Somit ist klar, dass wir zum Beispiel den Tag „2.4“ nutzen können: 
$ docker pull httpd:2.4

Best Practice TIPP: Besonders in Produktivumgebungen ist es nicht zu empfehlen, einfach den Tag „latest“ zu nutzen, sondern eine Version festzulegen.

Notice: Seit vergangenem Jahr hat Docker ein pull-Limit eingeführt. Damit kann man als nicht registrierter Nutzer „nur noch“ 100 Images in sechs Stunden beziehen. Als registrierter Nutzer sind es 200 Images und als zahlender Kunde sind es 50.000 in 24 Stunden.

Wenn das Image heruntergeladen ist, können wir prüfen, ob es auch zur Verfügung steht, in dem wir uns anzeigen lassen, welche Images lokal vorhanden sind. Das erfolgt mit folgendem Befehl: 
$ docker images

Die resultierende Ausgabe sollte mindestens folgende Informationen enthalten:

Es ist ersichtlich, dass das Image heruntergeladen und verfügbar ist. Somit ist es jetzt direkt nutzbar.

Run docker run

Der Befehl „run“ startet eine neue Instanz des angegebenen Images (also einen neuen Container) und die Option „-d“ sorgt dafür, dass der Container im Hintergrund ausgeführt und dessen Ausgabe nicht angezeigt wird.
$ docker run -d httpd

Best Practice TIPP: Wird dieses Argument weggelassen, wird nur die Log-Ausgabe des Containers angezeigt und weitere Eingaben sind nicht möglich. Daher gehört es zur Best Practice, immer „-d“ zu nutzen, insbesondere in Produktivumgebungen.

Nach der Eingabe des Kommandos ist zu sehen, wie die docker engine das benötigte Image herunterlädt. Das verdeutlicht uns ein Verhalten der docker engine: Wollen wir eine bestimmte Version nutzen, müssen wir diese auch angeben, ansonsten nutzt „docker run“ den „latest“-Tag des Images.
$ docker run -d httpd:2.4

Dieser Befehl startet einen neuen Container auf Basis des Images mit dem Versions-Tag 2.4. Nun schauen wir nach, ob unser Container auch tatsächlich läuft:
$ docker ps

Die Ausgabe von „docker ps“ sollte wie folgt aussehen:

Dabei ist erkennbar, dass zwei Container laufen. Ein Container auf Basis des „latest“-Images und ein Container auf Basis des Images mit der Version 2.4.

Nebst einer „Container ID“ wurden den Containern auch zufällige Namen zugeteilt. Das lässt sich auch manuell konfigurieren. Doch zuerst beenden wir die zwei laufenden Container, indem wir sie stoppen:
$ docker stop 799 romantic_neumann
Hier bitte die eigenen IDs/Namen nutzen.

Notice: Container können direkt über die ID - dank Autovervollständigung auch nur mit Teilen der ID (siehe: „799“) - oder auch mit ihrem Namen (siehe: „romantic_neumann“) angesprochen werden.

Ein anderer Weg als „stop“ ist der Befehl „kill“. Dieser entspricht jedoch einem „harten“ Ausschalten und könnte zu Problemen führen. Gestoppte Container können über den Befehl „start“ auch wieder in Betrieb gesetzt werden.

Führen wir nach dem Stoppen der Container nun „docker ps“ erneut aus, erhalten wir eine leere Ausgabe. Das liegt daran, dass die Container beendet sind und sie „per Default“ nicht angezeigt werden. Das kann mit der Option „-a“ (für all) erreicht werden:
$ docker ps -a

Somit wird eine Übersicht über alle – also auch gestoppte – Container ausgegeben. Da beide Container nicht mehr gebraucht werden, sollten sie über den Befehl „rm“ (für remove) gelöscht werden:
$ docker rm 799 romantic_neumann

Die eigene Website

Nun starten wir einen neuen Container und geben gleich ein paar weitere Argumente mit:
$ docker run -d --name Apache -p 8080:80 httpd:2.4

Mittels des Arguments „--name“ wird dem entstehenden Container ein selbstdefinierter Name (hier: „Apache“) mitgegeben. Namen müssen dabei eindeutig sein und können nicht doppelt verwendet werden – egal ob ein Container läuft oder gestoppt ist.

Das Argument „-p“ sorgt dafür, dass der Port 80 des Containers am Port 8080 des Docker-Hosts veröffentlicht wird oder in der Logik des Befehls, dass die Möglichkeit geschaffen wird, über den Host-Port 8080 auf den Port 80 des Containers zuzugreifen.

Somit kann auf die Website zugegriffen werden, die mittels des „httpd“-Containers bereitgestellt wird. Je nachdem wo Docker ausgeführt wird (und ob die lokale Firewall richtig konfiguriert ist), kann nun mittels Browser auf die IP des docker-hosts an Port 8080 zugegriffen werden. Die Default Website des Apache „httpd“ sollte jetzt angezeigt werden.

Fassen wir also kurz zusammen: Bis hierhin haben wir mit grundlegenden Kommandos eine Version des Images „httpd“ des Apache Projekts aus der Docker Registry heruntergeladen, zum Laufen gebracht als auch Container gestoppt und entfernt.

Was für einen Webserver noch fehlt, ist eine „richtige“ Website. Klar, eine Website hier zu erstellen ist in einem Container-Guide etwas fehl am Platz. Aber welche Möglichkeit wir haben, diese dem Container zur Verfügung zu stellen, soll hier noch kurz dargelegt werden.

Wenn nur die Default Website – also die Index.html des „httpd“ mit einer anderen ersetzt werden soll, kann sie in den laufenden Container hineinkopiert werden. Im folgenden Beispiel liegt im home-Verzeichnis des Users bereits eine nutzbare index.html-Datei:
$ docker cp ~/index.html Apache:/usr/local/apache2/htdocs/index.html

Der Befehl „cp“ nimmt zuerst die Quelle entgegen, hier also das home-Verzeichnis des Users. Darauf folgt das Ziel. Da es sich hierbei um einen bestimmten, laufenden Container handelt, wird dessen Name, gefolgt von einem Doppelpunkt und dem Pfad zur Datei angegeben. Auf diesem Weg wird zur Laufzeit des Containers eine (oder mehrere) Datei(en) in den Container kopiert. Auch ein Kopieren vom Container auf den docker-host ist damit möglich.

Für komplexere Websites ist das allerdings unpraktikabel und nicht empfohlen. Hier hilft dir der Befehl „run“ weiter, denn er bietet dir die Möglichkeit, über den Paramater „-v“ Dateien oder Ordner einzubinden.

Ebenso nützlich kann da der Paramater „--mount“ sein. Für mehr Informationen dazu haben wir die entsprechenden Artikel seitens Docker verlinkt:

Weitere Trainings-Ressourcen bietet Docker selbst an.

Somit sind wir im Rahmen des „Getting started“-Guides am Ende angekommen. Die kommenden Artikel in unserer Container-Reihe werden verschiedenste Aspekte rund um Container beleuchten, so zum Beispiel das Thema „Storage“ in Docker („Storage in Docker und K8s“ ca. KW35), was in diesem Artikel bereits angeschnitten wurde.

Autor

Johannes StieberAssociate IT-Consultant
Johannes Stieber