Was machen set -e und exec "$ @" für Docker-Einstiegspunktskripte?

94

Ich habe festgestellt, dass viele entrypoint.sh-Skripte für Docker ungefähr Folgendes tun:

#!/bin/bash
set -e

... code ...

exec "$@"

Wofür sind die set -eund die exec "$@"?

Nathan
quelle
2
Siehe BashFAQ # 105 zu : Warum set -ewird dies als weitaus fehleranfälliger angesehen als die handschriftliche Fehlerbehandlung ? (Wenn Sie es eilig haben, überspringen Sie die Analogie oben für die folgenden Übungen).
Charles Duffy

Antworten:

71

Grundsätzlich werden alle übergebenen Befehlszeilenargumente entrypoint.shals Befehl ausgeführt. Die Absicht ist im Grunde "Alles in diesem .sh-Skript tun, dann in derselben Shell den Befehl ausführen, den der Benutzer in der Befehlszeile übergibt".

Sehen:

Matthew
quelle
1
Beachten Sie auch, dass exec "$@"der aktuell ausgeführte Prozess durch den neuen Prozess ersetzt wird, der für die übergebenen Argumente erzeugt wurde. Wichtig für die Docker-Signalisierung: stackoverflow.com/a/32261019/99717
Hawkeye Parker
35

set -eLegt eine Shell-Option fest, die sofort beendet wird, wenn ein ausgeführter Befehl mit einem Exit-Code ungleich Null beendet wird. Das Skript wird mit dem Exit-Code des fehlgeschlagenen Befehls zurückgegeben. Von der Bash-Manpage:

set -e:

Beenden Sie das Programm sofort, wenn eine Pipeline (die aus einem einzelnen einfachen Befehl bestehen kann), eine Liste oder ein zusammengesetzter Befehl (siehe SHELL GRAMMAR oben) mit einem Status ungleich Null beendet wird. Die Shell wird nicht beendet, wenn der fehlgeschlagene Befehl Teil der Befehlsliste unmittelbar nach einer Weile oder bis zum Schlüsselwort ist, Teil des Tests nach den reservierten if- oder elif-Wörtern, Teil eines Befehls, der in einem && oder || ausgeführt wird Liste mit Ausnahme des Befehls nach dem letzten && oder ||, eines Befehls in einer Pipeline außer dem letzten oder wenn der Rückgabewert des Befehls mit! invertiert wird. Wenn ein anderer zusammengesetzter Befehl als eine Subshell einen Status ungleich Null zurückgibt, weil ein Befehl fehlgeschlagen ist, während -e ignoriert wurde, wird die Shell nicht beendet. Ein Trap auf ERR wird, falls gesetzt, ausgeführt, bevor die Shell beendet wird.

Wenn ein zusammengesetzter Befehl oder eine Shell-Funktion in einem Kontext ausgeführt wird, in dem -e ignoriert wird, wird keiner der im zusammengesetzten Befehl oder Funktionskörper ausgeführten Befehle von der Einstellung -e beeinflusst, selbst wenn -e festgelegt ist und ein Befehl a zurückgibt Fehlerstatus. Wenn ein zusammengesetzter Befehl oder eine Shell-Funktion -e während der Ausführung in einem Kontext setzt, in dem -e ignoriert wird, hat diese Einstellung keine Auswirkung, bis der zusammengesetzte Befehl oder der Befehl, der den Funktionsaufruf enthält, abgeschlossen ist.


exec "$@"wird normalerweise verwendet, um den Einstiegspunkt zu einem Durchlauf zu machen, der dann den Docker-Befehl ausführt. Es ersetzt die aktuell ausgeführte Shell durch den Befehl, auf den "$@"verwiesen wird. Standardmäßig zeigt diese Variable auf die Befehlszeilenargumente.

Wenn Sie ein Bild mit einem Einstiegspunkt haben, der auf entrypoint.sh zeigt, und Sie Ihren Container als ausführen, bedeutet dies docker run my_image server start, dass er entrypoint.sh server startim Container ausgeführt wird. In der Exec-Zeile ersetzt entrypoint.shsich die als PID 1 ausgeführte Shell durch den Befehl server start.

Dies ist für die Signalverarbeitung von entscheidender Bedeutung. Ohne Verwendung würde execdas server startim obigen Beispiel als eine andere PID ausgeführt, und nach dem Beenden würden Sie zu Ihrem Shell-Skript zurückkehren. Bei einer Shell in PID 1 wird ein SIGTERM standardmäßig ignoriert. Das bedeutet, dass das ordnungsgemäße Stoppsignal, docker stopdas an Ihren Container gesendet wird, vom serverProzess niemals empfangen wird. Nach 10 Sekunden (standardmäßig) docker stopwürde das ordnungsgemäße Herunterfahren aufgegeben und ein SIGKILL gesendet, das das Beenden Ihrer App erzwingt. Bei potenziellem Datenverlust oder geschlossenen Netzwerkverbindungen hätten App-Entwickler möglicherweise herumcodieren können, wenn sie das Signal empfangen hätten. Dies bedeutet auch, dass Ihr Container immer 10 Sekunden braucht, um anzuhalten.

Beachten Sie, dass Sie mit Shell-Befehlen wie shiftund set --den Wert von ändern können "$@". Beispiel: Hier ist ein kurzer Teil eines Skripts, das /bin/sh -c "..."den Befehl entfernt, der angezeigt werden kann, wenn Sie die Shell-Syntax von Docker verwenden für CMD:

# convert `/bin/sh -c "server start"` to `server start`
if [ $# -gt 1 ] && [ x"$1" = x"/bin/sh" ] && [ x"$2" = x"-c" ]; then
  shift 2
  eval "set -- $1"
fi

....

exec "$@"
BMitch
quelle
1
Siehe die POSIX- testSpezifikation , die -averaltet markiert . [ "$#" -gt 1 ] && [ "$1" = /bin/sh ]ist der richtige Ersatz (es ist kein x"$1"Hackery erforderlich, wenn nur die nicht veraltete Syntax verwendet wird).
Charles Duffy
Auch shift 2; set -- $1ist überhaupt nicht das gleiche wie, wie evaldie Zeichenfolge analysiert wird. Überlegen Sie /bin/sh -c 'printf "%s\n" "hello world" "goodbye world"', ob Sie einen konkreten Testfall wünschen und sehen, dass Bash beim Konvertieren einer Zeichenfolge in Argumente keine Anführungszeichen analysiert .
Charles Duffy
@CharlesDuffy danke für den Tipp zur veralteten Option, ich bin sicher, ich werde diesen Fehler wieder machen, alte Gewohnheiten sterben schwer. Mit dem eval, glaube ich, noch ich will , dass das Verhalten spiegelt /bin/sh -cauf der Saite haben würde, aber lassen Sie es mich wissen , wenn ich etwas fehle.
BMitch
30

set -e - Skript beenden, wenn ein Befehl fehlschlägt (Wert ungleich Null)

exec "$@"- leitet Eingangsvariablen um, mehr dazu hier

Agilob
quelle
"Leitet Eingangsvariablen um"? Eh? execEs gibt sicherlich einen Verwendungsmodus, in dem Umleitungen durchgeführt werden, aber dies ist nicht dieser Modus.
Charles Duffy