[Docker] Lernen wir Dockerfile kennen.

[Docker] Lernen wir Dockerfile kennen.

13. Juli 2022

😡 Jungs.. bin ich der Einzige, den das stört?

Wenn man sich vorher kurz die CLI von Docker angeschaut hat, wird man feststellen, dass es ziemlich unangenehm ist, Container zu erstellen und zu bauen.

Um es mir einfacher zu machen, muss ich bei der Erstellung eines Containers viele Befehle eingeben.

Ein noch größeres Problem könnte jedoch sein, dass das Image beim Bauen enorm groß werden kann.

Es wird nicht riesig sein, aber es kann von mindestens 100MB bis zu 1~2GB variieren.

img.png

In Firmen, die viele Container verwalten, kann dies noch katastrophaler werden.

Abgesehen von der Größe des Images kann es sehr lange dauern, das Image zu übertragen und herunterzuladen.

📄 Dockerfile

Deshalb schlägt Docker eine neue Methode zur Erstellung von Containern vor.

Der Kernpunkt ist, ein Script zu teilen, das das Image erstellt, anstatt ein bereits erstelltes Image zu verteilen.

🚫 Don’t Repeat Yourself

Man muss es sowieso einmal machen. Vielleicht macht das in Zukunft eine AI automatisch?

Aber man muss es nur einmal machen.

Da es eine integrierte Funktion von Docker ist, ist es viel einfacher, einen Befehlssatz in einer Textdatei zu speichern, der formal und strukturiert ist.

Und letztendlich ist ein Dockerfile auch nur eine Textdatei, die beim Speichern nicht viel Speicherplatz in Anspruch nimmt.

Nun zu den Details.

Wie führt man es aus?

Wenn ein Dockerfile erstellt wird, wird ein Image gemäß den Spezifikationen dieser Datei geladen und die Befehle ausgeführt.

Sobald der Container in Docker gestartet wird, wird das entsprechende Image lokal auf dem Rechner des Benutzers gespeichert.

Dieser Prozess wird als build bezeichnet und unterscheidet sich von commit, das den Container zu einem Image macht.

build [BUILD_PATH]

Der build-Befehl erstellt einen Container basierend auf dem Dockerfile im angegebenen Pfad.

Da dieser Befehl rekursiv ausgeführt wird, sollte niemals ein absoluter Root-Pfad angegeben werden.

$ docker build / # (X)

Da das Dockerfile normalerweise im Root-Verzeichnis des Projekts liegt, wird der folgende Befehl oft als Idiom verwendet.

$ docker build .

-f [DOCKER_FILE_PATH] Option

Traditionell liegt das Dockerfile im Root-Pfad des Projekts, das gebaut wird, aber die -f-Option erlaubt es, wenn der Root-Pfad und der Pfad des Dockerfile unterschiedlich sind.

$ docker build -f /path/to/a/Dockerfile .

Das bewirkt, dass das Dockerfile so agiert, als läge es im aktuellen Verzeichnis (.).

-t Option

Wenn der Build erfolgreich ist, kann das neue Image mit dem -t-Option im Repository gespeichert und getaggt werden.

$ docker build -t shykes/myapp .

Wenn das gleiche Image in verschiedenen Repositories oder mit verschiedenen Tags gespeichert werden soll, kann man mehrere -t-Optionen angeben, um das gleiche Image zu bauen.

$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .

Dockerfile erstellen

Es ist einfach, ein Dockerfile zu erstellen. Man erstellt einfach ein Dockerfile.

Lassen Sie uns nun die umfangreichen Einstellungen kennen lernen, die für die Erstellung eines Dockerfile erforderlich sind.

ARG

Dies ist eine Anweisung zur Deklaration von Variablen, die verwendet wird, um die Wiederverwendbarkeit in einem Dockerfile zu maximieren.

# Verwendung der Definition
ARG [KEY]=[value]

# Verwendung des Aufrufs
${[KEY]}

# Beispiel
ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD  /code/run-app

FROM extras:${CODE_VERSION}
CMD  /code/run-extras

FROM

# - Usage
FROM [--platform=<platform>] <image> [AS <name>]
# or
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
# or 
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

Gibt das Basisimage für den Build an.

Man gibt hier den Teil an, den man im IMAGE-Abschnitt der Befehle run und create aus dem vorherigen Kapitel einfügen würde. Zuerst sucht Docker das Image lokal, und wenn es nicht gefunden wird, wird es von Docker Hub heruntergeladen.

Natürlich kann es nicht heruntergeladen werden, wenn man keinen Zugriff auf das Repository hat oder der Name des Images falsch ist.

WORKDIR

Gibt das Arbeitsverzeichnis innerhalb des Containers an. Dies entspricht der Verwendung der -w, --workdir-Option in den run und create-Befehlen aus dem vorherigen Kapitel.

Alle COMMANDS werden im WORKDIR ausgeführt. Wenn nichts angegeben ist, wird das Home-Verzeichnis (~) als Standardort verwendet.

RUN [COMMAND]

Dies ist anders als der run-Befehl in das vorherige Kapitel. Es fällt vielmehr unter die Kategorie COMMAND und ermöglicht die Eingabe verschiedener Befehle im Container.

Der RUN-Befehl läuft unabhängig vom Basisbefehl des Images (als ob der Befehl in exec eingegeben wäre).

RUN unterstützt zwei Eingabeformate.

# Methode 1
RUN <Befehl>
# Methode 2
RUN ["ausführbare Datei", "param1", "param2"]

Die Methode 2 mag kompliziert aussehen, trennt die Befehle jedoch nur durch Leerzeichen.

Zum Beispiel führen die beiden folgenden Befehle das gleiche aus:

RUN ["/bin/bash", "-c", "echo hello"]
RUN /bin/bash -c "echo hello"

Achtung

Wenn Sie einen Befehl in Listenform ausführen möchten und Backslashes (\) verwenden, um einen Pfad anzugeben, beachten Sie Folgendes.

Da der tatsächliche Befehl im JSON-Format ausgeführt wird, sind Backslashes keine zulässigen Zeichen im JSON. Besonders in Windows wird (\) als Trennzeichen verwendet, und in diesem Fall muss der Backslash (\) verdoppelt werden, um ihn zu escapen.

RUN ["c:\windows\system32\tasklist.exe"] # (X)
RUN ["c:\\windows\\system32\\tasklist.exe"] # (O)

ENTRYPOINT

Führt ein Script oder einen Befehl aus, wenn der Container gestartet wird.

Es wird ausgeführt, wenn der Container gestartet (start) oder sowohl erstellt als auch gestartet (run) wird, nicht jedoch bei der Ausgabe von Befehlen an einen laufenden Container (exec).

Ein weiterer besonderer Aspekt ist, dass ENTRYPOINT nur einmal in einem Dockerfile deklariert werden kann.

CMD

Dies funktioniert ähnlich wie RUN, aber es ist der COMMAND, der standardmäßig ausgeführt wird, wenn kein COMMAND im docker run-Befehl angegeben ist.

# Methode 1
CMD ["ausführbare Datei","param1","param2"] # empfohlene exec-Form
# Methode 2
CMD ["param1","param2"]
# Methode 3
CMD Befehl param1 param2

Der ausführbare Code in Methode 1 kann weggelassen werden, aber dann muss ENTRYPOINT definiert sein.

ENV

Dient zur Angabe von Umgebungsvariablen im Container, die die gleiche Funktion wie die -e-Option im run, create-Befehl erfüllt.

# Verwendung
ENV [SCHLÜSSEL]=[WERT]

# Beispiel
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc

LABEL

Wird verwendet, um Metadaten in den Container aufzunehmen.

# Verwendung
LABEL <key>=<value> <key>=<value> <key>=<value> ...

Speichert key-value pairs und zum Beispiel kann folgendes geschrieben werden:

LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="Dieser Text zeigt, \ # Mehrzeiliger Text kann mit einem Backslash(\) eingegeben werden.
dass Label-Werte über mehrere Zeilen hinweg fortgeführt werden können."
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

Um die Metadaten eines bestimmten Images (myimage) zu sehen, können Sie folgenden Befehl ausführen:

$ docker image inspect --format='' myimage
{
  "com.example.vendor": "ACME Incorporated",
  "com.example.label-with-value": "foo",
  "version": "1.0",
  "description": "Dieser Text zeigt, dass Label-Werte über mehrere Zeilen hinweg fortgeführt werden können.",
  "multi.label1": "value1",
  "multi.label2": "value2",
  "other": "value3"
}

EXPOSE [PORT]/[PROTOKOLL]

EXPOSE gibt an, welchen Port ein Container abhören soll.

Aber Dockerfile bedeutet nicht, dass ein Host-Port weitergeleitet wird.

Es definiert nur, auf welchem PORT der Container Daten vom Host empfangen soll.

Deshalb kann die -p-Option nicht nur mit einem Dockerfile automatisiert werden.

[PROTOKOLL] gibt an, welches Protokoll zum Übertragen der Daten verwendet werden soll.

Wählen Sie zwischen tcp und udp. Der Standardwert ist tcp, und es wird empfohlen, diesen nicht zu ändern, es sei denn, es gibt einen spezifischen Grund.

TCP gewährleistet die Zuverlässigkeit bei der Paketzustellung.

COPY [OPTION] [HOST_PATH] [CONTAINER_PATH]

Kopiert Dateien von HOST_PATH nach CONTAINER_PATH. HOST_PATH kann nicht nur Pfade, sondern auch Dateinamen sowie Platzhalter wie ? (ein Zeichen), * (mehrere Zeichen) beinhalten.

Relative Pfade

Wenn relative Pfade in jedem PATH verwendet werden, basiert HOST_PATH auf dem Speicherort der Datei, während CONTAINER relativ zu WORKDIR ist.

Beispiele für Platzhalter

  • home*: alle Dateien und Pfade, die mit home beginnen
  • *home*: alle Dateien und Pfade, die home enthalten
  • ?home: alle Dateien und Pfade mit einem beliebigen Buchstaben vor home (z.B. 1home, ahome)

--chown

Die --chown-Option ermöglicht es, den Eigentümer des CONTAINER_PATH festzulegen.

Da dies intern durch den Befehl chown durchgeführt wird, kann es nur in Linux-basierten Containern ausgeführt werden.

ADD [HOST_PATH] [CONTAINER_PATH]

ADD funktioniert fast genauso wie COPY.

Die Funktionalitäten und Optionen von COPY gelten auch hier, jedoch hat ADD einige zusätzliche Funktionen:

  1. Wenn die zu kopierenden Dateien Archive (.tar, .tar.gz) sind, werden diese entpackt, bevor sie kopiert werden.
  2. Man kann Dateien von entfernten Standorten kopieren, ähnlich wie mit wget.

Dateien von entfernten Standorten haben Berechtigungen von 600 (nur lesbar von Benutzern).

Automatisieren Sie Optionen?

An dieser Stelle sollte ein aufmerksamer Leser skeptisch werden.

Kann man wirklich sagen, dass ein Dockerfile den gesamten Erstellungsprozess eines CONTAINER spezifiziert hat?

Die Antwort ist natürlich nein.

Obwohl ein Dockerfile den Container spezifiziert, zielt es nicht darauf ab, die Erstellungsoptionen des Containers zu automatisieren.

Betrachten Sie nur die -p-Option, die in den create, run-Befehlen verwendet wird.

Die -p-Option spezifiziert sowohl den Host- als auch den Container-Port, während EXPOSE nur den offenen Port des Containers angibt.

Viele andere Optionen auf der Hosts-Ebene können vom Dockerfile nicht behandelt werden.

Dies liegt daran, dass build ein Container-spezifisches Spezifikationsdokument ist, nicht aber host-spezifisch.

Diese Unzulänglichkeit ist der Grund für die Entwicklung von Docker Compose, das in einem zukünftigen Post behandelt wird.

Docker Ignore File

Wenn Sie Erfahrung mit git haben, haben Sie wahrscheinlich schon einmal ein .gitignore-File verwendet.

Die .dockerignore-Datei übernimmt eine ähnliche Rolle.

In .gitignore werden Dateien festgelegt, die nicht committet werden sollen. In diesem Fall definiert man die Pfade der Dateien oder Ordner, die nicht von ADD oder COPY vom Host OS kopiert werden sollen.

# Kommentar
*/temp*
*/*/temp*
temp?

Referenzen