写点什么

dumb-init:一个 Docker 容器初始化系统

  • 2016 年 1 月 08 日
  • 本文字数:2274 字

    阅读完需:约 7 分钟

容器化环境中,往往直接运行应用程序,而缺少初始化系统(如 systemd、sysvinit 等)。这可能需要应用程序来处理系统信号,接管子进程,进而导致容器无法停止、产生僵尸进程等问题。

Yelp 开发的 dumb-init ,旨在模拟初始化系统功能,避免上述问题的发生。

问题的根源

对于开发人员来说,希望在容器中运行的进程和普通进程行为一致,这样才能大大降低容器化迁移的成本,而无须让开发人员关注容器初始化和退出的流程。

归功于 Linux 的名字空间(namespace),从容器中看,由容器创建的第一个进程 pid 为 1。而对于 Linux 来说,pid 为 1 的进程,有着特殊的使命:

  1. 传递信号,确保子进程完全退出
  2. 等待子进程退出

子进程的优雅退出

对于第一点,如果 pid 为 1 的进程,无法向其子进程传递信号,可能导致容器发送 SIGTERM 信号之后,父进程等待子进程退出。此时,如果父进程不能将信号传递到子进程,则整个容器就将无法正常退出,除非向父进程发送 SIGKILL 信号,使其强行退出。

考虑如下进程树:

  • bash(PID 1)
    • app(PID2)

bash 进程在接受到 SIGTERM 信号的时候,不会向 app 进程传递这个信号,这会导致 app 进程仍然不会退出。对于传统 Linux 系统(bash 进程 PID 不为 1),在 bash 进程退出之后,app 进程的父进程会被 init 进程(PID 为 1)接管,成为其父进程。但是在容器环境中,这样的行为会使 app 进程失去父进程,因此 bash 进程不会退出。

举个例子:

复制代码
docker run ubuntu /bin/bash -c '(sleep 1000 &) && sleep 2000'

该命令会启动的容器,内部进程结构为:

复制代码
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 17960 2816 ? Ss 13:05 0:00 /bin/bash -c (sleep 1000 &) && sleep 2000
root 7 0.0 0.0 4348 748 ? S 13:05 0:00 sleep 1000
root 8 0.0 0.0 4348 644 ? S 13:05 0:00 sleep 2000

此时,如果对这个容器发送 SIGTERM 信号,该容器将不会退出:

复制代码
docker kill -s SIGTERM 8ef469d46b52

注意,直接使用 docker kill 命令,会向容器发送 SIGKILL 信号强制杀死进程。docker stop 命令会先发送 SIGTERM 信号,等待超时时间之后,发送 SIGKILL 信号。因此,此时通过这两个命令都能够结束容器,但都不能“优雅的”结束进程。

僵尸子进程

另一个问题是等待子进程退出。前面提到过,init 进程另一个任务,是需要接管子进程,确保其能正常退出。但是一般应用程序,不会考虑实现接管进程功能。当应用程序进程在容器中运行时,其子进程创建的子进程,就有可能成为僵尸进程。

这里来模拟这个过程,首先启动一个容器,执行 sleep 命令:

复制代码
docker run ubuntu /bin/bash -c 'sleep 1000'

此时,容器中只有一个 sleep 进程,其 PID 为 1。这时,我们进入这个容器,再启动一个 bash 进程和一个 sleep 进程,模拟应用程序派生出来的子进程。

首先进入容器,

复制代码
docker exec -it 4ecdaafb501f /bin/bash

然后创建进程:

bash -c 'sleep 1000'这时,容器中有一个 PID 为 1 的 sleep 进程,一个 bash 进程,一个父进程为 bash 进程的 sleep 进程。

复制代码
root@4ecdaafb501f:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 13:30 ? 00:00:00 sleep 1000
root 6 0 0 13:30 ? 00:00:00 /bin/bash
root 21 6 0 13:31 ? 00:00:00 sleep 1000

再新开一个回话进入容器,然后对 PID 为 6 的 bash 进程发送 SIGKILL 信号,将其杀死,该操作模拟应用程序的子进程结束场景。此时,bash 进程的子进程 sleep 进程,由于失去了父进程,将会由 PID 为 1 的 sleep 进程进行托管。但是,由于 sleep 命令不是标准的 init 系统,没有实现子进程托管的功能。此时的 PID 为 21 的进程,虽然已经结束,但是其没有被父进程回收(通过 waitpid 系统调用),进入僵尸进程状态。

复制代码
root@4ecdaafb501f:/# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 4348 668 ? Ss 13:30 0:00 sleep 1000
root 21 0.0 0.0 0 0 ? Z 13:31 0:00 [sleep] <defunct></defunct>

dumb-init 来了

dumb-init 解决了上述两个问题:向子进程代理发送信号和接管子进程。

默认情况下,dumb-init 会向子进程的进程组发送其收到的信号。原因也很简单,前面已经提到过,像 bash 这样的应用,自己接收到信号之后,不会向子进程发送信号。当然,dumb-init 也可以通过设置环境变量DUMB_INIT_SETSID=0来控制只向它的直接子进程发送信号。

另外 dumb-init 也会接管失去父进程的进程,确保其能正常退出。

dumb-init 使用

要在容器中使用 dumb-init,可以直接安装 deb 包,或者从源码构建。容器启动时,使用 dumb-init 作为初始进程,确保所有子进程都由 dumb-init 进程创建:

复制代码
docker run my_container dumb-init python -c 'while True: pass'

除了在容器中使用之外,dumb-init 也可以直接在 shell 脚本中使用。使用 dumb-init 作为 shell 的父进程,可以解决 shell 创建的子进程优雅退出问题。这种场景使用方式类似于 supervisord 或者 daemontools ,直接将脚本的 shebang 改成#!/usr/bin/dumb-init /bin/sh即可。


感谢魏星对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群InfoQ 好读者(已满),InfoQ 读者交流群(#2)InfoQ 好读者)。

2016 年 1 月 08 日 18:0026602

评论

发布
暂无评论
发现更多内容

苏州园区:面向全国“发帖”聚力 加快创建区块链技术应用高地

CECBC

WEB-API的设计与开发

liin

HTTP 软件设计 web tech

Flutter 学习笔记(二) Container 组件

U+2647

flutter 4月日更

【LeetCode】存在重复元素 III Java题解

Albert

算法 LeetCode 4月日更

架构实战营 - 模块 2- 作业

请弄脏我的身体

架构实战营

阿里致敬武侠首发“Java架构修炼笔记”,深入内核,拒绝蒙圈

Java架构师迁哥

如何做向上管理?

石云升

28天写作 职场经验 4月日更 向上管理

架构实战营 - 模块二作业

Sun

让孩子爱上阅读(二)

箭上有毒

读书笔记 4月日更

增强产业链供应链自主可控能力,区块链能否贡献力量?

CECBC

区块链

2020从干饭人到打工人

空城机

生活 生活记录 杂记 4月日更

Github接近10w点赞!阿里巴巴内部Java面试参考指南

Java 程序员 架构 面试

我是如何从零开始学Python: (1)如何选择合适的Python学习工具?

广之巅

Python 4月日更

架构实战营 模块二:课后作业

👈

架构实战营

架构实战营 模块二:学习总结

👈

架构实战营

Seldon 使用 (三): 模型服务如何运行

托内多

tensorflow kubeflow Kubernetes PyTorch seldon

架构实战营 - 模块 2- 作业

冬天的树

架构实战营 - 模块二作业

凯迪

架构实战营

业务架构训练营第 0 期模块二作业

目标一个亿

模块2-微信朋友圈高性能架构设计

yu

nginx反向代理和负载均衡策略实战案例

赖猫

nginx

wkhtmltopdf实践

六维

4月日更 wkhtmltopdf

陪伴的进化

小天同学

陪伴 爱情 个人感悟 4月日更 亲情

Spark运行状态监控与优化

小舰

4月日更

这套Java面试题推出第二天就惨遭全网封杀!已帮我拿下15个Offer

Java架构追梦

Java 阿里巴巴 架构 面试 金三银四

阿里巴巴用实践告诉你,架构师到底需要掌握什么样的技术?

Java架构师迁哥

Nacos实践

程序员架构进阶

源码分析 微服务 nacos 28天写作 4月日更

华为“引商”,VR“刻羽”,共觅知音人

脑极体

模块2作业

灯火阑珊

重读《重构2》- 引入参数对象

顿晓

重构 4月日更

dumb-init:一个Docker容器初始化系统_语言 & 开发_金灵杰_InfoQ精选文章