【问题标题】:SIGTERM does not reach node script when docker runs it with `/bin/sh -c`当 docker 使用 `/bin/sh -c` 运行 SIGTERM 时,它不会到达节点脚本
【发布时间】:2015-12-28 20:05:53
【问题描述】:

当我的 Dockerfile 以

结尾时
CMD node .

docker 使用命令 /bin/sh -c "node ." 而不是简单地 node . 运行该容器(我知道,我可以使用 CMD ["node", "."] 来执行此操作)。

我认为这种行为实际上很好,因为这意味着容器内部 PID1/bin/sh 而不是我不起眼的节点脚本。

如果我理解正确PID1 负责收割孤立的僵尸进程,而我真的不想对此负责...所以如果/bin/sh 可以做到这一点,那就太好了。 (其实我以为这就是 docker 重写我的CMD 的原因)。

问题是,当我通过docker-composer stopdocker-composer kill -s SIGTERM 向容器发送SIGTERM(以/bin/sh -c "node ." 开头)时,信号没有到达我的node 进程,因此它得到了在 10 秒的宽限期后,每次都用 SIGKILL 强行杀死。不好看。

有没有办法让别人管理我的僵尸并让我的节点实例接收 docker 发送的信号?

【问题讨论】:

    标签: linux node.js docker docker-compose


    【解决方案1】:

    我认为你必须了解ENTRYPOINTCMD的作用,并在你的Dockerfile中使用ENTRYPOINT(exec form)的方式。

    ENTRYPOINT,指定容器的启动可执行文件,是Docker容器的核心部分。每个容器必须有一个入口点来决定从哪里开始。默认值为/bin/bash -c。此外,CMD 设置的所有内容都将作为参数附加到ENTRYPOINT

    因此,如果您未能在Dockerfile 中指定ENTRYPOINT,则实际入口点将是/bin/bash -c {your_command_in_CMD},不幸的是不会传递信号。

    ENTRYPOINT有两种形式:执行形式shell形式

    • 执行形式:ENTRYPOINT ["executable", "param1", "param2"]
    • shell 形式:命令 param1 param2

    正如Docker reference所指出的:推荐exec形式,shell形式的缺点是command是由/bin/bash -c执行的,对信号可能效果不佳:

    shell 形式 阻止使用任何CMDrun 命令行参数,但缺点是您的ENTRYPOINT 将作为/bin/sh -c 的子命令启动,这不传递信号。这意味着可执行文件将不是容器的 PID 1 - 并且 不会 接收 Unix 信号 - 因此您的可执行文件将不会收到来自 docker stop <container>SIGTERM

    【讨论】:

      【解决方案2】:

      有一些工具可以解决这个问题:

      我认为,如果您只有一个进程,您需要做的就是使用信号处理程序显式处理信号,而 bash 不适合您。

      使用["node", "."] 语法,您可以使用https://nodejs.org/api/process.html#process_signal_events 并让它在SIGTERM 上退出。我相信这就足够了。

      或者使用 bash 脚本,您可以使用 trap "exit 0" TERM

      您也可以使用像 http://skarnet.org/software/s6/ 这样的流程主管

      【讨论】:

        【解决方案3】:

        我对 PID1 问题的解决方案:

        Dockerfile 以:

        结尾
        ENTRYPOINT ["/bin/bash", "-c"]
        

        或者将 ENTRYPOINT 完全排除在 Dockerfile 之外。默认为/bin/sh -c

        比我运行这个 shell 脚本,它的文件名是 node 并且有 chmod +x

        #!/bin/bash
        
        docker run --rm -it -p 8083:80 -v $HOME/node/work/:/root/node/:rw node_node "echo pid1 > /dev/null && node $@"
        

        诀窍是"echo pid1 > /dev/null && node $@",这是命令。 $@ 是一个从命令行接受用户输入的 shell 脚本。

        echo 将捕获 PID1 并将输出发送到 /dev/null

        例如./node -v 将返回正在运行的容器中的 Node.js 版本。

        使用./node /root/node/hello-world.js 命令运行网络服务器,CTRL+C 将再次起作用。

        Here 是我的 Dockerised Node.js 开发环境。

        编辑:

        完全疯狂的想法,但是将这个 node 命名的 bash 脚本添加到 $PATH。所以在主机上,你可以输入node -v 而不是./node -v。你在一个 Docker 容器中运行节点,它看起来就像是安装在主机上一样。 :)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-01-29
          • 2021-07-05
          • 1970-01-01
          • 2022-08-15
          • 1970-01-01
          • 2019-10-08
          • 2017-07-31
          • 1970-01-01
          相关资源
          最近更新 更多