【问题标题】:What purpose does using exec in docker entrypoint scripts serve?在 docker 入口点脚本中使用 exec 有什么作用?
【发布时间】:2015-11-22 05:10:59
【问题描述】:

以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?

【问题讨论】:

    标签: bash docker


    【解决方案1】:

    正如@Peter Lyons 所说,使用 exec 将替换父进程,而不是运行两个进程。

    这在 Docker 中对于正确代理信号很重要。例如,如果 Redis 在没有 exec 的情况下启动,它将不会在 docker stop 上收到 SIGTERM 并且不会有机会干净地关闭。在某些情况下,这可能会导致数据丢失或僵尸进程。

    如果您确实启动了子进程(即不使用 exec),则父进程将负责处理和转发适当的信号。这是在容器中运行多个进程时最好使用 supervisord 或类似的原因之一,因为它会适当地转发信号。

    【讨论】:

    • 感谢您提供有关 docker 的更多见解!
    • 这很重要。
    • @adrianmouat Windows 容器替换 exit "$@" 的内容是什么?
    • @kat1330 你的意思是exec "$@"?我对 Windows 容器没有太多经验 - 我想你可以看看 powershell,但我认为如果你可以在容器中使用 WSL,它可能会按原样工作?
    • 我很困惑,如果父进程负责处理信号并将其转发给子进程,那么如果在没有exec的情况下启动redis,为什么不会收到SIGTERM,不应该PID 1的父shell将信号转发给redis?
    【解决方案2】:

    没有 exec,父 shell 进程会存活并等待子进程退出。使用 exec,子进程完全取代了父进程,因此当分叉子进程后父进程无事可做时,我会考虑 exec 稍微更精确/正确/高效。总体而言,我认为将其归类为次要优化可能是安全的。

    没有执行

    • 父外壳启动
    • 父壳叉子
      • 儿童跑步
      • 子退出
    • 父外壳退出

    与 exec

    • 父外壳启动
    • 父 shell 分叉子,用子替换自己
    • 子程序运行接管 shell 进程
    • 子退出

    【讨论】:

    • 所以通常会创建一个新的 shell,但 exec 说只在当前 shell 中运行它?
    • 不,只有 1 个 shell,但不是分叉子进程,而是等待子进程退出,然后继续“哦,看,我在脚本文件的末尾,什么都没有了做”,然后退出,exec 版本基本上“预先退出”父外壳,所以当孩子接管时它已经消失了。但是,对于这种舞蹈的精确机制,我将不得不听从更深入的 unix 开发人员。
    • 我认为它比“小优化”更重要,因为父进程负责处理信号。我在回答中写了更多内容。
    【解决方案3】:

    将其视为尾递归之类的优化。

    如果运行另一个程序是 shell 脚本的最后一个动作,那么没有太多需要让 shell 在新进程中运行程序并等待它。使用exec,shell 进程将自己替换为程序。

    在任何一种情况下,shell 脚本的退出值都是相同的1。最初调用 shell 脚本的任何程序都将看到一个与执行程序的退出值相等的退出值(如果找不到该程序,则为 127)。

    1 模数极端情况,例如程序根据其父项的名称执行不同的操作。

    【讨论】:

      猜你喜欢
      • 2016-12-29
      • 1970-01-01
      • 2020-04-28
      • 1970-01-01
      • 2019-10-28
      • 2023-01-31
      • 1970-01-01
      • 2016-10-08
      • 2017-07-13
      相关资源
      最近更新 更多