如果 Ruby 收到 TERM 信号,它通常会以退出代码 143 退出,根据此来源,这表明进程成功响应了该信号。但是如果我让脚本在没有 shell 的情况下运行,退出代码为 1。
带壳:
> cat Dockerfile
FROM ruby:alpine
CMD ruby -e "Process.kill('TERM', Process.pid)" # <- shell form
> docker build -t term_shell . > /dev/null
> docker run term_shell
Terminated
> echo $?
143
不带外壳:
> cat Dockerfile
FROM ruby:alpine
CMD ["ruby", "-e", "Process.kill('TERM', Process.pid)"] # <- exec form
> docker build -t term_exec . > /dev/null
> docker run term_exec
> echo $?
1
但是如果我以 143 退出自己,则退出代码符合预期:
> cat Dockerfile
FROM ruby:alpine
CMD ["ruby", "-e", "exit(143)"] # <- exec form
> docker build -t exit_exec . > /dev/null
> docker run exit_exec
> echo $?
143
为什么?当 ruby 收到 TERM 时,退出代码是否不是来自 Ruby,而是来自 shell?
第二个示例的退出代码1
,因为调用Process.kill('TERM', Process.pid)
失败。ruby -e
由于此失败而退出,在这种情况下,状态代码为1
。
- 使用
CMD ruby -e "Process.kill('TERM', Process.pid)"
,docker 在 shell 中执行给定的命令。在正在运行的容器中,这意味着 pid 为 1 的根进程将被/bin/sh -c
,并且ruby -e
命令将在具有另一个 pid 的子进程中执行(例如 6(。 - 使用
CMD ["ruby", "-e", "Process.kill('TERM', Process.pid)"]
,docker 直接ruby -e
作为根进程执行,pid 1。
Linux 上的 PID 1 的行为与普通 PID 不同。来自 docker 文档:
注意:在容器内作为 PID 1 运行的进程由 Linux 特别处理:它忽略任何具有默认操作的信号。因此,该过程不会在SIGINT或SIGTERM上终止,除非它被编码为这样做。
因此,在您的情况下,TERM
信号不会发送给您的进程。
您可以在本文中找到有关 PID 1 行为的更多信息: https://hackernoon.com/my-process-became-pid-1-and-now-signals-behave-strangely-b05c52cc551c