解决 Docker + selenium + chromedriver + chrome 会出现僵尸进程的问题

一、僵尸进程问题

在docker里,使用selenium爬虫,  webdriver quit后,会产生很多僵尸进程。
docker run  - it  - v / home / blackip :/ home / blackips /   selenium : 1.0   python3 linux_black_ip . py
top查看僵尸进程:
ps -ef | grep defunct查看僵尸进程:
僵尸进程的父进程是python3。
看了下chrome运行时的状况,发现开始的父进程并不是1,但到最后都变成了1,并且变为defunct状态:
ps -ef | grep chrome| grep -v defunct
超多的僵尸进程会耗尽 pid 表,导致 Chrome failed to start: exited abnormally.
snapshot-consumer | selenium.common.exceptions.WebDriverException: Message: unknown error: Chrome failed to start: exited abnormally.
snapshot-consumer | (unknown error: DevToolsActivePort file doesn't exist)
snapshot-consumer | (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
snapshot-consumer | Stacktrace:

二、原因分析

在调查了进程树之后,终于发现:chromedriver fork出了多个子进程和一个孙进程,而 调用quit后,chrome driver正常退出,而它的子孙们变成了孤儿进程 ,被托管给了docker的1号进程,也就是docker的启动进程。这些孤儿进程退出后,根据孤儿进程托管的优先级,最终由1号进程托管,而 1号进程是业务进程, 没有能力处理子进程退出的信号 (wait/waitpid),这些孤儿进程就变成了僵尸进程
   
对于Linux 来说, pid 为 1 的进程,有着特殊的使命
  1. 传递退出信号,确保子进程正确退出
  2. wait子进程退出,回收僵尸进程

三、解决办法

3.1、脚本内部加入信号处理的方法

在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN,这样,内核在子进程结束时不会产生僵尸进程。python 可以调用singal包来处理。
 
signal包的核心是使用signal.signal()函数来预设(register)信号处理函数:
参照:
singnal.signal(signalnum, handler)
参数:
  • signalnum为某个信号,
  • handler为该信号的处理函数。
我们在信号基础里提到,进程可以无视信号,可以采取默认操作,还可以自定义操作。当handler为signal.SIG_IGN时,信号被无视(ignore)。当handler为singal.SIG_DFL,进程采取默认操作(default)。当handler为一个函数名时,进程采取函数中定义的操作
​
# python3
# 开头加入如下代码,告诉此进程 SIGCLD的信号用SIG_IGN方法处理
import signal
signal.signal(signal.SIGCLD, signal.SIG_IGN)

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options

chrome_options = webdriver.ChromeOptions()  # 或者 chrome_options = Options()
chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])  # 防止反爬
chrome_options.add_argument('--disable-blink-features=AutomationControlled')  # 避免webdriver检测
chrome_options.add_argument("--remote-debugging-port=9222") # 解决报错 WebDriverException: Message: unknown error: DevToolsActivePort file doesn't exist
chrome_options.add_argument('--no-sandbox')  # 直接把sandbox禁用了,–-no-sandbox参数是让Chrome在root权限下跑
chrome_options.add_argument('--disable-dev-shm-usage') # 大量渲染时候写入/tmp而非/dev/shm
chrome_options.add_argument('--headless')  # 无头模式,传递此参数浏览器不会显示界面,程序在后台运行
hrome_options.add_argument('--disable-gpu')  # 禁用GPU加速
chrome_options.add_argument("user-agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'") # 模拟浏览器
# chrome_options.add_argument('--proxy-server=http://127.0.0.1:10809')  # 利用v2ray配置代理

driver = webdriver.Chrome(options=chrome_options, service=Service('/usr/local/bin/chromedriver'))
# driver.set_page_load_timeout(300)
driver.maximize_window()  # 仅仅适配到当前显示屏大小,数据偶尔还是不完整,需要下滑鼠标

try:
    driver.get("https://www.163.com")
  
except Exception as e:
    print(e)
else:
    content = driver.page_source.encode('utf-8') # 获取页面响应数据
    print content
finally:
    driver.quit()
​
缺点:需要修改每个相关脚本。

3.2、dumb_init

参照
制作Dockerfile时可加上此程序,参考docker 中 browserless/chrome 的制作
# It's a good idea to use dumb-init to help prevent zombie chrome processes.
ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 /usr/local/bin/dumb-init
RUN chmod +x /usr/local/bin/dumb-init
然后,要在容器中使用 dumb-init,可以直接安deb 包,或者从 源码构建。容器启动时,使用 dumb-init 作为初始进程,确保所有子进程都由 dumb-init 进程创建:
docker run my_container dumb-init python -c 'while True: pass' 
结果:1号进程是dumb-init
缺点:需要先安装dumb -init才能在docker run时使用dumb -init。

3.3、docker运行时加入--init参数

在docker运行时加入--init参数,这样 docker内1号进程就是docker-init进程,业务进程则是其子进程
docker-init进程会将收到的信号传递给其子进程,并且会处理僵尸进程。
docker run  -it  --init  -v /home/blackip:/home/blackips/ selenium:1.0  python3 linux_black_ip.py
结果:
可以看到1号进程是/sbin/docker-init -- python3 linux_black_ip.py;top查看的僵尸进程zombie为0。

3.4、其他解决办法参考

如果直接使用 docker、docker-compose 就用第一种,如果是 k8s,就用第二种!

四、参考

docker,防止产生孤儿进程和僵尸进程 - 简书 (jianshu.com)

(6条消息) 爬虫服务(chromedp)僵尸进程排查记录_chromedp 僵尸进程_liyunlong41的博客-CSDN博客

dumb-init:一个Docker容器初始化系统_语言 & 开发_金灵杰_InfoQ精选文章

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Docker是一个开源的容器化平台,可以实现轻量级、可移植和自足的容器化应用程序的创建、部署和运行。通过使用Docker,可以将应用程序及其依赖项打包成一个容器,以简化应用程序的部署和管理过程。 Selenium是一个用于自动化Web浏览器操作的工具。它可以模拟用户在浏览器中的操作,如点击、输入文本、提交表单等。Selenium可以对Web应用程序进行自动化测试,并提供多种编程语言的支持。 Pytest是一个Python编程语言中的测试框架,用于编写和执行单元测试。它提供了丰富的断言库、执行测试的灵活控制,以及丰富的插件机制,使得测试编写和运行过程更加简单和高效。 结合使用DockerSelenium和Pytest可以实现自动化Web应用程序的测试和部署。通过将Selenium和Pytest集成在Docker容器中,可以创建一个独立的测试环境,无需在本地安装和配置相关的软件和依赖项。这样可以简化测试环境的搭建过程,并保持测试环境的一致性。 使用Docker容器中的Selenium和Pytest,可以编写自动化测试脚本,并在测试容器中运行。测试脚本可以使用Selenium模拟用户在Web应用程序中的操作,并使用Pytest进行断言和结果判断。通过使用Docker容器,测试可以在不同的环境中运行,确保测试的可移植性和一致性。 总结来说,DockerSelenium和Pytest联合使用可以实现自动化测试环境的快速搭建,提高测试效率和可移植性。这种集成可以帮助开发人员和测试人员更好地开发和测试Web应用程序,并简化部署和管理过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值