Redian新闻
>
为啥你的容器刚启动就停了?这篇文章告诉你

为啥你的容器刚启动就停了?这篇文章告诉你

公众号新闻

运维研习社

很多docker初学者,在运行容器的时候,或者是写第一个dockerfile的时候,问题最多的就是容器启动后就停了,怎么看都觉得命令没有问题,容器也没有错误日志,dockerfile也就那么几条……

其实你没有错,错的是docker,它执行的太快了

这话怎么说呢,我拿nginx官方的dockerfile给你解释下

上面是nginx官方的dockerfile文件,我把set部分删掉了,其他没啥,主要看下CMD

为什么这里不是systemctl nginx start,或者/etc/init.d/nginx start,再或者nginx直接启动,而是用daemon off的方式启动?

这是因为如果nginx用后台模式运行,启动的命令执行完之后,这个启动的命令就退出了,这个时候,容器也就跟着退出了

又为什么命令执行完,容器就退出了?这个要从linux内核说起

在linux操作系统中,当内核初始化完毕之后,会启动一个init进程,这个进程是整个操作系统的第一个用户进程,所以它的进程ID为1,也就是我们常说的PID1进程,然后所有的用户态进程,都是这个进程的子进程,所以,整个系统的用户进程,都是由init进程作为根进程的

要了解这个PID1进程,要从以下几个概念了解

  • 进程表项

linux内核程序通过进程表对进程进行管理, 每个进程在进程表中占有一项,称为进程表项,它记录了进程的状态,打开的文件描述符等等一系统信息。当一个进程结束了运行或在半途中终止了运行,那么内核就需要释放该进程所占用的系统资源。这包括进程运行时打开的文件,申请的内存等。但是,这里要注意的是,进程表项并没有随着进程的退出而被清除,它会一直占用内核的内存。为什么会有这么奇怪的行为呢?这是因为在某些程序中,我们必须明确地知道进程的退出状态等信息,而这些信息的获取是由父进程调用wait/waitpid而获取的。设想这样一种场景,如果子进程在退出的时候直接清除文件表项的话,那么父进程就很可能没有地方获取进程的退出状态了,因此操作系统就会将文件表项一直保留至wait/waitpid系统调用结束

  • 僵尸进程

僵尸进程指的是:进程退出后,到其父进程还未对其调用wait/waitpid之间的这段时间所处的状态。一般来说,这种状态持续的时间很短,所以我们一般很难在系统中捕捉到。但是,一些粗心的程序员可能会忘记调用wait/waitpid,或者由于某种原因未执行该调用等等,那么这个时候就会出现长期驻留的僵尸进程了。如果大量的产生僵尸进程,其进程号就会一直被占用,可能导致系统不能产生新的进程

然后还有我们经常会见到的一种情况,就是父进程先于子进程结束,这种情况多见于手动kill某个父进程的情况,这种情况就是下面要说到得

  • 孤儿进程

父进程先于子进程退出,那么子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)接管,并由init进程对它完成状态收集(wait/waitpid)工作

PID1负责清理那些被抛弃的进程所留下来的痕迹,有效的回收的系统资源,保证系统长时间稳定的运行

了解了linux的PID1,接着来看下容器中的PID1进程

熟悉docker都知道,docker容器并不是一个完整的linux的操作系统,它也没什么内核初始化过程,更没有像init(1)这样的初始化过程。在docker容器中被标志为PID1的进程实际上就是一个普通的用户进程,我们还拿nginx官方的镜像起的容器来看

我用docker run -d nginx直接启动的

可以看到,就是Dockerfile中指定的CMD那个进程,注意:如果你启动容器的时候,指定了命令,会覆盖CMD,也就是CMD是条默认启动的命令参数,如果启动容器时指定了命令,会覆盖,当Dockerfile中有多条CMD时,执行最后一条

这个进程其实在宿主机上有一个普通的用户进程ID

之所以在容器中PID变成1,是因为linux内核提供的PID namespaces功能,如果宿主机上所有用户进程构成了一个完整的树形结构,那么PID namespaces实际上就是将这个CMD或ENTRYPOINT进程及其子进程作为另外一个分支,很显然这部分也是一个树形结构

当我们在宿主机上kill掉这个进程ID,那么整个容器便会处于退出状态

这也就解释了上面为什么命令执行完之后,容器就退出了

认真的小伙伴从上面图中看到了,我上面说linux中PID1进程为所有用户进程的父进程,但是在容器里面,通过ps命令看到的进程的父进程都是“0”,这又是为什么呢?

前面提到,容器中的进程树实际上是宿主机进程树的一棵子树,或者说分支,那么我们在宿主机上就可以找到这颗子树的父进程

我们可以看到,这个docker容器中PID 0的进程应该就是这个containerd-shim

我们结合docker的结构图看一下

从架构图中,我们可以看到containerd-shim进程下还有一个runC进程,但是我们在上面过程中,并没有发现runC这个进程

runC是OCI标准的一个参考实现,而OCI Open Container Initiative,是由多家公司共同成立的项目,并由linux基金会进行管理,致力于container runtime的标准的制定和runc的开发等工作。runc,是对于OCI标准的一个参考实现,是一个可以用于创建和运行容器的CLI(command-line interface)工具。runc直接与容器所依赖的cgroup/linux kernel等进行交互,负责为容器配置cgroup/namespace等启动容器所需的环境,创建启动容器的相关进程

事实上,Docker容器的创建过程是这样子的 docker-containerd-shim –> runC –> entrypoint,而我们看到的最终状态是 docker-containerd-shim –> entrypoint,而runc进程创建完容器之后,自己就先退出去了,所以我们上面的过程中一直没有出现

看到这里你应该了解,为什么你启动容器或写好的dockerfile,总是刚启动就退出,而且没有任何错误了吧!

END

官方站点:www.linuxprobe.com

Linux命令大全:www.linuxcool.com

刘遄老师QQ:5604241

Linux技术交流群:3762708

(新群,火热加群中……)

想要学习Linux系统的读者可以点击"阅读原文"按钮来了解书籍《Linux就该这么学》,同时也非常适合专业的运维人员阅读,成为辅助您工作的高价值工具书!


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
1000+世界名著,这篇告诉你哪20本值得读孩子看见数学就头疼?那一定要看看这篇文章!当你失去从容,方寸大乱时,不妨看看这篇文章背完这篇文章,以后再也不怕写作文了(良心整理,建议收藏)教育随笔(111)高考只讲淘汰率,不讲合格率足球彩票怎么买才能赚钱?看完这篇文章再下注!鱼水之情只要5分钟,请为你的妈妈看完这篇文章!如何在容器中进行抓包?一篇文章搞懂其原理!房子究竟还能买吗?这篇文章说透了!这个秋天,你想进入金融圈找到理想工作,请认真看完这篇文章三线建设对子孙的生活息息相关《一点声明》+《黛玉与宝钗的诗才比拼》一个人的徒步,900公里法国之路+世界尽头:D50~我与波尔图有个约这篇文章教您看懂肺癌病理报告发烧以后不能吃高蛋白,要吃清淡的?这篇文章告诉你真相人人都在降价,为啥你星巴克越卖越贵?如何让孩子爱上读科普,提高求知欲?这篇文把细节都讲了……科普有多重要?国家有哪些支持?这篇文章都说清楚了 | 袁岚峰通过这篇文章,邀请大家认识我的佤族朋友。为什么中国经济总能化危为机 人民日报这篇文章谈了5点至暗时刻结束!影响千万人的资产大变革,才刚刚启动……听说你今天想去参加纽约时代广场的跨年仪式,那你现在看这篇文章还来得及!在这篇文章里,再快的人也只能停留三分钟。电解质水全网被抢断货?这篇文章告诉你到底有没有用。别被吃药顺序图误导了,这篇文章带你了解新冠用药真相这篇文章,让你彻底了解「共情」这件事Test Optional的美本大学,标化在审核中的占比如何?这篇文章让你看清大学的真面目!纸像人的身体,是一种邂逅无限的容器为啥你总是“上班如上坟”?该如何选择,这篇文章给你说清楚CES 2023落幕,亮点都在这篇文章里了这篇文章保你学通网络基础知识新西兰能成富国吗?人们能安居乐业吗?这篇文章真相了!AI模型告诉你,为啥巴西最可能在今年夺冠!曾精准预测前两届冠军
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。