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

Beim Syntax der 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 oder ggf. auch ARG-Anweisung deklariert) können in bestimmten Anweisungen auch als Variablen verwendet werden, die vom Dockerfile interpretiert werden.

Wie beim Befehlszeilen-Parsing können Anführungszeichen und Backslashes verwendet werden, um Leerzeichen innerhalb von Werten einzuschließen:

ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy


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).


Übersicht der Dockerfile Anweisungen

Anweisung/Befehl Unterstützt

Umgebungs

-Variablen

Beschreibung
FROM
FROM [--platform=<platform>] <image> [AS <name>]
# Oder
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
# Oder
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
✔️ Die FROM-Anweisung initialisiert eine neue Bauphase und legt das Basis-Image für nachfolgende Anweisungen fest.
  • ARG ist die einzige Anweisung, die im Dockerfile vor FROM stehen darf.
  • FROM kann innerhalb eines einzigen Dockerfiles mehrfach erscheinen, um mehrere Images zu erzeugen oder um einen Bauabschnitt als Abhängigkeit für einen anderen zu verwenden. Hierzu notiert man sich einfach vor jedem neuen FROM-Befehl die letzte vom Commit ausgegebene Image-ID. Jeder FROM-Befehl löscht jeden Zustand, der durch vorherige Befehle erzeugt wurde.
  • Optional kann einem neuen Bauabschnitt ein Name gegeben werden, indem AS-Name zur FROM-Anweisung hinzugefügt wird. Der Name kann in nachfolgenden FROM- und COPY --from=<name>-Anweisungen verwendet werden, um auf das in dieser Stufe erstellte Image zu verweisen.
  • Die tag- oder digest-Werte sind optional. Wenn einer von ihnen weggelassen wird, nimmt der Builder standardmäßig den latest-Tag an. Der Builder gibt einen Fehler zurück, wenn er den Tag-Wert nicht finden kann.
  • Falls die FROM-Anweisung auf ein Multi-Plattform-Image verweist kann mit dem optionalen --plattform-Flag festgelegt werden. Zum Beispiel linux/amd64, linux/arm64 oder windows/amd64.
RUN
# shell form, the command is run in a shell
RUN <command>
# exec form
RUN ["executable", "param1", "param2"]
Der RUN-Befehl führt alle Befehle in einer neuen Ebene über dem aktuellen Image aus und überträgt die Ergebnisse. Das resultierende festgeschriebene Image wird für den nächsten Schritt in der Dockerfile verwendet.

Die Schichtung von RUN-Anweisungen und die Generierung von Commits entspricht den Kernkonzepten von Docker, wo Commits billig sind und Container von jedem Punkt in der Geschichte eines Bildes aus erstellt werden können, ähnlich wie bei der Quellcodekontrolle.

Die exec-Form ermöglicht es, Shell-String-Munging zu vermeiden und RUN-Befehle unter Verwendung eines Basis-Images auszuführen, das die angegebene ausführbare Shell-Datei nicht enthält.

CMD
# exec form, this is the preferred form:
CMD ["executable","param1","param2"]
# as default parameters to ENTRYPOINT:
CMD ["param1","param2"]
# shell form:
CMD command param1 param2
Mit CMD kann ein Standardbefehl definiert werden, der beim Start des Containers ausgeführt wird. Der Hauptzweck eines CMD ist die Bereitstellung von Vorgaben für einen ausführenden Container.

Wenn man zum Beispiel ein Dockerfile für eine Webanwendung erstellt, wäre es sinnvoll, den Anwendungsserver Ihrer Webanwendung mithilfe von CMD zu starten.


Im Gegensatz zur Shell-Form ruft die exec-Form keine Befehlsshell auf. Dies bedeutet, dass die normale Shell-Verarbeitung nicht stattfindet. Zum Beispiel führt CMD [ "echo", "$HOME" ] keine Variablenersetzung /Variable Sustitution) auf $HOME durch.


In einem Dockerfile kann es nur eine CMD-Anweisung geben.

LABEL
LABEL <key>=<value> <key>=<value> <key>=<value> ...
✔️ Die LABEL-Anweisung fügt Metadaten zu einem Bild hinzu. Ein LABEL ist ein Schlüssel-Werte-Paar. Um Leerzeichen innerhalb eines LABEL-Wertes einzuschließen werden, wie beim Befehlszeilen-Parsing, Anführungszeichen und umgekehrte Schrägstriche verwendet.


Labels, die in Basis- oder Eltern-Images (Bilder in der FROM-Zeile) enthalten sind, werden an das aktuelle Image vererbt. Wenn ein Label bereits vorhanden ist, aber einen anderen Wert hat, überschreibt der zuletzt angewandte Wert jeden zuvor eingestellten Wert.


Der Befehl docker image inspect wird verwendet, um die Labels eines Images anzuzeigen. Man kann die Option --format verwenden, um nur die Labels anzeigen zu lassen: docker image inspect --format='' myimage

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
EXPOSE <port> [<port>/<protocol>...]
✔️ Die EXPOSE-Anweisung informiert Docker darüber, dass der Container zur Laufzeit an den angegebenen Netzwerkports lauscht.

Die EXPOSE-Anweisung veröffentlicht den Port nicht wirklich. Sie fungiert als eine Art Dokumentation zwischen der Person, die das Image erstellt, und der Person, die den Container betreibt, darüber, welche Ports veröffentlicht werden sollen.

Die eigentliche Veröffentlichung der Ports wird beim Ausführen des docker run-Befehls realisiert, in dem man diesem gewisse Flaggen wie -p oder -P übergibt.


Standardmäßig nimmt EXPOSE /tcp als Protokoll an.

ENV
ENV <key>=<value> ...
✔️

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
ADD [--chown=<user>:<group>] <src>... <dest>
# Oder (Für Pfade die Leerzeichen enthalten)
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
✔️ Die ADD-Anweisung kopiert neue Dateien, Verzeichnisse oder entfernte Datei-URLs von <src> und fügt sie dem Dateisystem des Bildes unter dem Pfad <dest> hinzu.

Jedes <src> kann Platzhalter enthalten, und der Abgleich erfolgt unter Verwendung der filepath.Match-Regeln von Go, wie in .dockerignore. (Bereits im oberen Kapitel über .dockerignore mit Beispielen angesprochen)


Mehrere <src> Ressourcen können angegeben werden, aber wenn es sich um Dateien oder Verzeichnisse handelt, werden ihre Pfade als relativ zur Quelle des Kontextes des Builds interpretiert. Das folgende Beispiel verwendet einen relativen Pfad und fügt test.txt zu <WORKDIR>/relativeDir/ hinzu:
ADD test.txt relativeDir/


ADD unterliegt noch vielen weiteren, spezifischeren Regeln. Siehe dazu die verlinkte Dokumentationsseite.

COPY
COPY [--chown=<user>:<group>] <src>... <dest>
# Oder (Für Pfade die Leerzeichen enthalten)
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
✔️ COPY und ADD sind beides Dockerfile-Anweisungen, die ähnlichen Zwecken dienen. Mit ihnen können Dateien von einem bestimmten Ort in ein Dockerfile-Image kopiert werden.


Mit COPY können nur lokale Dateien oder ein lokale Verzeichnisse des Hosts (dem Rechner, der das Docker-Image erstellt) in das Docker-Image selbst kopiert werden.

ADD hingegen unterstützt darüber hinaus 2 weitere Möglichkeiten als Quelle: Ersten kann eine URL anstelle einer lokalen Datei oder eines lokalen Verzeichnisses angegeben werden. Zweitens kann eine tar-Datei von der Quelle direkt in das Ziel extrahiert werden. (ADD rootfs.tar.gz /). Der zweite Anwendungsfall ist einer der einzig validen Gründe, den ADD-Befehl dem COPY-Befehl vorzuziehen.[2][3]

ENTRYPOINT
# EXEC-Form (Preferred)
ENTRYPOINT ["executable", "param1", "param2"]
# SHELL-Form
ENTRYPOINT command param1 param2
Ein ENTRYPOINT ermöglicht es Einem, einen Container zu konfigurieren, der als ausführbare Datei ausgeführt wird. Diese Anweisung unterliegt noch vielen weiteren Regeln, siehe die links verlinkte Offizielle Dokumentation. Beispiel:
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
VOLUME
VOLUME ["/data"]
✔️ Die VOLUME-Anweisung erstellt einen Einhängepunkt mit dem angegebenen Namen und markiert ihn als extern eingebundene Volumes von einem nativen Host oder anderen Containern. Siehe "Share Directories via Volumes" für weitere Informationen.
USER
USER <user>[:<group>]
# Oder
USER <UID>[:<GID>]
✔️ Die USER-Anweisung legt den Benutzernamen (oder UID) und optional die Benutzergruppe (oder GID) fest, die beim Ausführen des Images und für alle RUN-, CMD- und ENTRYPOINT-Anweisungen verwendet werden sollen, die im Dockerfile darauf folgen. Beispiel:
FROM microsoft/windowsservercore
# On Windows, the user must be created first if it’s not a built-in account. This can be done with the net user command called as part of a Dockerfile.
RUN net user /add patrick
# Set it for subsequent commands
USER patrick
WORKDIR
WORKDIR /path/to/workdir
✔️ Die Anweisung WORKDIR legt das Arbeitsverzeichnis für alle RUN-, CMD-, ENTRYPOINT-, COPY- und ADD-Anweisungen fest, die ihr in der Dockerdatei folgen. Wenn der Befehl WORKDIR nicht existiert, wird er auch dann erstellt, wenn er in keiner nachfolgenden Dockerfile-Anweisung verwendet wird. Die Anweisung WORKDIR kann in einem Dockerfile mehrfach verwendet werden. Wenn ein relativer Pfad angegeben wird, ist dieser relativ zum Pfad der vorherigen WORKDIR-Anweisung. Zum Beispiel:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd # /a/b/c
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
ONBUILD <INSTRUCTION>
✔️* Der Befehl ONBUILD fügt dem Bild einen Triggerbefehl hinzu, der zu einem späteren Zeitpunkt ausgeführt wird, wenn das Bild als Grundlage für einen weiteren Build verwendet wird. Der Trigger wird im Kontext des nachgeschalteten Builds ausgeführt, als ob er unmittelbar nach der FROM-Instruktion in die nachgeschaltete Dockerdatei eingefügt worden wäre.


Wenn es sich bei seinem Image beispielsweise um einen wiederverwendbaren Python-Anwendungsgenerator handelt, muss der Anwendungsquellcode in einem bestimmten Verzeichnis hinzugefügt werden, und es kann ein Buildskript erforderlich sein, das danach aufgerufen wird. Man kann jetzt nicht einfach ADD und RUN aufrufen, weil man noch keinen Zugriff auf den Anwendungs-Quellcode hat, und es wird für jeden Anwendungs-Build anders sein. Man könnte den Anwendungsentwicklern einfach eine Boilerplate-Dockerfile zum Kopieren und Einfügen in ihre Anwendung zur Verfügung stellen, aber das ist ineffizient, fehleranfällig und schwierig zu aktualisieren, da es sich mit anwendungsspezifischem Code vermischt.


Und so funktioniert es:

  1. Wenn eine ONBUILD-Anweisung angetroffen wird, fügt der Builder einen Trigger zu den Metadaten des zu erstellenden Bildes hinzu. Die Anweisung hat ansonsten keine Auswirkungen auf den aktuellen Build.
  2. Am Ende des Builds wird eine Liste aller Auslöser im Manifest des Images unter dem Schlüssel OnBuild gespeichert.
  3. Später kann das Image mit Hilfe der FROM-Anweisung als Grundlage für einen neuen Build verwendet werden. Als Teil der Verarbeitung der FROM-Anweisung sucht der nachgeschaltete Builder nach ONBUILD-Triggern und führt sie in derselben Reihenfolge aus, in der sie registriert wurden. Wenn einer der Auslöser fehlschlägt, wird die FROM-Anweisung abgebrochen, was wiederum zum Fehlschlagen des Builds führt. Wenn alle Auslöser erfolgreich sind, wird die FROM-Anweisung abgeschlossen und der Build wie üblich fortgesetzt.
  4. Die Trigger werden nach ihrer Ausführung aus dem Endbild gelöscht. Mit anderen Worten, sie werden nicht an "Enkelkinder"-Builds vererbt.


Beispiel:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src

STOPSIGNAL
STOPSIGNAL signal
✔️ Der Befehl STOPSIGNAL legt das Systemaufrufsignal fest, das zum Beenden an den Container gesendet wird. Dieses Signal kann eine gültige vorzeichenlose Nummer sein, die mit einer Position in der Syscall-Tabelle des Kernels übereinstimmt, z. B. 9, oder ein Signalname im Format SIGNAME, z. B. SIGKILL.
HEALTHCHECK
HEALTHCHECK [--interval=30s] [--timeout=30s] [--start-period=0s] [--retries=3] CMD command
# Oder (Um einen vererbten HEALTHCHECK zu deaktivieren)
HEALTHCHECK NONE
Die HEALTHCHECK-Anweisung sagt Docker, wie er einen Container testen soll, um zu überprüfen, ob er noch funktioniert. Dadurch können Fälle wie z.B. ein Webserver erkannt werden, der in einer Endlosschleife feststeckt und nicht in der Lage ist, neue Verbindungen zu verarbeiten, obwohl der Serverprozess noch läuft.


Wenn für einen Container eine Gesundheitskontrolle vorgeschrieben ist, hat er zusätzlich zu seinem normalen Status einen Gesundheitsstatus. Dieser Status ist zunächst starting. Wann immer eine Gesundheitsprüfung besteht, wird er health (in welchem Zustand er sich zuvor befand). Nach einer bestimmten Anzahl von aufeinanderfolgenden Fehlschlägen wird er unhealthy.

Der Ausgangsstatus (Exit-Code) des Befehls zeigt den Gesundheitszustand des Containers an. Die möglichen Werte sind:

  • 0: Erfolg - der Container ist gesund und einsatzbereit
  • 1: Ungesund - der Container funktioniert nicht richtig
  • 2: reserviert - Dieser Exit-Code sollte nicht verwendet werden
Beispiel: Durch die folgende Anweisung wird jede 5 Minuten gecheckt, ob ein Webserver die Hauptseite der Site innerhalb von drei Sekunden bedienen kann:
HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1


In einem Dockerfile kann es nur eine HEALTHCHECK-Anweisung geben.

SHELL
SHELL ["executable", "parameters"]
Mit der SHELL-Anweisung kann die für die Shell-Form von Befehlen (RUN, CMD und ENTRYPOINT) verwendete Standardshell überschrieben werden. Die Standard-Shell unter Linux ist ["/bin/sh", "-c"] und unter Windows ["cmd", "/S", "/C"]. Die SHELL-Anweisung muss in JSON-Form in einer Dockerdatei geschrieben werden.

Die SHELL-Anweisung ist besonders unter Windows nützlich, wo es zwei häufig verwendete und recht unterschiedliche native Shells gibt: cmd und powershell.


Die SHELL-Anweisung kann mehrmals erscheinen. Jede SHELL-Anweisung setzt alle vorherigen SHELL-Anweisungen außer Kraft und wirkt sich auf alle nachfolgenden Anweisungen aus. Beispiel:
FROM microsoft/windowsservercore

# Executed as cmd /S /C echo default
RUN echo default

# Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default

# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello

# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S", "/C"]
RUN echo hello

* (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. Eine Beispielhafte docker-compose.yml-Konfiguration sieht wie folgt aus:

version: "3.8"
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
      - logvolume01:/var/log
    links:
      - redis
  redis:
    image: redis
volumes:
  logvolume01: {}

docker-compose verfügt über Befehle zur Verwaltung des gesamten Lebenszyklus einer Anwendung:

  • Starten, Stoppen und Wiederherstellen von Diensten
  • Den Status der laufenden Dienste anzeigen
  • Streamen der Protokollausgabe von laufenden Diensten
  • Einen einmaligen Befehl für einen Dienst ausführen

Befehle, die sich auf die Manipulation von Images oder auf benutzerinteraktive Optionen beziehen, sind in docker-compose nicht vorhanden, da sie sich auf einen bestimmten 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.