Docker SIGTERM在使用标志启动时不会被传递到node.js / coffee应用程序

我在应用程序中设置了侦听器来捕获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会启动一个进程

 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 

问题是:为什么?

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

 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. 信号不会从init进程自动传播,进程必须pipe理这个。

所以Docker进程有望处理信号本身。

咖啡--nodejs选项

正如你所注意到的,当coffee 具有--nodejs选项以便能够传递额外的选项时 , coffee将分叉一个子node进程 。

这最初出现信号处理(至less在osx上) 之外的Docker 之外的一些奇怪的行为。 SIGINTSIGTERM将被转发到孩子身上,但是无论你如何处理代码中的信号(在孩子身上运行),都要立即杀掉父coffee进程。

一个简单的例子

 process.on 'SIGTERM', -> console.log 'SIGTERM' process.on 'SIGINT', -> console.log 'SIGINT' cb = -> console.log "test" setTimeout cb, 5000 

当你运行这个和ctrlc ,信号被转发到subprocess并处理。 父进程立即closures并返回到shell。

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

然后,你的代码的subprocess继续在后台运行5秒,最终输出test

docker和coffee --nodejs

主要问题是父母coffee过程不处理代码中的任何信号,所以信号不能到达并且不被转发到孩子身上。 这可能需要更改coffeescript的启动程序代码来解决。

在Docker之外发现的信号怪异coffee --nodejs也可能是糟糕的。 如果主容器过程(叉的父母)在信号处理程序有机会在孩子完成之前退出,则容器将在其周围closures。 如果仅通过将信号转发给孩子来解决上述问题,则不太可能发生这种情况。

使用build议的JavaScript加载程序或修复咖啡脚本加载程序的替代方法是使用实​​际的init进程,如runit或supervisor,但会在docker和服务之间增加另一层复杂性。

Interesting Posts