Dockerfiles: RUN vs. CMD vs. ENTRYPOINT

Dockerfiles: RUN vs. CMD vs. ENTRYPOINT

Dockerfiles: RUN vs. CMD vs. ENTRYPOINT

Ein Thema, das bei Einsteigern regelmäßig für Verwirrung sorgt, ist die Verwendung von RUN, CMD und ENTRYPOINT in Dockerfiles. Grund für die Verwirrung ist wohl, dass alle der genannten Dockerfile-Anweisungen einen CLI-Befehl oder eine ausführbare Datei ausführen. Was machen die Anweisungen und wann sollte ich welche Anweisung verwenden? Darauf möchte ich hier eingehen.

RUN

RUN ist der Befehl, der sich am stärksten von den anderen beiden unterscheidet: Ziel des Dockerfiles ist es, auf Grundlage des Basis-Images ein Dateisystem zu schaffen, in welchem das eigene Projekt ausgeführt werden kann. Um diesen Zustand zu erreichen, müssen Verzeichnisse angelegt, Pakete installiert und Abhängigkeiten heruntergeladen werden. All dies geschieht mit dem RUN-Befehl im Dockerfile.

Da RUN das zugrunde liegende Dateisystem modifiziert, wird bei jedem Aufruf des Befehls ein neuer Image-Layer erstellt, der die Änderung im Dateisystem beinhaltet. Für RUN npm ci wäre die Änderung im Dateisystem beispielsweise ein neu erstelltes node_modules-Verzeichnis.

Mit RUN werden also Skripte und Befehle ausgeführt, die man normalerweise als Administrator verwenden würde, um das Betriebssystem einzurichten - oder solche, die man als Entwickler ausführt, um seinen Code lauffähig zu machen.

CMD

Im Gegensatz zu RUN wird CMD nicht mehrere Male aufgerufen, um das finale Image vorzubereiten. Stattdessen legt CMD für das Image ein Default-Kommando fest, das beim Start eines Containers ausgeführt werden soll.

Dockerfile:
FROM alpine:3.9
CMD ["echo", "Hallo vom CMD!"]

Dieses Dockerfile verwendet Alpine Linux als Basis und führt beim Start eines entsprechenden Containers den echo-Befehl aus. Dabei könnte auch eine eigene ausführbare Datei aufgerufen werden, wie z. B. ein zuvor kompiliertes Projekt. In der hier verwendeten Exec Form werden die Argumente für das Kommando kommasepariert angehängt.

Nach dem Erstellen eines Images mit docker image build -t my-alpine . kann ich einen darauf basierenden Container ausführen:

Bash:
$ docker container run my-alpine
  Hallo vom CMD!

Das Besondere an CMD ist, dass diese Angabe beim Start eines Containers überschrieben werden kann. Die Syntax zum Start eines Containers lautet docker container run [OPTIONS] IMAGE [COMMAND] [ARG...], es kann also ein optionaler Befehl mit Argumenten an den Image-Namen angehängt werden, der dann die CMD-Anweisung des Images überschreibt:

Apache-Konfiguration:
$ docker container run my-alpine echo "Hallo von der Konsole!"
  Hallo von der Konsole!

Dieses Feature wird gleich noch wichtig sein.

ENTRYPOINT

Auch ENTRYPOINT legt einen Befehl fest, der beim Start eines Containers ausgeführt werden soll. Anders als CMD wird er jedoch nicht von docker container run überschrieben. Damit eignen sich Entrypoints gut für Container, die immer das selbe Programm ausführen sollen, also selbst als eine Art Executable fungieren.

Jedoch liegt die Mächtigkeit von Entrypoints darin, dass eine CMD-Anweisung immer an die ENTRYPOINT-Anweisung angehängt wird. Mit einem einfachen Dockerfile lässt sich das bestätigen:

Dockerfile:
FROM alpine:3.9
ENTRYPOINT ["echo"]
CMD ["Hallo vom CMD"]

Nachdem ich das Image neu baue, sieht die Ausführung trotz Aufteilung des echo-Befehls wie folgt aus:

Bash:
$ docker container run my-alpine
  Hallo vom CMD

CMD ist dabei aber nach wie vor überschreibbar. Das bedeutet, dass ich hier die Argumente für echo überschreiben kann, da der "Command" genau wie vorher einfach an den Entrypoint angehängt wird.

Bash:
$ docker container run my-alpine "Hallo von der Konsole"
  Hallo von der Konsole

Der Einstiegspunkt für einen Container, nämlich ENTRYPOINT, bleibt also immer gleich. So ist es möglich, für einen Container einen fixen Befehl als Entrypoint festzulegen, der immer ausgeführt wird - und zusätzlich mit CMD Default-Argumente dafür anzugeben, die mit docker container run überschrieben werden können.

Fazit

Der Anwendungsfall für RUN sollte klar sein.

ENTRYPOINT sollte bei Containern verwendet werden, deren einziger Zweck es ist, einen bestimmten Dienst auszuführen. Der entsprechende Befehl sollte als Entrypoint definiert werden. Mit CMD können dann Default-Argumente für diesen Befehl angegeben und bei Bedarf vom Nutzer überschrieben werden.

Eine CMD-Anweisung ohne zugehörigem Entrypoint kann dann sinnvoll sein, wenn standardmäßig ein bestimmter Befehl ausgeführt werden soll, der aber jederzeit vollständig mit docker container run überschrieben werden kann.
Autor
dominik
Aufrufe
6.705
Erstellt am
Letzte Bearbeitung
Bewertung
0,00 Stern(e) 0 Bewertung(en)

Weitere Ressourcen von dominik

Zurück
Oben Unten