【问题标题】:Docker SIGTERM not being delivered to node.js/coffee app when started with flags使用标志启动时,Docker SIGTERM 未传递到 node.js/coffee 应用程序
【发布时间】:2016-08-04 20:13:48
【问题描述】:

我在我的应用程序中设置了侦听器来捕获 SIGTERM、SIGINT 和 SIGUSR2:

# kill
process.on 'SIGTERM', ->
    killExecutors 'SIGTERM'

# ctrl + c
process.on 'SIGINT', ->
    killExecutors 'SIGINT'

# nodemon signal
process.on 'SIGUSR2', ->
    killExecutors 'SIGUSR2'

它按预期工作。当我在 docker 实例中运行它时:

FROM node:4.4.7

MAINTAINER Newborns <newborns@versul.com.br>

COPY . /src

EXPOSE 7733

WORKDIR /src
RUN npm install

CMD ["./node_modules/.bin/coffee", "feeder.coffee"]

一切正常,也是。但是,当我在执行中添加节点标志时

FROM node:4.4.7

MAINTAINER Newborns <newborns@versul.com.br>

COPY . /src

EXPOSE 7733

WORKDIR /src
RUN npm install

CMD ["./node_modules/.bin/coffee", "--nodejs", "--max_old_space_size=384", "feeder.coffee"]

它停止捕捉信号。我试图将 de CMD exec 表单更改为

CMD ./node_modules/.bin/coffee --nodejs --max_old_space_size=384 feeder.coffee

但还是不行。带标志和不带标志的执行有何变化?

编辑:

其实是docker在没有flags通过的情况下启动一个进程

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  4.2  1.0 960940 85424 ?        Ssl  20:21   0:01 node ./node_modules/.bin/coffee feeder.coffee
root        16  0.1  0.0  20220  2884 ?        Ss   20:22   0:00 bash
root        20  0.0  0.0  17500  2064 ?        R+   20:22   0:00 ps -aux

以及传递标志时的两个进程

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  0.3 707704 25272 ?        Ssl  20:17   0:00 node ./node_modules/.bin/coffee --nodejs --max_old_space_size=384 feeder.coffee
root        10  1.7  1.1 965900 90068 ?        Sl   20:17   0:01 /usr/local/bin/node --max_old_space_size=384 /src/node_modules/.bin/coffee feeder.coffee

问题是:为什么?

【问题讨论】:

  • 你能告诉哪个标志是问题吗?我怀疑--nodejs--max_old_space_size 更有可能出错。
  • 实际上,--nodejs 参数告诉coffee 将--max_old_space_size 参数直接转发到节点二进制文件。

标签: javascript node.js docker coffeescript dockerfile


【解决方案1】:

TL;DR当您需要使用扩展的 node 选项来避免 Docker 下的分叉进程的信号技术问题时,请使用 Javascript 加载程序文件而不是 coffee 可执行文件。

require('coffee-script').register();
require('./whatever.coffee').run();

然后

node --max_old_space_size=384 app.js

现在,进入技术细节...


Docker 和信号

容器中的初始进程是容器命名空间中的 PID 1。 PID 1(或 init 进程)在信号处理方面被内核视为特殊情况。

  1. 如果 init 进程没有安装信号处理程序,则不会向它发送该信号。
  2. 信号不会从初始化进程自动传播,该进程必须对此进行管理。

因此,docker 进程应该自己处理信号。


Coffeescripts --nodejs 选项

正如您所指出的,coffee 将通过 fork a child node process when it has the --nodejs 选项传递额外的选项。

这最初提出了一些奇怪的行为在 docker 之外 带有信号处理(至少在 osx 上)。 SIGINTSIGTERM 将被转发给子进程,但也会立即终止父进程 coffee,无论您如何处理代码中的信号(在子进程中运行)。

一个简单的例子

process.on 'SIGTERM', -> console.log 'SIGTERM'
process.on 'SIGINT', -> console.log 'SIGINT'

cb = -> console.log "test"
setTimeout cb, 5000

当你运行这个和 ctrl-c 时,信号被转发到子进程并被处理。父进程立即关闭并返回到 shell。

$ coffee --nodejs --max_old_space_size=384 block_signal_coffee.coffee 
^C
SIGINT
$ <5ish second pause> test

然后带有你代码的子进程继续在后台运行5秒,最终输出test


Docker 和 coffee --nodejs

主要问题是父进程coffee 不处理代码中的任何信号,因此信号不会到达并且不会转发给子进程。这可能需要更改 coffeescript's launcher code 才能修复。

如果出现在 Docker 之外,出现在 Docker 外部的信号怪癖 coffee --nodejs 也可能是坏的。如果主容器进程(fork 的父进程)在您的信号处理程序有机会在子进程中完成之前退出,则容器将围绕它们关闭。如果仅通过将信号转发给孩子来解决上述问题,则这种情况不太可能发生。

使用建议的 javascript 加载器或修复咖啡脚本加载器的替代方法是使用实​​际的 init 进程,例如 runitsupervisor,但这会在 docker 和您的服务之间增加另一层复杂性。

【讨论】:

  • 这是一个很棒的答案,马特。解决了我的问题并给了我很好的见解。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-29
  • 2021-11-27
  • 2016-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多