C/C++教程

爬虫服务(chromedp)僵尸进程排查记录

本文主要是介绍爬虫服务(chromedp)僵尸进程排查记录,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

发现现象

如何解决?

临时解决

根本上解决

修复过程


发现现象

爬虫服务会使用chromedp库(https://github.com/chromedp/chromedp)模拟浏览器登录,抓取网页数据,某天在pod内查看服务运行状态时,发现有大量的zombie进程,看了下是Chrome进程。

 

 

爬虫服务使用Chrome,应该是以创建子进程的方式来启动Chrome,如果在子进程销毁时没有wait或者waitpid来处理,那么子进程会成为zombie进程。

每个zombie进程会占用系统少量资源,造成资源泄露,最明显的是pid数量会逐渐变多,最终结果会造成无法启动新的进程。

如何解决?

临时解决

准备将所有的僵尸进程kill掉,但是僵尸进程很难被kill掉,kill -9也不行。

后考虑kill掉其父进程,然后所有的僵尸进程就可以挂到init进程上,init会清理僵尸进程,然后看了下发现容器内没有init进程,所有僵尸进程的父进程都是1,进程1对应的是爬虫服务,杀掉1进程会导致容器挂掉重启……

后面直接重启服务临时解决了。

 

根本上解决

考虑到僵尸进程的产生原因,首先想到的是对于chromedp库的使用有问题,可能没有正确的关闭浏览器,去看了下代码内的使用,以及官方示例的代码,发现没有问题,所有的资源都被关闭了……

然后去chromedp库的issue中查找相关的内容,可能会有人遇到相同的问题。

发现一个类似的open状态的issue:https://github.com/chromedp/chromedp/issues/752

总结了几个观点:

  • 关闭浏览器时,kill掉chrome进程,但是这样太暴力,可能会影响其他使用chrome的线程。
  • 一些Chrome在被启动的时候,会有一个wrapper,例如shell,shell会启动Chrome作为子进程,执行完后,shell退出了,Chrome成为孤儿进程,挂到pid1进程上,但是容器内pid为1的进程为业务进程,没有回收僵尸进程的能力。

看了下chrome运行时的状况,发现开始的父进程并不是1,但到最后都变成了1,并且变为defunct状态。

 

 

在上面的issue中,有人提供了一个pr,并且被merge了,所以考虑升级库版本来解决,但是发现升级没有解决……

考虑其他方案。

 

后来发现一种方案,在docker运行时加入--init参数,这样docker内1号进程就是docker-init进程,业务进程则是其子进程。

docker-init进程会将收到的信号传递给其子进程,并且会处理僵尸进程。

 

然后自己在本地实验了下,同样的使用chromedp库,发现拥有docker-init进程的容器内,没有产生僵尸进程,因为1号进程已经具备处理僵尸进程的能力。

而1号进程是业务进程的容器内,僵尸进程逐渐积累……

同时实验了下信号捕获,看业务进程能否捕获退出信号,实验也是ok的。

ps:

通过本地实验,发现bash作为1号进程、业务进程作为其父进程的情况下,也可以处理僵尸进程,但是却不能传递退出信号给业务进程。

所以对于Linux 来说,pid 为 1 的进程,有着特殊的使命:

  1. 传递信号,确保子进程完全退出
  2. 等待子进程退出

修复过程

本地测试时在docker run参数中加–init,经测试发现是ok的,但是服务是部署在k8s集群中,查了好久发现无法在deployment yaml文件中传递docker run的参数…

只能从镜像层面考虑解决。

首先打了一个有tini(docker-init实际使用的是tini)的镜像,然后修改Dockerfile:

 

最终上线完美解决!

僵尸进程已经消失不见。

参考:https://stackoverflow.com/questions/50803268/kubernetes-equivalent-of-docker-run-init

这篇关于爬虫服务(chromedp)僵尸进程排查记录的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!