为何您的Docker应用程序接收不到信号?

当您的应用程序是经过docker的方式运行时,在终止应用程序前进行适当的清理,也就是所谓的优雅退出也很是重要。尽管这仅仅取决于确保信号到达您的应用程序并对其进行处理,但仍有不少方面可能致使错误。python

从原则上讲,这很是简单:当您(或您的集群管理工具)运行docker stop时,Docker会向您的应用程序的入口点发送可配置的信号; SIGTERM是默认值。在对个人应用程序进行容器化时,我肯定了四个陷阱(但只进入了其中三个!),以便为您节省一些调查时间。docker

1:您使用了错误的ENTRYPOINT 格式 shell

Dockerfile容许您使用一种诱人的便捷字符串(称为shell格式)来定义入口点:数组

ENTRYPOINT "/app/bin/your-app arg1 arg2"

或者更使人讨厌的 exec 格式,它使您能够将命令行做为JSON数组提供:app

ENTRYPOINT ["/app/bin/your-app", "arg1", "arg2"]

长话短说:始终使用后一种exec格式。 Shell格式将您的入口点做为 /bin/sh -c 的子命令来运行,它带有不少问题,其中一个值得注意的问题是您在应用程序中永远看不到信号。框架

2:您的入口点是一个Shell脚本,您没有exec工具

若是您以常规方式从Shell脚本运行应用程序,则Shell将以新进程生成应用程序,而且您的应用程序将不会收到来自Docker的信号。命令行

您须要作的就是告诉您的Shell用您的应用程序替换自身。为此,shell具备 exec 命令(与前面讲到的 exec 格式类似)。详情见exec syscall日志

因此替换code

/app/bin/your-app

为:

exec /app/bin/your-app

您确实使用了exec,可是您经过启动子shel​​l欺骗了本身

我一直是runit日志记录的忠实拥护者,您只需在没有时间戳的状况下就将日志记录到stdout,而且runit会在全部日志条目前添加tai64n时间戳。

例如:

exec /app/bin/your-app | tai64n

可是,这致使您的应用程序在子shell中执行,其结果一般是:没有信号给您。

3:您监听了错误的信号

尽管SIGTERM在进程管理器中盛行,但许多框架仍但愿使用SIGINT ,例如Control-C中止应用程序。特别是在Python生态系统中,一般要作:

try:
    do_work()
except KeyboardInterrupt:
    cleanup()

若是不采起任何进一步的措施,则若是您的应用程序收到SIGTERM,将永远不会调用cleanup()。更糟的是,若是您的PID为1,那么超过最大等待时间,而后将SIGKILL发送到入口点以前,实际上什么也不会发生。

所以,若是您曾经想知道为何docker stop会花费这么长时间–这可能的缘由:您没有监听SIGTERM,而且因为PID为1,信号从您的进程中弹回。没有清理,缓慢关闭。

最简单的解决方法是在Dockerfile中添加一行:

STOPSIGNAL SIGINT

可是,您实际上应该尝试同时支持这两种信号,并首先避免成为PID 1。

总结

  • 使用ENTRYPOINT的 exec/JSON 数组形式。
  • 在shell程序入口点中使用exec。
  • 不要pipe应用程序的输出。
  • 避免成为PID 1。
  • 侦听SIGTERM或在Dockerfile中设置STOPSIGNAL。
相关文章
相关标签/搜索