Docker 容器中 PID 1 导致的 zombie 进程

PID 1 的责任

我们知道,在 Linux 内核启动的时候会先启动一个 init 进程,这个进程的 PID 始终为 1,通过 ps 命令你会发现这个进程是很多进程的父进程。

在 Linux 中,进程是用树来组织的,这个 init 就是根结点,很多重要的系统服务进程例如 sshdcron 都是 init 的子节点。

init 进程天生有个重要的任务,就是做好这个父节点的角色,把系统里所有的进程管起来。经常发生如下的情况,一个进程还没有等到子进程结束(waitpid),自己就因为某些原因退出了,那它的子进程就此时成为孤儿会被 init 接管,init 会很尽责的对他们做 waitpid,从而确保资源不被浪费。

容器中的世界

容器中的进程位于一个独立的 PID Namepsace 中,容器启动时的进程就是这个 Namespace 中的第一个进程,也就是 PID = 1。多数情况下,这个启动进程就是我们自己写的程序,或者 Nginx 之类的程序。

Unluckily,这些程序通常都不会有任何对于孤儿进程的妥善处理。也就是说,因为没有对这些接管过来的子进程做 waitpid,它们会一直存在。尽管内存已经释放了,但是会占用一个 PID 成为所谓的僵尸进程;如果这一行为不断发生,会耗尽系统的 PID 资源。

解决方案

对于大多数只有一级子进程的应用程序来说,这不是个很大的问题。

如果你的业务程序变得复杂,并且有必要产生两级子进程,那最好写一个 init 替代品来作为启动进程。这个项目可能会有帮助。

Reference

  1. Docker and the PID 1 zombie reaping problem