- GreatSQL 社区原创内容未经受权不得随便应用,转载请分割小编并注明起源。
一、发现问题
小玲是一名数据库测试人员,这一天她尝试在 docker 环境中部署 GreatDB 集群,后果在对 greatsqld 过程进行 kill 操作后,意外发现 greatsqld 过程变成了僵尸过程(如下图所示)。
因为小玲成为测试人员的工夫较短,她没有遇见过这种状况,所以无奈第一工夫判断这个景象,是她的操作失误导致的正当景象,还是一个新发现的 bug。
于是她进行了以下尝试:
- 1. 在物理机上反复上述操作,并没有呈现僵尸过程。
- 2. 在 docker 内,对 MySQL 的 mysqld 过程进行雷同操作,呈现了僵尸过程。
通过这样的尝试,小玲初步判断出 docker 环境才是产生僵尸过程的本源,但具体是什么起因,又该如何防止,还须要小玲的进一步摸索。
二、僵尸过程简介
首先须要搞清楚的是:
- 僵尸过程是什么?
- 僵尸过程又是如何造成的?
以下定义内容和解决办法来自维基百科。
僵尸过程:在类 UNIX 零碎中,僵尸过程是指实现执行(通过 exit 零碎调用,或运行时产生致命谬误或收到终止信号所致),但在操作系统的过程表中依然存在其过程管制块 (PCB),处于 "终止状态" 的过程。僵尸过程不能被杀死,因为它们曾经死亡,只期待它们的父过程回收它们。
通过这样的概念,和其余一些相干资料,小玲理解了僵尸过程的呈现,问题多半是呈现僵尸过程的父过程上。
这时,小玲看到了一个比较简单粗犷的解决办法,就是间接将僵尸过程的父过程杀死。她立即去实际了一番,惋惜她又失败了。
在 docker 中,她 kill 产生的僵尸过程的父过程 PID 是 1,而 1 这个过程在 docker 外部是 kill 不掉的;如果在 docker 内部的物理机找到 1 的对应过程进行 kill,则会将整个容器杀死。
三、孤儿过程的呈现
小玲并不泄气,她持续通过搜索引擎进行摸索学习,很快她就理解到更多的概念。
当过程的父过程 id 变为 1 的时候,这个过程也有了专门定义的称呼,叫做孤儿过程。
孤儿过程:父过程完结后仍在运行的子过程。
没有了父过程总要有人“关照”它。
在类 UNIX 操作系统中,为防止孤儿过程退出时无奈开释所占用的资源而僵死,任何孤儿过程产生时都会立刻被零碎过程 init 或 systemd 主动接管为子过程,这一过程也被称为“收养”。在此需注意,尽管事实上该过程已有 init 作为其父过程,但因为创立该过程的过程已不存在,所以仍应称之为“孤儿过程”。
依据下面的解释,又引出来一个概念,init 过程。咱们也来看下它的定义。
init 过程:
是 Unix 和 类 Unix 零碎中用来产生其它所有过程的程序。它以守护过程的形式存在,其过程号为 1。init 过程是非凡的:它不取得它不想解决的信号,因而它能够疏忽 SIGKILL。
这里又有个新概念,SIGKILL 信号。
个别咱们 kill 过程是发信号给过程,而后被过程捕捉之后执行对应操作。次要须要理解的信号有上面 3 种。都是来终止过程应用的。
四、整顿景象产生的过程
小玲曾经分明这个景象跟 GreatDB 和 MySQL 都没有关系了,于是应用 sleep 命令持续尝试。
进入容器,执行命令。此时子过程 PID 是 61,父过程 PID 是 44,父过程是 bash。
退出容器,再进入容器。PID 是 44 的 bash 过程因为退出容器的操作被杀死,子过程 61 就被 init 收养了,父过程的 PID 就变成了 1。
杀掉这个过程 61,而后查看这个过程状态,发现它曾经成为一个僵尸过程。
到这里,小玲就曾经很靠近问题的假相了:docker 容器在默认的参数配置下,其 init 过程并没有解决孤儿过程的能力。
对于这个问题,docker 的开发者们曾经思考到了。
五、如何解决 docker 中的僵尸过程
有两个解决办法能够让 docker 的 init 过程可能解决孤儿过程。
1. 启动 docker 容器时,指定 init 过程为 bash,由 bash 过程对孤儿过程的资源进行回收。
2. 减少专门的 init 过程,比方 tini。
咱们能够去 docker 官网文档找到答案:
The container’s main process is responsible for managing all processes that it starts.
In some cases,the main process isn’t well-designed,and doesn’t handle“reaping”(stopping) child processes gracefully when the container exits.
If your process falls into this category,you can use the --init option when you run the container.
The --init flag inserts a tiny init-process into the container as the main process,and handles reaping of all processes when the container exits.
Handling such processes this way is superior to using a full-fledged init process such as sysvinit,upstart,or systemd to handle process lifecycle within your container.
依据文档倡议,能够在启动容器时候加上 –init 参数,开始应用 tini。这样会有一个 tiny 的过程来负责过程 ” 收养 ” 之后的解决工作。
接下来咱们别离对两种办法进行验证。
5.1 bash 解决
咱们尝试将启动容器的命令指定为 bash 来启动一个容器 test01,来看下这个容器会不会呈现文章结尾的问题。
启动容器,指定命令为 bash。这个镜像基于 CentOS 7 的镜像,装了一些罕用的包。
进入 test01,确认 id 为 1 的过程。
执行 sleep 700s &,而后查看父过程 id 为 16,子过程 id 为 33。
退出,从新登录容器 test01,查看过程 33 的父过程,曾经被 init 过程“领养”。
kill 过程 33。而后 check 过程 33。发现 33 没有成为僵尸,曾经彻底被终止了。
从下面步骤来看,指定启动命令为 bash 的确能够解决咱们遇到的问题。
5.2 tini 解决
再来试下 tini,它是应用 C 语言写的一个 tiny 级的 init 过程,github 对此有具体的介绍。
这是一个相当轻量级的 init 零碎,相比拟传统的 Upstart、Systemd、SysV init 之类的大型零碎应用起来更加的优良。
当初咱们在启动 docker 容器时时候加上 –init 参数,来看下成果。
加上 init 参数,启动容器 test02,启动命令应用 tail -f /dev/null,而后登录查看过程状况。
登录 test02,执行 sleep 700s &。查看父子过程 id。
退出登录。
再次登录之后,查看过程的父过程 id。
kill 过程,查看过程是否彻底终止。曾经看到没有僵尸过程。
六、总结
在应用 tail -f /dev/null 作为 docker 容器的 init 过程时,如果不加上 –init 参数,就会因为 init 过程没有解决孤儿过程的能力,而导致僵尸过程的呈现。
在这种状况下,咱们能够将 tail -f /dev/null 替换成 bash,或是加上 –init 来解决这个问题。
文章举荐:
GreatSQL MGR FAQ
https://mp.weixin.qq.com/s/J6…
万答 #12,MGR 整个集群挂掉后,如何能力主动选主,不必手动干涉
https://mp.weixin.qq.com/s/07…
『2021 数据技术嘉年华·ON LINE』:《MySQL 高可用架构演进及实际》
https://mp.weixin.qq.com/s/u7…
一条 sql 语句慢在哪之抓包剖析
https://mp.weixin.qq.com/s/AY…
万答 #15,都有哪些状况可能导致 MGR 服务无奈启动
https://mp.weixin.qq.com/s/in…
技术分享 | 为什么 MGR 一致性模式不举荐 AFTER
https://mp.weixin.qq.com/s/rN…
对于 GreatSQL
GreatSQL 是由万里数据库保护的 MySQL 分支,专一于晋升 MGR 可靠性及性能,反对 InnoDB 并行查问个性,是实用于金融级利用的 MySQL 分支版本。
Gitee:
https://gitee.com/GreatSQL/Gr…
GitHub:
https://github.com/GreatSQL/G…
Bilibili:
https://space.bilibili.com/13…
微信 &QQ 群:
可搜寻增加 GreatSQL 社区助手微信好友,发送验证信息“加群”退出 GreatSQL/MGR 交换微信群
QQ 群:533341697
微信小助手:wanlidbc
本文由博客一文多发平台 OpenWrite 公布!