点击围观!腾讯 TAPD 助力金融行业研发提效、敏捷转型最佳实践! 了解详情
写点什么

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

  • 2016-01-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-01-08 18:0027419

评论

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

音视频详细学习路线和权威资料

hanaper

音视频 ffmpeg 语音识别 语音合成 图形图像处理

详解Java内存模型

程序员阿杜

Java JVM Java虚拟机

网络攻防学习笔记 Day91

穿过生命散发芬芳

网络攻防 7月日更

揭开进程的概念、状态、通信的迷雾。看完瞬间豁然开朗

Linux服务器开发

线程 网络编程 Linux服务器开发 Linux后台开发 进程管理

Drools 入门

LeifChen

drools 规则引擎 8月日更 业务规则

体验设计工具:18格窗口

石云升

用户体验 7月日更 体验设计

程序员有哪些不可或缺的效率神器?

Jackpop

开发

熬夜整理的c/c++万字总结(一)

C语言与CPP编程

c c++

毕业总结

请弄脏我的身体

架构实战营

阿里面试官把以往的Java面试题全部总结在这份《Java10W字面试复盘笔记》里面了

Java 程序员 架构 面试 计算机

Fil行情:什么时候投资fil合适?

区块链 分布式存储 IPFS fil fil行情

牛客网爆火!面试命中率高达 90% 的阿里 10W 字面试笔记已被疯传

Java 程序员 架构 面试 计算机

各国纷纷推出数字货币,数字货币发展正当其时

CECBC

等待结果

IT蜗壳-Tango

7月日更

想要跳槽拿高薪,却没有大型性能调优经验怎么办?淘宝架构师手把手带你前进

Java架构师迁哥

网络安全现状,一个黑客真实的收入

网络安全学海

黑客 网络安全 信息安全 渗透测试 漏洞分析

操作系统--虚拟内存

en

Vue进阶(幺柒柒):Vue 应用 Sass、Scss、Less 和 Stylus

No Silver Bullet

Vue 7月日更 预处理器

Introduction to the Keras Tuner

毛显新

tensorflow

Tensorflow日常随笔(一)

毛显新

tensorflow

「SQL数据分析系列」13. 索引和约束

数据与智能

sql 分布式

Vue进阶(幺陆陆):组件实例 $el 详解

No Silver Bullet

Vue 7月日更 $el

程序员专属的搜索主页

程序员阿杜

搜索技巧 搜索引擎;

北鲲云超算平台如何提高高性能计算在云环境下的可行性?

北鲲云

Druid 查询返回引擎版本困惑的地方

HoneyMoose

外包学生管理系统的架构设计

架构0期-Bingo

面试官:聊聊JVM吧?

程序员阿杜

Java JVM JVM原理

Text classification with TensorFlow Hub: Movie reviews

毛显新

tensorflow

开发者必备神器,你真的会用吗?

Jackpop

区块链产业政策红利加速释放

CECBC

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