docker打基础镜像处理僵尸进程

在使用docker容器的时候,应该了解“PID1僵尸进程reap”问题。如果使用的时候不加注意,可能会导致出现一些意想不到的问题。

问题

僵尸进程

僵尸进程是指完成执行(通过exit系统调用,或运行时发生致命错误或收到终止信号所致),但在操作系统进程表中仍然有一个表项,处于“终止状态”的进程。这发生于子进程需要保留表项以允许其父进程读取子进程的exit status:一旦退出态通过wait系统调用读取,僵尸进程条目就从进程表中删除,这个过程被称为reap。正常情况下,进程直接被其父进程wait并由系统回收,进程长时间保持僵尸状态一般是错误的并导致资源泄漏。

英语中的zombie process源自丧尸–不死之人,隐喻进程已死大但没有被reap。与正常进程不同,kill命令对僵尸进程无效。孤儿进程不同于僵尸进程,其父进程已经死掉,但孤儿进程仍能正常执行,并不会变为僵尸进程,因为init进程会收养并wait其退出。

子进程死后,系统会发送SIGCHLD信号给父进程,父进程对其默认处理是忽略。如果想响应这个消息,父进程通常在SIGCHLD信号处理程序中,使用wait系统调用来响应子进程的终止。

僵尸进程被reap后,其进程号与在进程表中的表项都可以被系统重用。但如果父进程没有调用wait,僵尸进程将保留进程表中的表项,导致资源泄漏。

reap僵尸进程的方式是通过kill命令手工向其父进程发送SIGCHLD信号,如果其父进程仍然拒绝reap僵尸进程,则终止父进程,使得init进程收养僵尸进程。init进程周期执行wait系统调用reap其所收养的所有僵尸进程。

为避免产生僵尸进程,实际应用中一般采取的方式是:

  1. 将父进程中对SIGCHLD信号的处理函数设置SIG_IGN
  2. fork两次并杀死一级自进程,令二级子进程成为孤儿进程而被init所“收养”、清理

解决办法

Tini是能想到的最简单的init

Tini一般在容器中运行,用于生成子进程,等待它推出,reap僵尸进程,并执行信号转发。

在github上是这么介绍的:

1
2
3
4
5
6
Why Tini?
Using Tini has several benefits:

It protects you from software that accidentally creates zombie processes, which can (over time!) starve your entire system for PIDs (and make it unusable).
It ensures that the default signal handlers work for the software you run in your Docker image. For example, with Tini, SIGTERM properly terminates your process even if you didn't explicitly install a signal handler for it.
It does so completely transparently! Docker images that work without Tini will work with Tini without any changes.

docker打一个tomcat镜像

措施

  • 基于以上要求我们构建出一下镜像
  • 首先使用apline作为基础镜像足够小只有5M
  • 由于alpine自带支持中文的字符集,这里我们只需要将LANG设置为C.UTF-8即可完美的支持中文。
  • 国内软件源首选阿里云啦,顺道配置一下阿里云的镜像源,加速我们的镜像构建速度。
  • 配置UTC+8时区需要安装tzdata,安装完成之后配置一下即可。
  • 使用tini 包装java进程。

dockerfile

tini的dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM alpine:3.8
ENV LANG=C.UTF-8 \
JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk/jre \
PATH=$PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin \
TZ=Asia/Shanghai
RUN echo "" > /etc/apk/repositories \
&& echo "https://mirrors.aliyun.com/alpine/v3.8/main/" >> /etc/apk/repositories \
&& echo "https://mirrors.aliyun.com/alpine/v3.8/community/" >> /etc/apk/repositories \
&& apk update && apk add --no-cache openjdk8-jre openjdk8 ca-certificates tzdata tini \
&& apk add --update procps \
&& rm -rf /var/cache/apk/* \
&& ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENTRYPOINT ["tini"]

tomcat的dockerfile

1
2
3
4
5
FROM sy:tini

ADD apache-tomcat-8.5.43.tar.gz /opt/

CMD ["/sbin/tini", "--","/opt/apache-tomcat-8.5.43/bin/catalina.sh","run"]
1
docker build -t sy-tomcat:tini -f Dockerfile .

这样打完的镜像不仅小,也解决了僵尸进程,包括jmap等命令用不了的问题

Donate