一般的docker镜像为了节省空间,通常是没有安装systemd或者sysvint这类初始化系统的进程。一旦容器的起始进程不稳定将会产生大量的僵尸进程,影响宿主系统的运行。

缺少init的容器

init系统有以下几个特点:

  • 它是系统的第一个进程,负责产生其他所有用户进程。
  • init 以守护进程方式存在,是所有其他进程的祖先。
  • 它主要负责:

1.启动守护进程

2.回收孤儿进程

3.将操作系统信号转发给子进程

以下dockerfile为例:

FROM nginxENTRYPOINT ["nginx", "-c"]
CMD ["/etc/nginx/nginx.conf"]

当docker容器启动时,PID 1即容器启动程序将会是nginx, 只要这个程序停止,容器就会跟住停止。由于nginx 不具备init 上述的功能,PID 1是无法回收异常退出进程,异常退出的进程变成僵尸进程,继续占用系统资源。

当多个容器运行在一个宿主机上的时候,为了避免一个容器消耗完我们整个宿主机进程号资源,docker会配置PID CGROUP来限制每个容器的最大进程数目。也就是说,进程数目在每个容器中也是有限的,是一种很宝贵的资源。(例如:Linux 机器上的进程总数目是有限制,如果进程数据过多,比如你想ssh登录到机器上就不行 )

如何使用 tini 初始化系統

tini 是一套更简单的 init 系统,专门用来执行一个子程序(spawn a single child),并等待子程序结束,即便子程序已经变成僵尸程序也能捕捉到,同时也能转送 Signal 给子程序。如果你使用docker来跑容器,可以非常简便的在docker run的时候用**–init参数,就会自动注入tini程式 (/sbin/docker-init**) 到容器中,并且自动取代ENTRYPOINT设定,让原本的程式直接跑在 tini程序底下。

dockerfile如下:

FROM nginxRUN export TINI_VERSION=0.9.0 && \export TINI_SHA=fa23d1e20732501c3bb8eeeca423c89ac80ed452 && \curl -fsSL https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-static -o /bin/tini && \echo 'Calculated checksum: '$(sha1sum /bin/tini) && \chmod +x /bin/tini && echo "$TINI_SHA  /bin/tini" | sha1sum -c ENTRYPOINT ["/bin/tini","--","/opt/nginx/docker-entrypoint.sh"]
ENTRYPOINT ["nginx", "-c"]
CMD ["/etc/nginx/nginx.conf"]

建议为了提高容器运行的可靠性,可以选择在打包镜像时加入tini的安装,并以tini作为启动入口。

Jenkins的官方镜像中使用Tini 的使用

首先,我们先简单聊聊Jenkins。当您运行Docker容器时,Docker会将它与系统的其他部分隔离开来。这种隔离发生在不同的级别(例如网络、文件系统、进程)。

但Tini并不真正关注网络或文件系统,所以让我们把注意力放在Tini的一个重要概念上:进程。

每个Docker容器都是一个PID命名空间,这意味着容器中的进程与主机上的其他进程是隔离的。PID命名空间是一棵树,从PID 1开始,通常称为init。

注意:当你运行一个Docker容器时,镜像的ENTRYPOINT就是你的根进程,即PID 1(如果你没有ENTRYPOINT,那么CMD就会作为根进程,你可能配置了一个shell脚本,或其他的可执行程序,容器的根进程具体是什么,完全取决于你的配置)。

与其他进程不同的是,PID 1有一个独特的职责,那就是收割“僵尸进程”。

那何为“僵尸进程”呢?

“僵尸进程”是指:

  • 已经退出。
  • 没有被其父进程wait(wait是指syscall父进程用于检索其子进程的退出代码)。
  • 父进程已丢失(也就是说,它们的父进程已经不存在了),这意味着他们永远不会被其父进程处理。

当“僵尸进程”被创建时(也就是说,一旦它的父进程非正常退出了,它也就跟着无法正常退出了),它会继承成为PID 1的子级,最后PID 1会负责关闭它。

换句话说,有人必须在“不负责任”的父进程离开后,对这些“孤儿”进行清理,这是PID 1的作用。

请注意,创建“僵尸进程”通常是不被允许的(也就是说,理想情况下,您应该修复代码,这样就不会创建“僵尸进程”),但是对于像Jenkins这种应用来说,它们是不可避免的:因为Jenkins通常运行的代码不是由Jenkins维护者编写的(也就是您的Jenkins构建脚本),所以他们也无法“修复代码”。

这就是Jenkins使用Tini的原因:在构建了创建“僵尸进程”的脚本后进行清理。


但其实Bash实际上也做同样的事情(收割“僵尸进程”),所以你可能会想:为什么不把Bash当作PID 1呢?

第一个问题是,如果您将Bash作为PID 1运行,那么您发送到Docker容器的所有信号(例如,使用docker stop或docker kill)最终都会发送到Bash,Bash默认不会将它们转发到任何地方(除非您自己编写代码实现)。换句话说,如果你使用Bash来运行Jenkins,那么当你运行docker stop的时候,Jenkins将永远收不到停止信号!

而Tini通过“信号转发”解决了这个问题:如果你向Tini发送信号,那么它也会向你的子进程发送同样的信号(在你的例子中是Jenkins)。

第二个问题是,一旦您的进程退出,Bash也会继续退出。如果您不小心,Bash可能会退出,退出代码为0,而您的进程实际上崩溃了(但0表示“一切正常”;这将导致Docker重启策略不符合您的预期)。因为您真正想要的可能是Bash返回与您的进程相同的退出代码。

请注意,您可以通过在Bash中创建信号处理程序来实际执行转发,并返回适当的退出代码来解决这个问题。另一方面,这需要做更多的工作,而添加Tini只是文档中的几行。


其实还有另一个解决方案可以将Jenkins作为PID 1运行,即在Jenkins中添加另一个线程来负责收割“僵尸进程”。

但这也不理想,原因有二:

首先,如果将Jenkins以PID 1的身份运行,那么很难区分继承给Jenkins的进程(应该被收割)和Jenkins自己产生的进程(不应该被收割,因为还有其他代码已经在等待它们执行)。我相信你可以用代码来解决这个问题,但还是要问一遍:当你可以把Tini放进去的时候,为什么还要写呢?

其次,如果Jenkins以PID 1运行,那么它可能不会接收到您发送的信号!

这是PID 1进程中的微妙之处。与其他进程不同的是,PID 1没有默认的信号处理程序,这意味着如果Jenkins没有明确地为SIGTERM安装信号处理程序,那么该信号在发送时将被丢弃(而默认行为是终止该过程)。

Tini确实安装了显式信号处理程序(顺便说一下,是为了转发信号),所以这些信号不再被丢弃。相反,它们被发送到Jenkins,Jenkins并不像PID 1(Tini )那样运行,因此有默认的信号处理程序(注意:这不是Jenkins使用Tini的原因,Jenkins使用它来获取信号,但在RabbitMQ的镜像中是这个作用)。


请注意,Tini中还有一些额外的功能,在Bash或Java中很难实现(例如,Tini可以注册为“子收割者”,因此它实际上不需要作为PID 1运行来完成“僵尸进程”收割工作),但是这些功能对于一些高级应用场景来说非常有用。

希望以上内容对你有所帮助!

如果您有兴趣了解更多,以下是一些可供参考的资料:

  • 僵尸进程详解:https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/
  • 更简洁的解释:https://github.com/docker-library/official-images#init

最后,请注意Tini还有更多的选择(比如Phusion的基础镜像)。

Tini的主要特性是:

  • 做PID 1需要做的一切,而不做其他任何事情。像读取环境文件、改变用户、过程监控等事情不在Tini的范围内(还有其他更好的工具);
  • 零配置就能上手(如果运行不正常,Tini >= 0.6也会警告您);
  • 它有丰富的测试。

docker 容器如何使用tini 作为启动进程并 清理、管理 Docker 容器僵死进程相关推荐

  1. docker mysql 蜂巢_在网易蜂巢中创建和管理Docker容器的教程

    创建容器点击左侧的导航菜单「容器管理」,进入容器管理列表页,通过点击容器列表左上角的「创建容器」按钮可进入创建容器页面,如下图所示: 其中带 * 为必填项. 选择镜像可选择的镜像分为「我的镜像」和「官 ...

  2. docker常用命令 | game2048网页游戏 | nginx镜像 | 数据卷管理 | docker网络管理 | 构建apache镜像 | Dockerfile常用的命令

    DOCKER https://www.docker.com http://www.runoob.com/docker/docker-command-manual.html docker命令大全 镜像用 ...

  3. linux进程与服务管理,linux基础之进程管理与服务

    进程的状态 进程被称作任务,有4种状态 运行态 运行或准备运行 等待态 可中断(TASK_ITERRUPTIBLE) 不可中断(TASK_UNITERRUPTIBLE) 停止态(TASK_STOPPE ...

  4. linux进程的高级管理,sched_yield()函数 高级进程管理

    1.让出处理器 Linux提供一个系统调用运行进程主动让出执行权:sched_yield.进程运行的好好的,为什么需要这个函数呢?有一种情况是用户空间线程的锁定.如果一个线程试图取得另一个线程所持有的 ...

  5. oracle中tnslsnr进程,查找处理oracle数据库中僵死进程

    查找数据库中僵死进程 Col terminal format a20 select pid, spid, username, terminal, program from v$process wher ...

  6. mysql 查看僵死的进程_如何查看并杀死僵死进程

    这些进程已经死亡,但没有释放系统资源,包括内存和一些一些系统表等,如果这样的进程很多,会引发系统问题.用ps -el看出的进程状态如果是Z,就是僵尸进程. ps -ef|grep defunc可以找出 ...

  7. oracle 僵死的进程,【原】杀掉oracle僵死进程

    作者:david_zhang@sh [转载时请以超链接形式标明文章] 链接:http://www.cnblogs.com/david-zhang-index/archive/2012/03/01/23 ...

  8. linux服务与进程管理sup,linux下进程管理工具-supervisord

    一 简介 supervisord是linux下的一个优秀的进程管理工具,通过supervisord可以方便管理和应用linux系统下服务进程过多的问题,其支持服务异常退出自动重启,通过浏览器管理控制相 ...

  9. 清除WAS的僵死进程

    "百足之虫,至死不僵,以扶之者众也."                               ----三国·魏·曹冏<六代论> 一.背景:僵死进程让人心烦意乱   ...

最新文章

  1. 深入理解cookie和session
  2. 图解JAVA参数传递
  3. CentOS 5安装mplayer
  4. 用迭代法求方程cos(y)-y=0的一个实根
  5. atitit。自定义uml MOF EMF体系eclipse emf 教程o7t
  6. 将CCT色温转换成RGB
  7. springboot Vue java学生宿舍报修管理系统源码介绍
  8. html整体布局居中,div整体位置居中
  9. 【信号隐藏】基于lsb算法实现音频水印嵌入提取matlab代码
  10. 【多轮对话】多轮对话状态追踪技术综述
  11. 最新高品质+武汉城区建筑物范围面shp格式+小区大厦学校医院占地面积
  12. 数字电路28(设计步骤及十进制加计数器设计)
  13. 分享28个前端优秀项目源码(React+Vue+Node)
  14. 读透《阿里巴巴数据中台实践》,其到底有什么高明之处?
  15. 【HDU 1512】Monkey King
  16. 5g时代php发展趋势,5g时代还有多久到来
  17. 国密算法SM2 密钥对的生成
  18. 马云回国后,四年前的事情又被翻出来了
  19. 开源神器:可快速在 iOS 设备上安装 Windows、Linux 等操作系统!
  20. destoon首页底部加产品分类拼音索引

热门文章

  1. 初次发现 ondblclick( ) 方法
  2. 统信UOS系统之间文件共享
  3. 计算机体系结构--Tomasulo算法
  4. java a201游戏_OPPO A201固件再升级 内置官方QQ2010
  5. GRN: Generative Rerank Network for Context-wise Recommendation
  6. Python简易web静态服务器程序搭建
  7. 习题6-3 二叉树重建(Tree Recovery,ULM 1997,UVa 536)
  8. 吉比特笔试编程题 - 最好一样
  9. 南京某机关智能配电项目
  10. 阿博茨科技宣布完成3000万美元B轮融资 1