dumb-init:一个Docker容器初始化系统
容器化环境中,往往直接运行应用程序,而缺少初始化系统(如systemd、sysvinit等)。这可能需要应用程序来处理系统信号,接管子进程,进而导致容器无法停止、产生僵尸进程等问题。dumb-init旨在模拟初始化系统功能,避免上述问题的发生。
容器化环境中,往往直接运行应用程序,而缺少初始化系统(如systemd、sysvinit等)。这可能需要应用程序来处理系统信号,接管子进程,进而导致容器无法停止、产生僵尸进程等问题。
Yelp开发的dumb-init,旨在模拟初始化系统功能,避免上述问题的发生。
问题的根源
对于开发人员来说,希望在容器中运行的进程和普通进程行为一致,这样才能大大降低容器化迁移的成本,而无须让开发人员关注容器初始化和退出的流程。
归功于Linux的名字空间(namespace),从容器中看,由容器创建的第一个进程pid为1。而对于Linux来说,pid为1的进程,有着特殊的使命:
- 传递信号,确保子进程完全退出
- 等待子进程退出
子进程的优雅退出
对于第一点,如果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>
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
即可。
dumb-init:一个Docker容器初始化系统相关推荐
- docker新建Linux虚拟机,RHEL/CentOS 7下创建你的第一个Docker容器
Docker容器人气一直在增长.他们现在正在快速采用,正在取代虚拟机,特别是在应用程序开发方面. 在本教程中,我们将讨论Docker容器的内容,并将学习在RHEL/CentOS 7上安装它的过程. D ...
- docker多个容器一起打包_详解Docker 容器基础系统镜像打包
因为容器本身是共享宿主操作系统内核,所以容器基础系统镜像包本身就是一个标准的 Linux rootfs + 用户自定义的工具.根据这个思路,我们就可以构建一个自己的容器基础系统镜像. 构建标准的 Li ...
- 拉起Docker容器初始化数据源时,报:org.postgresql.util.PSQLException: The connection attempt failed. 的解决办法
问题概述 关于这个问题,博主是在进行容器化部署的时候遇到的, 这个问题,说是问题又不是问题,为了紧贴单位的发展战略及发展文化方针,给单位培养人才,先大胆的让同事先尝试着操作,最后出现问题了,博主就出场 ...
- 服务器系统打包,详解Docker 容器基础系统镜像打包
因为容器本身是共享宿主操作系统内核,所以容器基础系统镜像包本身就是一个标准的 Linux rootfs + 用户自定义的工具.根据这个思路,我们就可以构建一个自己的容器基础系统镜像. 构建标准的 Li ...
- 运行第一个docker容器
我们来学习SpringCloud部署方面的知识,我们先来个简单的,这是Eureka,首先你电脑上得先安装Docker,docker run -p 8761:8761 -d hub.c.163.com/ ...
- Linux怎么查询全部容器时间,linuxea:如何单单修改docker容器的系统时间
一般情况下,我们仅仅需要修改容器的时间与我们宿主机的实际实际一致即可, 我们知道,默认情况下docker容器是不允许访问系统时钟,但是有一款开源的软件使这样的需求变成了可能.此lib拦截用于检索当前时 ...
- (八)构建一个Docker容器来训练Deep Fake Autoencoders
目录 我们的Docker容器的结构 编码Dockerfile 定义config.yaml文件 编写task.py文件 编码model.py文件 编码我们的data_utils.py文件 构建Docke ...
- 创建一个docker容器
根据第一节,把docker安装完毕并学习了基本操作之后,我们来学习构建一个docker应用程序 要创建一个便携的镜像,首先需要创建一个叫做Dockerfile的文件,这个文件定义了你要创建的容器所需要 ...
- 如何搭建一个docker容器
搭建docker容器 一. 概述: Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源,让我们开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发 ...
最新文章
- Object of type 'ListSerializer' is not JSON serializable “listserializer”类型的对象不可JSON序列化...
- 带参数二维码如何跟踪用户来自哪个推广人员?
- bilibili有电脑版吗_电脑版和平精英你期待吗?可惜国内并未上线
- Orleans配置---持久化
- Linux 环境变量启动过程/配置文件的读取过程
- 全国计算机基础知识考试题型,盘点 | 全国计算机等级考试一般考哪些内容?
- group by后可以join吗_去韩国留学毕业后可以留在韩国吗
- python数据结构与算法分析 第2版_题库 | 百度数据结构 / 算法面试题型介绍及解析 第 2 期...
- 利用 Finder 清理Mac旧档案,释放空间
- 月薪三万,依然买不起房
- android与php登录,Android开发中使用PHP服务器怎么实现一个登录功能
- python开发的代码如何加密_python 代码加密
- 解决2种jni加载错误
- python 东方财富接口_东方财富 股票数据接口_
- 在Android上将实时摄像头与AI危害检测配合使用
- php计算macd,macd计算公式?MACD指标的原理是什么
- 洛谷 P3987 我永远喜欢珂朵莉~(Splay+BIT+无限卡常)
- 通过piranha搭建lvs高可用集群
- 从JDBC到Mybatis
- WGCLOUD在windows部署运行怎么实现隐藏窗口