乐趣区

关于docker:容器中的1号进程

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 是最好的。
退出移动版