我已经注意到,许多docker的entrypoint.sh脚本都会执行以下操作:
#!/bin/bash
set -e
... code ...
exec "$@"
是什么set -e
和exec "$@"
什么?
Answers:
它基本上接受传递给它的任何命令行参数,entrypoint.sh
并将其作为命令执行。目的基本上是“在此.sh脚本中执行所有操作,然后在同一shell中运行用户在命令行中传递的命令”。
看到:
exec "$@"
它将用为传递的参数产生的新进程替换当前正在运行的进程。对于Docker信令很重要:stackoverflow.com/a/32261019/99717
set -e
设置外壳程序选项以在任何正在运行的命令以非零退出代码退出时立即退出。该脚本将返回失败命令的退出代码。从bash手册页:
设置-e:
如果管道(可能包含单个简单命令),列表或复合命令(请参见上面的SHELL GRAMMAR)以非零状态退出,则立即退出。如果失败的命令是紧随一段时间或直到关键字之后的命令列表的一部分,紧随if或elif保留字之后的测试的一部分,以及在&&或||中执行的任何命令的一部分,则外壳程序不会退出。列出最后一个&&或||之后的命令,管道中除最后一个命令之外的任何命令,或者该命令的返回值用!反转。如果除子外壳程序以外的复合命令由于在-e被忽略时命令失败而返回非零状态,则外壳程序不会退出。如果设置了ERR,则会在外壳程序退出之前执行陷阱。
如果复合命令或shell函数在忽略-e的上下文中执行,则即使设置了-e且命令返回a故障状态。如果在忽略-e的上下文中执行时,复合命令或shell函数设置了-e,则该设置在复合命令或包含该函数调用的命令完成之前不会生效。
exec "$@"
通常用于使入口点通过,然后运行docker命令。它将用"$@"
指向的命令替换当前正在运行的shell 。默认情况下,该变量指向命令行参数。
如果您的映像具有一个指向entrypoint.sh的入口点,并且您将容器运行为docker run my_image server start
,则将转换为entrypoint.sh server start
在容器中运行。在exec行entrypoint.sh
,以pid 1运行的shell将用命令替换自身server start
。
这对于信号处理至关重要。如果不使用exec
,则server start
上面的示例中的会作为另一个pid运行,并且退出后,您将返回到shell脚本。对于pid 1中的shell,默认情况下会忽略SIGTERM。这意味着docker stop
发送到您的容器的平稳停止信号将永远不会被server
进程接收。10秒钟后(默认情况下),docker stop
将放弃正常关机并发送SIGKILL,这将迫使您的应用退出,但由于潜在的数据丢失或网络连接断开,如果应用程序开发人员收到信号,它们可能会进行编码。这也意味着您的容器将始终需要10秒钟才能停止。
请注意,使用诸如shift
和之类的Shell命令set --
,可以更改的值"$@"
。例如,这是脚本的一小部分,该脚本/bin/sh -c "..."
从将docker的shell语法用于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 "$@"
shift 2; set -- $1
与如何eval
解析字符串完全不同。考虑一下/bin/sh -c 'printf "%s\n" "hello world" "goodbye world"'
,如果您想要一个具体的测试用例,那么将字符串转换为arguments时,Bash不会解析引号。
eval
,我相信我仍然希望反映/bin/sh -c
字符串上的行为,但是如果我缺少某些内容,请告诉我。
set -e
认为比手写错误处理更容易出错。(如果着急,请跳过顶部的类比练习,以进行下面的练习)。