例如在redis官方图片中:
https://github.com/docker-library/redis/blob/master/2.8/docker-entrypoint.sh
#!/bin/bash
set -e
if [ "$1" = 'redis-server' ]; then
chown -R redis .
exec gosu redis "$@"
fi
exec "$@"
为什么不像往常一样运行命令而不使用exec呢?
答案 0 :(得分:41)
正如@Peter Lyons所说,使用exec将替换父进程,而不是运行两个进程。
在Docker中,正确代理信号非常重要。例如,如果Redis在没有exec的情况下启动,它将不会在SIGTERM
上收到docker stop
,并且不会有机会干净地关闭。在某些情况下,这可能会导致数据丢失或僵尸进程。
如果确实启动了子进程(即不使用exec),则父进程负责处理和转发信号。这是在容器中运行多个进程时最好使用supervisord或类似的原因之一,因为它会适当地转发信号。
答案 1 :(得分:14)
如果没有exec,父shell进程仍然存在,并等待子进程退出。使用exec,子进程完全替换父进程,所以如果在分配子进程后父进程没有任何内容,我会认为exec
稍微更精确/正确/有效。在宏观方案中,我认为将其归类为次要优化可能是安全的。
没有exec
使用exec
答案 2 :(得分:2)
将其视为尾递归等优化。
如果运行另一个程序是shell脚本的最后一个行为,那么不需要让shell在新进程中运行程序并等待它。使用exec
,shell进程将自己替换为程序。
在任何一种情况下,shell脚本的退出值都是相同的 1 。无论最初调用shell脚本的程序是什么程序都会看到退出值等于exec'ed程序的退出值(如果找不到程序则为127)。
1 modulo corner case,例如程序根据其父级的名称执行不同的操作。