Mehrere Befehle in der Docker CMD-Direktive

39

Ich verstehe nicht, was passiert, wenn ich versuche, zwei Befehle zur Laufzeit über die CMD-Direktive in `Dockerfile auszuführen. Ich ging davon aus, dass dies funktionieren sollte:

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]

Aber es funktioniert nicht. Container hat nicht begonnen. Also musste ich es so machen:

CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]

Ich verstehe nicht Warum ist das so? Warum ist die erste Zeile nicht der richtige Weg? Kann mir jemand diese "CMD-Shell-Format vs JSON-Format, etc" Sachen erklären. In einfachen Worten.

Nur zur Kenntnis zu nehmen - dasselbe galt erwartungsgemäß auch für die command:Direktive docker-compose.yml.

Vladan
quelle

Antworten:

33

Ich glaube, der Unterschied könnte damit zusammenhängen, dass der zweite Befehl die Shell-Verarbeitung ausführt, während der erste dies nicht tut. Per der offiziellen Dokumentation gibt die ist execund shellForm, ist Ihr erster Befehl ist ein exec Form und es nicht zum Beispiel Umgebungsvariablen erweitern, während der zweite Fall ist. Daher ist es möglich, dass der Befehl bei Verwendung des exec-Formulars aufgrund seiner Abhängigkeit von der Shell-Verarbeitung fehlschlägt. Sie können dies überprüfen, indem Sie ausführendocker logs CONTAINERID

Ihr zweiter Befehl, die Shell-Form, entspricht:

CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm

Auszüge aus der Dokumentation -

Anmerkung: Im Gegensatz zum Shell-Formular ruft das Exec-Formular keine Befehlsshell auf. Dies bedeutet, dass keine normale Shell-Verarbeitung stattfindet. Zum Beispiel CMD [ "echo", "$HOME" ]wird keine Variablensubstitution durchgeführt $HOME. Wenn Sie dann entweder Shell - Verarbeitung wollen das Shell - Formular oder eine Shell direkt, beispielsweise ausführen: CMD [ "sh", "-c", "echo", "$HOME" ].

Daniel t.
quelle
Möglicherweise ist der Befehl aufgrund von Umgebungsvariablen fehlgeschlagen. Sollte ich dieses execFormular weiterhin verwenden , da es das bevorzugte Formular ist? Warum ist es bevorzugt? Oder soll ich einfachere shellForm verwenden?
Vladan
Es ist fehlgeschlagen, weil die Ausführung eines Befehls nach dem anderen eine Shell-Funktion ist. Umgebungsvariablen ist ein roter Hering.
Bryan
Wenn Sie in Docker mehrere Dienste ausführen, würde ich die Verwendung eines Prozessmanagers wie Supervisor empfehlen. Auf diese Weise starten Sie nur Supervisord unter dem Abschnitt CMD, und es wird sich darum kümmern, die Dienste zu starten. Sie können hier nach Details suchen
Daniel t.
Dies ist ein genauer Artikel, den ich gerade gelesen habe :) Danke.
Vladan
Ich verstehe immer noch nicht, warum du das tun musst CMD [ "sh", "-c", "echo", "$HOME"]. Warum nicht CMD ["sh", "-c", "echo $HOME"]oder CMD ["sh -c echo $HOME"]?
Sixty4bit
4

Mach es dir nicht schwer. Erstellen Sie einfach eine Bash-Datei "start.sh":

#!/bin/bash

/usr/bin/command2 param1
/usr/bin/commnad1

in deinem Dockerfile mache:

ADD start.sh /

CMD ["/start.sh"]
Digitaler Mensch
quelle
2

Die json-Syntax von CMD(und RUNund ENTRYPOINT) übergibt die Argumente direkt als exec-Syscall an den Kernel. Es gibt keine Trennung des Befehls von den Argumenten durch Leerzeichen, Anführungszeichen, E / A-Umleitung, Variablensubstitution, Weiterleiten zwischen Befehlen, Ausführen mehrerer Befehle usw. im exec-Systemaufruf. Der syscall führt nur die ausführbare Datei aus und listet die Argumente auf, die an diese ausführbare Datei übergeben werden sollen, und führt sie aus.

Zeichen wie $das Erweitern von Variablen, ;das Trennen von Befehlen (Leerzeichen), das Trennen von Argumenten &&und ||das Verketten von Befehlen, das >Umleiten von Ausgaben, das Weiterleiten |zwischen Befehlen usw. sind alle Merkmale der Shell und benötigen etwas Ähnliches /bin/shoder /bin/bashzum Interpretieren und Implementieren.


Wenn Sie zur Zeichenfolgensyntax von wechseln CMD, führt docker Ihren Befehl mit einer Shell aus:

CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm

Ansonsten macht Ihre zweite Syntax genau dasselbe:

CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]

Beachten Sie, dass ich nicht empfehle, mehrere Befehle auf diese Weise innerhalb eines Containers auszuführen, da es keine Fehlerbehandlung gibt, wenn Ihr erster Befehl fehlschlägt, insbesondere wenn er im Hintergrund ausgeführt wird. Sie lassen auch eine Shell als PID 1 im Container laufen, die die Signalverarbeitung unterbricht, was zu einer Verzögerung von 10 Sekunden und einem unbefriedigenden Töten Ihres Containers durch den Docker führt. Die Signalbehandlung kann mithilfe des Shell- execBefehls verringert werden :

CMD /etc/init.d/nullmailer start ; exec /usr/sbin/php5-fpm

Wenn Sie jedoch Prozesse verarbeiten möchten, die im Hintergrund unbemerkt fehlschlagen, müssen Sie zu einer Art Multi-Prozess-Manager wie Supervisord wechseln oder Ihre Anwendung vorzugsweise in mehrere Container aufteilen und mit Docker-Compose bereitstellen.

BMitch
quelle
1

Ich vermute, der erste Befehl schlägt fehl, weil in der DOCKER CMD-Form nur der erste Parameter ausgeführt wird, der Rest wird in diesen Befehl eingespeist.

Die zweite Form funktioniert, weil alle Befehle mit ";" werden in den Befehl sh eingespeist, der sie ausführt.

Devrimbaris
quelle
1

Ich denke nicht, dass Sie nach "Start" ein Semikomma setzen sollten

anstatt zu verwenden

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]

Versuchen

CMD ["/etc/init.d/nullmailer", "start", "/usr/sbin/php5-fpm"]

Da Docker "sh -c" verwendet, wird der obige Befehl wie folgt ausgeführt

/etc/init.d/nullmailer start
/etc/init.d/nullmailer /usr/sbin/php5-fpm
vedat
quelle
Die json-Syntax führt keine Befehle mit einer Shell aus, es gibt sh -cin diesem Szenario keine .
BMitch