Docker 容器中的僵尸进程

之前并非通过 docker stop 来停止程序和容器,而是通过容器中的 shell 脚本,在不停止容器的情况下,通过运行 shell 脚本来杀死程序。

这种方式导致每次停止程序后,被停止的进程都会变成一个僵尸进程。即被标记为 defunct 的进程。僵尸进程只能通过重启容器的方式消除。

尝试在容器内直接运行二进制程序,使其保持在前台运行,并通过 Ctrl + C 退出,不会产生僵尸进程。

docker 容器中僵尸进程产生的原因

Linux 中子进程通常由其父进程 fork 创建,并在子进程退出时回收子进程的资源。

当父进程先于子进程退出时,子进程会变成孤儿进程,而其父进程会变更为 init 进程。

当子进程已终止,但父进程尚未回收子进程资源时,子进程的残留资源(PCB)存放于内核中,变成僵尸进程。

Linux 中,若子进程缺失父进程,其残留资源会由 init 进程回收。但在 Docker 中,容器并非一个完整的操作系统,不会初始化 init 进程,容器中的第一个进程只是一个普通进程,所以并不会回收僵尸进程。

当使用 shell 脚本在后台执行目标程序时,shell 脚本会在启动目标程序后立刻退出而不会等待目标程序结束。shell 脚本尚未退出时,目标程序的父进程为当前脚本程序,而当 shell 脚本退出后,目标程序的父进程为当前终端进程。

终端进程并非 init 进程,不会回收孤儿进程的残留资源,所以子进程最终会变为僵尸进程。

问题修复

之前将 docker 容器当作虚拟机来使用,导致产生了很多问题并且没有真正减轻运维负担。

正确的做法是将 docker 容器作为单一服务,使用真正的容器化部署方式。