Docker

Aus Jonas Notizen Webseite
Zur Navigation springen Zur Suche springen

Docker ist eine 2013 veröffentlichte, Freie Software zur Isolierung von Anwendungen mit Hilfe von Containervirtualisierung.

Docker ist eine Containervirtualisierungsplattform, die viele der vorstehenden Techniken einsetzt und um benutzerfreundliche Werkzeuge und Dienste ergänzt. Dazu gehört beispielsweise eine Beschreibung von Images (Dockerfiles) oder ein Repository, das solche Images verwaltet. Der gleichnamige Hersteller bietet für die Plattform viele weitere Ergänzungen an, einige davon kostenlos, andere kostenpflichtig.

Quelle des Textes: deutschen Wikipedia


Operation

Docker kann eine Anwendung und ihre Abhängigkeiten in einen virtuellen Container packen, der auf jedem Linux-Server laufen kann. Die Abhängigkeiten beinhalten laut Aussage der Entwickler: Code, Laufzeitmodul, Systemwerkzeuge, Systembibliotheken – alles was auf einem Rechner installiert werden kann. [1]

Dadurch kann die Anwendung leicht als Datei transportiert an verschiedenen Orten ausgeführt werden, z. B. vor Ort, in einer öffentlichen Cloud und/oder in einer privaten Cloud. Docker nutzt die Ressourcen-Isolationsfunktionen des Linux-Kernels (wie cgroups und Kernel-Namensräume) und ein unionsfähiges Dateisystem, um die Ausführung von Containern innerhalb einer einzigen Linux-Instanz zu ermöglichen und den Overhead beim Starten und Warten virtueller Maschinen zu vermeiden.

Die Unterstützung von Namensräumen durch den Linux-Kernel isoliert meist die Sicht einer Anwendung auf die Betriebsumgebung, einschließlich:

  • Prozessbäumen,
  • Netzwerk,
  • Benutzer-IDs und
  • gemounteten Dateisystemen,

während cgroups eine Ressourcenbegrenzung für Speicher und CPU bietet.

Begriffe

Image
ein Speicherabbild eines Containers. Das Image selbst besteht aus mehreren Layern, die schreibgeschützt sind und somit nicht verändert werden können. Ein Image ist portabel, kann in Repositories gespeichert und mit anderen Nutzern geteilt werden. Aus einem Image können immer mehrere Container gestartet werden.
Container
als Container wird die aktive Instanz eines Images bezeichnet. Der Container wird also gerade ausgeführt und ist beschäftigt. Sobald der Container kein Programm ausführt oder mit seinem Auftrag fertig ist, wird der Container automatisch beendet.
Layer
ein Layer ist Teil eines Images und enthält einen Befehl oder eine Datei, die dem Image hinzugefügt wurde. Anhand der Layer kann die ganze Historie des Images nachvollzogen werden.
Dockerfile
eine Textdatei, die mit verschiedenen Befehlen ein Image beschreibt. Diese werden bei der Ausführung abgearbeitet und für jeden Befehl ein einzelner Layer angelegt.
Repository
ein Repository ist ein Satz gleichnamiger Images mit verschiedenen Tags, zumeist Versionen.
Registry
eine Registry, wie zum Beispiel Docker Hub oder Artifactory, dient der Verwaltung von Repositories.
libcontainer
eine Schnittstelle zu den Grundfunktionen von Docker.
libswarm
eine Schnittstelle, um Docker-Container zu steuern.
libchan
ermöglicht eine einfache („light weighted“) Kommunikation zwischen Prozessteilen und Prozessen.


Persistierung der Daten im Container mit Volumes

Quelle: Einführung / Übersicht Docker und Container-Virtualisierung - Sehr empfehlenswerter Artikel!

Container werden üblicherweise nicht gepatcht und gepflegt, sondern im Falle eines Problems oder neueren Version einfach durch einen neuen Container ersetzt. Da alles, was im Container enthalten ist, verloren geht, muss man sich vor der Erstellung eines Containers Gedanken machen, ob in der darin betriebenen Applikation Daten anfallen, die nach einem Fehler/Neustart/Upgrade weiterhin bestehen bleiben sollen. Für diese Daten gibt es verschiedene Speichermöglichkeiten: So kann für Docker-Container vom Hostsystem Ordner mounten oder aber Docker-Volumes erstellen.


Docker-Volumes sind Daten-Container, die einem Container zugewiesen werden, aber bestehen bleiben, wenn der Container neu erstellt wird. Dann wird das Docker-Volume an den neuen Container gebunden, so dass die im Docker-Volume gespeicherten Daten auch Neustarts und Upgrades überstehen. Da in einem Container-Cluster auch Skalierung möglich sein soll, werden hier Volumes benötigt, auf die alle Nodes bzw. alle Container zugreifen können. Auch dafür bestehen Varianten der Docker-Volumes in den entsprechenden Cluster-Managementtools.


Damit diese Volumes auch beständig bleiben, gibt es persistenten Speicher, der in der Regel (Bei verwendung einer Containerplattform, siehe unten) über ein Cluster-Filesystem, auf das alle Nodes zugreifen können, verfügbar gemacht wird. Dort werden Volumes in bestimmten Größen bereitgestellt, die an Container gebunden werden können und bestehen bleiben, auch wenn die zugehörigen Container gelöscht werden


Container Images über Registries bereitstellen

Nicht nur die Container benötigen Speicher für ihre Anwendungen – es wird auch auch ein Ort benötigt, um die Containerimages zu sammeln. Zum einen existiert Docker Hub als zentrale Anlaufstelle, um Containerimages zu beziehen. Dort findet man von Softwareanbietern zum Teil offizielle vorgefertigte Containerimages, aber der Open Source-Gedanke ermöglicht es jedem, die eigenen Container dort öffentlich zur Verfügung zu stellen. Gerade für Unternehmen kann dies ein potentielles Sicherheitsrisiko bedeuten, wenn nicht ausführlich geprüft wird, welche Prozesse und Dienste in einem solchen Container laufen.

In den meisten Fällen entwickeln Unternehmen jedoch ihre eigenen Microservices und möchten diese oft nicht öffentlich freigeben bzw. auch die verschiedenen Versionen ihrer Container in Entwicklung zentral in der eigenen Plattform unterbringen. Für diesen Zweck gibt es Registries wie die Docker Registry, die auf einem eigenen Server gehostet werden, in denen die Images abgelegt und somit im eigenen Netz verfügbar gemacht werden können. Dabei spielen die Tags der Containerimages die Rolle der Versionskontrolle, so dass auch gezielt Upgrades und Rollbacks von Containern möglich sind.


Dockerfile

Docker kann Images automatisch erstellen, indem die Anweisungen aus einer Dockerfile gelesen werden. Ein Dockerfile ist ein Textdokument, das alle Befehle enthält, die ein Benutzer auf der Befehlszeile aufrufen kann, um ein Bild zusammenzusetzen. Mit docker build können Benutzer einen automatischen Build erstellen, der mehrere Befehlszeilenanweisungen nacheinander ausführt.


Der Befehl Docker Build erstellt ein Image aus einer Docker-Datei und einem Kontext. Der Kontext des Builds besteht aus den Dateien an einem bestimmten Speicherort PATH oder URL. Der PATH ist ein Verzeichnis im lokalen Dateisystem. Die URL ist ein Git-Repository-Speicherort.

Ein Kontext wird rekursiv verarbeitet. Ein PATH enthält also alle Unterverzeichnisse und die URL enthält das Repository und seine Submodule. Dieses Beispiel zeigt einen Build-Befehl, der das aktuelle Verzeichnis als Kontext verwendet:

$ docker build .

Sending build context to Docker daemon  6.51 MB
...

Der Build wird vom Docker-Daemon ausgeführt, nicht von der CLI.

Das erste, was ein Erstellungsprozess tut, ist, den gesamten Kontext (rekursiv) an den Dämon zu senden. In den meisten Fällen ist es am besten, mit einem leeren Verzeichnis als Kontext zu beginnen und Ihre Docker-Datei in diesem Verzeichnis zu belassen. Man sollte nur Dateien hinzufügen, die zum Erstellen der Docker-Datei erforderlich sind. Sprich: / als Kontext anzugeben wäre sehr schlecht, da Docker dann den gesamten Inhalt der Festplatte kopieren und an den Docker Dämon schicken würde.

.dockerignore

Bevor die Docker-CLI den Kontext an den Docker-Daemon sendet, sucht diese im Stammverzeichnis des Kontexts nach einer Datei mit dem Namen .dockerignore. Wenn diese Datei vorhanden ist, ändert die CLI den Kontext so, dass Dateien und Verzeichnisse ausgeschlossen werden, die mit den darin enthaltenen Mustern übereinstimmen. Auf diese Weise kann vermieden werden, dass große oder vertrauliche Dateien und Verzeichnisse unnötig an den Dämon gesendet und möglicherweise mithilfe von ADD oder COPY zu Bildern hinzugefügt werden.


Die CLI interpretiert die .dockerignore-Datei als eine durch Zeilenumbrüche getrennte Liste von Mustern, die den Datei-globs von Unix-Shells ähneln. Für den Abgleich wird das Stammverzeichnis des Kontexts sowohl als Arbeitsverzeichnis als auch als Stammverzeichnis betrachtet. Beispielsweise schließen die Muster /foo/bar und foo/bar eine Datei oder ein Verzeichnis mit dem Namen bar im Unterverzeichnis foo von PATH oder im Stammverzeichnis des Git-Repositorys unter URL aus. Beides schließt nichts anderes aus.

Wenn eine Zeile in der .dockerignore-Datei mit # in Spalte 1 beginnt, wird diese Zeile als Kommentar betrachtet und ignoriert, bevor sie von der CLI interpretiert wird.


Beispiel einer .dockerignore-Datei:

# Kommentar
*/temp*
*/*/temp*
/test/**/*.go
!/test/**/*.spec.go
temp?

Beschreibungen der einzelnen Anweisungen dieser .dockerignore-Datei:

Regel Verhalten
# comment Ignoriert
*/temp* Schließt Dateien und Verzeichnisse aus, deren Namen in einem beliebigen unmittelbaren Unterverzeichnis des Stammverzeichnisses mit temp beginnen. Zum Beispiel wird die einfache Datei /somedir/temporary.txt ausgeschlossen, ebenso wie das Verzeichnis /somedir/temp.
*/*/temp* Schließt Dateien und Verzeichnisse, die mit temp beginnen, aus allen Unterverzeichnissen aus, die zwei Ebenen unterhalb des Stammverzeichnisses liegen. Zum Beispiel wird /somedir/subdir/temporary.txt ausgeschlossen.
/test/**/*.go


!/test/**/*.spec.go

Docker unterstützt auch eine spezielle Platzhalter-Zeichenfolge **, die mit einer beliebigen Anzahl von Verzeichnissen (einschließlich Null) übereinstimmt.

Dieses Beispiel schließt alle Dateien mit der Erweiterung .go und beliebigem Namen aus, dass zudem einen Ordner namens test in seiner vorlaufenden Stammbaum-Hierarchie besitzt. Zum Beispiel werden /test/component.go und /test/a/b/c/component.go ausgeschlossen.


Zeilen die mit dem Anfangsbuchstaben ! (Ausrufezeichen) beginnen, können verwendet werden, um Ausnahmen von Ausschlüssen zu machen.

In diesem Beispiel wird zum Beispiel die Datei /test/component.spec.go von der hervorgehenden Regel ausgeschlossen und wird im Kontext nicht ausgeschlossen.

temp? Schließt Dateien und Verzeichnisse im Stammverzeichnis aus, deren Namen eine Ein-Zeichen-Erweiterung von temp sind. Beispielsweise werden /tempa und /tempb ausgeschlossen.


Grundlegende Struktur

Eine Dockerfile hat die folgende Struktur:

# Kommentar
# parserdirective=value1
ANWEISUNG argumente

Bei der wird ANWEISUNG wird nicht zwischen Groß- und Kleinschreibung unterschieden. Es ist jedoch üblich, dass GROSSBUCHSTABEN verwendet werden, um sie leichter von Argumenten unterscheiden zu können.

Docker führt Anweisungen in einer Dockerdatei der Reihe nach aus. Ein Dockerfile muss mit einer FROM-Anweisung beginnen. Dies kann nach Parser-Direktiven, Kommentaren und global-scoped ARGs erfolgen. Die FROM-Anweisung gibt das Parent-Image an, aus dem man baut. FROM darf nur eine oder mehrere ARG-Anweisungen vorangestellt werden, die Argumente deklarieren, die in FROM-Zeilen im Dockerfile verwendet werden.

Umgebungs-Variablen

Umgebungsvariablen (mit der ENV-Anweisung deklariert) können in bestimmten Anweisungen auch als Variablen verwendet werden, die vom Dockerfile interpretiert werden müssen. Es werden auch Escapes behandelt, um eine variablenähnliche Syntax wörtlich in eine Anweisung aufzunehmen.

Die unterstützten Operationen sind ${FOO-default} (default nutzen, wenn FOO nicht gesetzt ist) und ${FOO:-Standard} (ziehen Sie sich zurück, wenn das FOO nicht gesetzt oder leer ist).


Der Variablen-Zugriff mit dem Syntax ${Variablenname} unterstützt die folgenden Standard-Bash-Modifikatoren:

  • ${FOO-default} (Fallback-Wert verwenden, wenn FOO nicht gesetzt ist) und
  • ${FOO:-default} (Fallback-Wert verwenden, wenn FOO nicht gesetzt oder leer ist).



Anweisung/Befehl Unterstützt

Umgebungs

-Variablen

Beschreibung
FROM ✔️
RUN
CMD
LABEL ✔️
MAINTAINER (veraltet) Legt das Feld Author der erzeugten Images fest.

Die LABEL-Anweisung ist eine sehr viel flexiblere Version davon und man sollte diesen Befehl nicht mehr nutzen.

EXPOSE ✔️
ENV ✔️

Die Umgebungsvariablen, die mit ENV gesetzt werden, bleiben erhalten, wenn ein Container aus dem resultierenden Image ausgeführt wird.


Wenn eine Umgebungsvariable nur während der Erstellung und nicht im endgültigen Image benötigt wird, sollte stattdessen entweder

  • ein Wert für einen einzelnen Befehl festlegen:
    RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...
    
  • oder die ARG-Anweisung genutzt werden:
    ARG DEBIAN_FRONTEND=noninteractive
    RUN apt-get update && apt-get install -y ...
    
ADD ✔️
COPY ✔️
ENTRYPOINT
VOLUME ✔️
USER ✔️
WORKDIR ✔️
ARG
ARG <name>[=<default value>]
Die ARG-Anweisung definiert eine Variable, die Benutzer zur Build-Zeit an den Builder übergeben können. ARG-Anweisungen können einen optionalen Fallback-Wert besitzen.


Es wird nicht empfohlen, ARG-Variablen für die Weitergabe von Geheimnissen zu verwenden. Die Werte von Build-Time-Variablen sind für jeden Benutzer des Images mit dem Befehl docker history sichtbar.


Eine ARG-Variablendefinition tritt ab der Zeile in Kraft, in der sie im Dockerfile definiert ist, nicht ab der Verwendung des Arguments in der Befehlszeile oder an anderer Stelle.

Eine ARG-Anweisung verlässt den Anwendungsbereich am Ende der Bauphase, in der sie definiert wurde. Um ein Argument in mehreren Stufen zu verwenden, muss jede Stufe die ARG-Anweisung enthalten:
FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS

FROM busybox
ARG SETTINGS
RUN ./run/other $SETTINGS

ONBUILD ✔️*
STOPSIGNAL ✔️
HEALTHCHECK
SHELL

* (in Kombination mit einer der oben unterstützten Anweisungen)

Multi-Container Docker-Anwendungen mit Docker Compose

Docker Compose ist ein Werkzeug zur Definition und Ausführung von Docker-Anwendungen mit mehreren Containern.

Es verwendet YAML-Dateien zur Konfiguration der Dienste der Anwendung und führt den Erstellungs- und Startvorgang aller Container mit einem einzigen Befehl durch.

Mit dem CLI-Dienstprogramm docker-compose können Benutzer Befehle für mehrere Container gleichzeitig ausführen, z. B. um Images erstellen, Container zu skalieren, gestoppte Container auszuführen und vieles mehr. Befehle, die sich auf die Manipulation von Images oder auf benutzerinteraktive Optionen beziehen, sind in Docker Compose nicht relevant, da sie sich auf einen Container beziehen.




Containerplattformen: Kubernetes, Docker Swarm, ..

Quelle + Viele weiterführende Informationen: Einführung / Übersicht Docker und Container-Virtualisierung - Sehr empfehlenswerter Artikel!


Mit Docker alleine kann man Container starten und dabei einige Parameter mitgeben (wie die Mountpoints oder das Portforwarding), damit diese dann auch von außerhalb des internen Docker-Netzwerks erreichbar sind. Wenn man aber viele Container auf einmal starten muss, ist dieser Weg sehr umständlich und Automatisierung ist wünschenswert. Außerdem verwaltet das klassische Docker nur den Host, auf dem es läuft, wodurch die Anzahl der Container schnell begrenzt ist. Daher werden Container-Cluster verwendet, die aus vielen Servern bestehen. Für die Umsetzung eines solchen Clusters gibt es bereits die ersten Wahlmöglichkeiten, z. B. Docker Swarm Mode, Kubernetes und Rancher.

Skalierbarkeit

Cluster-Managementtools kümmern sich um Aufgaben, die erst in Clustern eine Rolle spielen, wie z. B. Loadbalancing, Scheduling und Skalierung der Container.

Skalierung bedeutet bei Containern in der Regel, dass ein bestimmter Container mehrfach über verschiedene Server erstellt wird und die Anfragen über einen Loadbalancer auf diese Container verteilt werden, damit nicht ein einzelner Container überlastet wird. Diese Skalierung kann manuell oder dynamisch abhängig von Parametern wieder CPU-, RAM- oder Netzwerkauslastung geschehen. Dadurch können Lasten von Frontend-Diensten verteilt werden, die unter Umständen an einem gemeinsamen Backend wieder auf die gleiche Datenbank zugreifen. Eine solche Skalierbarkeit muss allerdings schon im Design der Anwendung berücksichtigt sein, denn nicht alle Dienste lassen sich problemlos skalieren.