Linux零碎中,PID为1的过程表演了非常重要的角色,在容器衰亡后,因为其秉承的准则是「one process per container」或「one thing per container」,这个时候谁来当容器内的1号过程就是一个须要答复的问题。

一般来说,容器内的过程可能有这么几种状况:

  1. 只有一个过程个,就是1号过程
  2. 1号过程+由它派生进去的整个过程树

对Docker 进行一个容器而言, 其发送stop命令时, 实质上就是发送一个SIGTERM信号给容器内的1号过程, 而后1号过程接管信号后退出, 如果有子过程,那么也须要一并退出.

对于状况1还好,只有1个过程,没有那么多幺蛾子。对于状况2,如果1号过程可能精确地将进行信号转发给所有子过程,并能顺利完结整个过程树,这样也还OK。

问题出在:

  1. 很多人的容器启动命令是用shell脚本启动的( 当初你能够进入业务容器内用 ps 看一下过程树 )
  2. 很多的shell脚本没有采纳Docker所举荐的exec模式

这就导致了:

  1. bash过程实际上是容器内的1号过程, 你的业务过程实际上是bash的子过程
  2. bash过程不能转发信号

这就导致了:业务过程没法收到进行的信号,间接期待Docker在过了超时工夫后强行将整个容器停掉。

这会导致什么问题呢?

假如:

  1. 业务过程会向某种注册核心注册( 这不要太常见,服务注册根本是标配 )
  2. K8S治理着容器的编排调度,那么在K8S的语境下,一个容器是会被随时kill或者重启的,kill无非也是调用底层容器引擎的命令( 不光是K8S,其余容器编排工具也会存在相似问题 )
    后果是:想下线一个服务,那么应用K8S kill对应容器,而后返回了。因为你的启动命令是shell包裹的,所以bash是1号过程,它无奈转发终止信号给业务过程,所以容器内的业务过程须要期待超时工夫完结之后才会被强行进行(与你「过程立即完结」的冀望不符),所以导致想下线的服务迟迟无奈从注册核心下线。

怎么解决呢?
有几种计划:

  1. 应用 Shell exec 的模式,使你的业务过程就是1号过程。PS:能够参考Redis或MySQL等的docker-entrypoint.sh是如何写的
  2. 应用相似tini或dumb-tini的工具,充当1号过程,它实现了转发性能。PS:dumb-tini还有坑,能够网上搜寻。相似的能够看看Jenkins的启动脚本是怎么编写的
    其中计划1是最好的。