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进程)被内核视为特殊情况。
- 如果init进程没有安装信号处理程序,那么该信号将不会被发送到它。
- 信号不会从init进程自动传播,进程必须pipe理这个。
所以Docker进程有望处理信号本身。
咖啡--nodejs
选项
正如你所注意到的,当coffee
具有--nodejs
选项以便能够传递额外的选项时 , coffee
将分叉一个子node
进程 。
这最初出现在信号处理(至less在osx上) 之外的Docker 之外的一些奇怪的行为。 SIGINT
或SIGTERM
将被转发到孩子身上,但是无论你如何处理代码中的信号(在孩子身上运行),都要立即杀掉父coffee
进程。
一个简单的例子
process.on 'SIGTERM', -> console.log 'SIGTERM' process.on 'SIGINT', -> console.log 'SIGINT' cb = -> console.log "test" setTimeout cb, 5000
当你运行这个和ctrl – c ,信号被转发到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和服务之间增加另一层复杂性。