Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker + Node + Pm2 + Redis + Puppeteer #20

Open
SunXinFei opened this issue Jun 13, 2019 · 6 comments
Open

Docker + Node + Pm2 + Redis + Puppeteer #20

SunXinFei opened this issue Jun 13, 2019 · 6 comments
Labels

Comments

@SunXinFei
Copy link
Owner

SunXinFei commented Jun 13, 2019

centOS7安装node+pm2+chrome步骤

安装node
sudo wget https://nodejs.org/dist/v10.16.0/node-v10.16.0-linux-x64.tar.xz
sudo xz -d node-v10.16.0-linux-x64.tar.xz 
sudo tar -xf node-v10.16.0-linux-x64.tar
命令指向
sudo ln -s  /usr/local/sin_mobile/node-v10.16.0-linux-x64/bin/node /usr/bin/node
sudo ln -s /usr/local/sin_mobile/node-v10.16.0-linux-x64/bin/npm /usr/bin/npm
安装pm2
sudo npm install -g pm2
sudo ln -s /usr/local/sin_mobile/node-v10.16.0-linux-x64/bin/pm2 /usr/local/bin/
下载chrome安装包
sudo wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
安装chrome安装包
sudo yum localinstall google-chrome-stable_current_x86_64.rpm或sudo rpm -ivh google-chrome-stable_current_x86_64.rpm

Selenium在MAC中的环境搭建

Selenium由于不再迭代,现在主流爬虫都使用的--Puppeteer
由于mac中自带python,那么我们可以避开python的安装。

  1. 运行sudo easy_install pip 安装pip;
  2. 运行sudo pip install selenium 安装selenium;
  3. 安装与运行机器的chrome浏览器版本号相同的chromedriver
    brew install chromedriver
    注意:如果运行该命令报错:则改为如下命令去执行:
brew tap caskroom/cask
brew cask install chromedriver

新建test.py,粘贴下面的内容;保存之后使用python test.py执行,即可得到运行结果与爬虫截图。

#-*-  coding:utf-8 -*-
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
#要想调用键盘按键操作需要引入keys包
from selenium.webdriver.common.keys import Keys
import time
chrome_options = Options()
chrome_options.add_argument('--headless')
driver = webdriver.Chrome(chrome_options=chrome_options)
driver.set_window_size(1366, 768)
driver.get("http://www.baidu.com")
# 获取页面名为wraper的id标签的文本内容
data = driver.find_element_by_id('wrapper').text
#打印数据内容
print(data)
print driver.title
#生成页面快照并保存
driver.save_screenshot("baidu.png")
#获取当前页面Cookie
print(driver.get_cookies())
#ctrl+a全选输入框内容
driver.find_element_by_id('kw').send_keys(Keys.CONTROL, 'a')
#ctrl+x剪切输入框内容
driver.find_element_by_id('kw').send_keys(Keys.CONTROL, 'x')

#输入框重新输入内容
driver.find_element_by_id('kw').send_keys(u'百度')

#模拟Enter回车键
driver.find_element_by_id('su').send_keys(Keys.RETURN)
time.sleep(5)

#清空输入框内容
# driver.find_element_by_id('kw').clear()

#生成新的页面快照
driver.save_screenshot('test.png')
#获取当前url
print(driver.current_url)
driver.close()
@SunXinFei SunXinFei changed the title Docker + Node 服务 Docker + Node + Pm2服务 Jun 18, 2019
@SunXinFei
Copy link
Owner Author

SunXinFei commented Jun 21, 2019

Docker部署方式

【issue】 上面全局安装这种会造成全局环境污染,这里我们使用docker部署相关服务。
Docker中一些概念这里就不再赘述了,重要概念提一下分别是镜像和容器, 镜像可以用远端下载的也可以自己生成,容器是运行起来的环境,可以从主机进入容器内部运行命令。

常用docker命令:
service docker restart #docker服务启动
service docker stop #docker服务停止
docker images #查看已有镜像
docker ps #查看活动中的容器
docker ps -a #查看所有容器
docker rm 容器id/容器名称 #移除容器,注:需要先stop
docker stop 容器id/容器名称
docker restart 容器id/容器名称
docker exec -it 容器id/容器名称 bash #进入容器内,执行命令
docker exec -it 容器id/容器名称 命令 # 不需进入容器,执行命令
docker logs 容器id/容器名称# 查看docker日志
docker run 参数 镜像名称 生成容器,-it进入容器内,--restart=always-v 主机路径: 容器路径 挂载路径到主机, -p 80:3200 主机80映射给容器内3200端口,--name crawler-node 容器名称,-d 后台运行。

安装docker-nginx并配置

  • 首先查看有无镜像,没有就下载nginx镜像
docker images 
docker search nginx 
docker pull docker.io/nginx
  • 然后我们运行一个容器,目的是把里面的基础配置文件拷贝一份出来
docker run -it --restart=always --name crawler-nginx-test -p 8081:80 -d docker.io/nginx
mkdir -p /data0/nginx/www /data0/nginx/logs /data0/nginx/conf  /data0/nginx/conf.d
docker cp crawler-nginx-test:/etc/nginx/nginx.conf /data0/nginx/conf
docker cp crawler-nginx-test:/etc/nginx/conf.d /data0/nginx
  • 这样我们就会得到一个基础配置在主机上,我们可以stop crawler-nginx-test 这个镜像,然后rm掉,我们开始生成真正的要是使用的镜像
docker run -d -p 80:80 --name crawler-nginx -v /data0/nginx/www:/usr/share/nginx/html -v /data0/nginx/conf.d:/etc/nginx/conf.d -v /data0/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v /data0/nginx/logs:/var/log/nginx docker.io/nginx
  • 上面的操作使得我们得到监听80端口的nginx容器,log和配置文件都已经挂载到了主机的相应位置,下面我们添加nginx配置,用来做业务端口转发
# 在主机/data0/nginx/conf.d目录下有一个默认的default.conf,再建一个crawler.com.conf,内容如下
server{
    listen 80;
    server_name crawler.com;
    location / {
        proxy_pass http://宿主主机ip:3200;   //<=注意这里是宿主的主机ip,不是localhost或者127.0.0.1,除非docker是`network_mode: host`
    }
}
  • 最后重启docker容器即可生效
    以上步骤实现了,docker-nginx部署,docker监听80端口转发给crawler-nginx容器,crawler-nginx容器内部做server转发,转发给主机的3200端口,下面我们开始配置监听3200端口的node服务
    如果docker容器用了network_mode: host配置,那么可以写为proxy_pass http://127.0.0.1:3200
  • 首先我们要清楚一点是docker镜像是可以依赖其他镜像,然后生成自己的镜像,别人也可以通过自己生成的镜像再生成新的镜像。我们将含有node+pm2的镜像,生成一个新的镜像,命名为crawler:v0.2,上传到远端。
  • 基于该镜像生成该项目crawler-node的容器,将项目目录挂载到主机,
docker run -it --restart=always -v /data0/www/crawler:/data0/vad/jucai/crawler.com  -p 3200:3200  --name crawler-node  -d 远端地址crawler:v0.2
  • 在node项目中添加ecosystem.config.js文件,目的是方便我们配置pm2的一些监测等配置,文件大致配置如下:
module.exports = {
  apps : [{
    name: 'crawler',
    script: '/data0/vad/jucai/crawler.com/app.js',
    cwd: '/data0/vad/jucai/crawler.com',
    instances: 1,
    autorestart: true,
    watch: ['routes','controller','app.js'],
    ignore_watch : ["node_modules"],
    max_memory_restart: '1G',
    watch_delay: 1000,
    env: {
      NODE_ENV: 'development'
    },
    env_production: {
      NODE_ENV: 'production'
    }
  }]
};
  • 使crawler-node容器内pm2启动应用的命令如下,这样pm2将监测相关代码的变化,可以自动重启应用
docker exec -it crawler-node  pm2 start /data0/路径/ecosystem.config.js

@SunXinFei
Copy link
Owner Author

SunXinFei commented Jun 21, 2019

Dockerfile

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

FROM <基础镜像名称>

基于某一个基础镜像进行拉取

COPY <源路径1>... <目标路径>

复制指令,从上下文目录中复制文件或者目录到容器里指定路径
<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

RUN <命令行命令>

用于执行后面跟着的命令行命令,用于构建镜像阶段
注意:以 && 符号连接命令,这样执行后,只会创建 1 层镜像,比不加&&符号体积要小很多。

CMD ["<可执行文件或命令>","","",...]

类似于 RUN 指令,用于运行程序,但二者运行的时间点不同: 1. CMD 在docker run 时运行。2. RUN 是在 docker build。
注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

用Dockerfile构建镜像

进入Dockerfile所在文件夹,运行docker build -t 新镜像名称 .
注意:最后的 . 代表本次执行的上下文路径,默认上下文路径就是 Dockerfile 所在的位置

docker-compose.yml

Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

其实主要就是避免了过于复杂的docker run命令参数,且可以依次运行出多个容器。

version: '3'
services:
    xxx_nginx:
        container_name: xxx_nginx
        network_mode: host
        image: 镜像名称/地址
        volumes:
            -  宿主机地址:容器内地址

用compose创建容器

docker-compose up -d
-d参数表示后台运行

@SunXinFei
Copy link
Owner Author

SunXinFei commented Aug 7, 2019

Docker 空间使用分析与清理

【issue】 docker的空间由于一段时间运行发现占满了磁盘,故作出清理

# 查看整体磁盘占用量
[root@localhost ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda6       7.8G  7.6G     0 100% /var
# 也可根据使用的存储驱动的不同,相应目录会有所不同:
[root@node3 docker]# du -h --max-depth=1 |sort
7.5G   ./overlay2   # 这个目录占用了非常高的磁盘磁盘空间
#进入overlay2目录,查看具体子文件夹体积
du -sh *
[root@localhost overlay2]# du -sh *
1.1G	043895ffff36d99286b393c24b69d9936eb73c443c530d83bd5f9bf68f3968f2
684M	067f907d8d0e9986fe5c6a4b1d2de9120e9cb5b333a0f98219b9cfbb0d95e891
44K	067f907d8d0e9986fe5c6a4b1d2de9120e9cb5b333a0f98219b9cfbb0d95e891-init
560M	29ca2111560fa21ded9721a48edaf0664c7796a4e54a651a1127141e5ff2ebb3
396M	4624731b9981661378c4e519e5628e3de5b224e37765967f5e6ee3d37cdfe3e0
111M	509e61fd254b761bd57de30a2c315b8c9e73c2d5fc972049f43634144209678d
162M	6560f3a2dbdc455a01e42517cda4dbd620a2233f4a69f0bdc96ac6f4195a9403
25M	6be5989c62ecfefc9d5f3da75fb5260c05630f5a82bb54881eaee8b16fb74475
166M	76fef89179df7f1b77716922494719718314cc7f5787d5545e813027d9162253
44K	76fef89179df7f1b77716922494719718314cc7f5787d5545e813027d9162253-init
208M	7de7fd55dfcdcd256d35add4c8d9ad6a7c49807aa3f9359f94fb6e3da9f0ca1d
150M	8157ff3137fa6fd0b675d0a09b16b5bdd0c526c6a813fd1c1cedfbaf82c98a0b
36K	8ec926ca8d01cf1a1f5e434a720b69d99359cfb18419adda29ccd2f9e8cfad01
480M	a318592d773dd79e7c6661455b574b6a168c4a9f536a9d32b7594474539eb10b
44K	a318592d773dd79e7c6661455b574b6a168c4a9f536a9d32b7594474539eb10b-init
2.6G	aa2fef252df88e4ae0723bcc5280a75865625824b029c813da2f91614c4e479f
44K	aa2fef252df88e4ae0723bcc5280a75865625824b029c813da2f91614c4e479f-init
62M	aab3fd31db16cb204fd77297c4126842a38631808da6f1c8aa4aec571ca7f7e3
59M	acb6bd732166f7c6d155b7a395e1c7699e6ecbba0c0f6da520d7e3641f46de76
36K	c2c6ca753469e4aa604dd3974836c471b835996399a542718cab97a24f8f22f2
8.2M	c944d3c8803f09695833aa77fe265eafc476bdbf092e4b130b2bbb5bb691b8ed
54M	cdfa3aefc204b33a0d301fd1c11f230ab41dd4f6826e9261c28a060c018a87ba
213M	f1e3485ead1b6cc005e0e0e21676c6a755a3d8b08c4a2460988b328adec841a8
72K	f3ecc3546fa8774acad5d6ef95334fbfe5f5b2da08057fa15fdce681b8f26d4c
44K	f3ecc3546fa8774acad5d6ef95334fbfe5f5b2da08057fa15fdce681b8f26d4c-init
104K	l

#进行镜像详细分析
[root@localhost overlay2]# docker system df -v
Images space usage:

REPOSITORY                               TAG                 IMAGE ID            CREATED             SIZE                SHARED SIZE         UNIQUE SIZE         CONTAINERS
asdasdasdsdasdsaddasds/dsdsdsd/crawler   v0.2                f72977c649ea        7 weeks ago         717.6 MB            0 B                 717.6 MB            1
docker.io/nginx                          latest              719cd2e3ed04        8 weeks ago         109.3 MB            0 B                 109.3 MB            1
nginx/php56                              v0.1                8cd464ac166f        3 months ago        1.235 GB            196.6 MB            1.039 GB            0
docker.io/centos                         centos7.4.1708      9f266d35e02c        4 months ago        196.6 MB            196.6 MB            0 B                 0
Containers space usage:

CONTAINER ID        IMAGE                                         COMMAND                  LOCAL VOLUMES       SIZE                CREATED             STATUS                            NAMES
69b30a75ff6d        asdasdasdsdasdsaddasds/dsdsdsd/crawler:v0.2   "sdsd"                   0                   2.8 GB              6 weeks ago         Up About a minute                 crawler-node
a8a3352218db        docker.io/nginx                               "sdsdsdsdsdsdsdsdsdsd"   0                   2 B                 7 weeks ago         Exited (255) About a minute ago   crawler-nginx

Local Volumes space usage:

VOLUME NAME         LINKS               SIZE
[root@localhost overlay2]# docker ps -a
CONTAINER ID        IMAGE                                         COMMAND                  CREATED             STATUS                       PORTS                    NAMES
69b30a75ff6d        asdasdasdsdasdsaddasds/dsdsdsd/crawler:v0.2   "sdsd"                   6 weeks ago         Up 2 minutes                 0.0.0.0:3200->3200/tcp   crawler-node
a8a3352218db        docker.io/nginx                               "sdsdsdsdsdsdsdsdsds."   7 weeks ago         Exited (255) 2 minutes ago   0.0.0.0:80->80/tcp       crawler-nginx

docker system prune

docker system prune 自动清理说明:

该指令默认会清除所有如下资源:

  • 已停止的容器(container)
  • 未被任何容器所使用的卷(volume)
  • 未被任何容器所关联的网络(network)
  • 所有悬空镜像(image)。

该指令默认只会清除悬空镜像,未被使用的镜像不会被删除。
添加 -a 或 --all 参数后,可以一并清除所有未使用的镜像和悬空镜像。
可以添加 -f 或 --force 参数用以忽略相关告警确认信息。
指令结尾处会显示总计清理释放的空间大小。

[root@localhost logs]# docker system prune
WARNING! This will remove:
	- all stopped containers
	- all volumes not used by at least one container
	- all networks not used by at least one container
	- all dangling images
Are you sure you want to continue? [y/N] y
Deleted Containers:
6e8dfad222ddfb66ac7dfc7a4f6eb86d902c66e94a1a0740462845fd5be76e31
a8a3352218dbf65b611a62658f7cd64655f5acb3393dbc1a138e7aa7f4d4c658
69b0e7b00f3cf01d1855aded7e38c7753623a5e5f4486f38245c8f497e1cb10e
2b31d6e54bbbaf6f2784f15a392517598bc7ba2763c96b7a1168c94a6654f54f

Total reclaimed space: 1.332 GB

Tips :

不同状态的镜像

  • 已使用镜像(used image): 指所有已被容器(包括已停止的)关联的镜像。即 docker ps -a 看到的所有容器使用的镜像。
  • 未引用镜像(unreferenced image):没有被分配或使用在容器中的镜像,但它有 Tag 信息。
  • 悬空镜像(dangling image):未配置任何 Tag (也就无法被引用)的镜像,所以悬空。这通常是由于镜像 build 的时候没有指定 -t 参数配置 Tag 导致的。
  • 悬空镜像(dangling image)
  • 挂起的卷(dangling Volume)

类似的,dangling=true 的 Volume 表示没有被任何容器引用的卷。

总体瘦身后的docker体积详情

Images space usage:

REPOSITORY                               TAG                 IMAGE ID            CREATED             SIZE                SHARED SIZE         UNIQUE SIZE         CONTAINERS
asdasdasdsdasdsaddasds/dsdsdsd/crawler   v0.2                f72977c649ea        7 weeks ago         717.6 MB            0 B                 717.6 MB            1
docker.io/nginx                          latest              719cd2e3ed04        8 weeks ago         109.3 MB            0 B                 109.3 MB            0

Containers space usage:

CONTAINER ID        IMAGE                                         COMMAND             LOCAL VOLUMES       SIZE                CREATED             STATUS              NAMES
69b30a75ff6d        asdasdasdsdasdsaddasds/dsdsdsd/crawler:v0.2   "bash"              0                   54 MB               6 weeks ago         Up 2 hours          crawler-node

Local Volumes space usage:

VOLUME NAME         LINKS               SIZE

参考文档:
Docker 空间使用分析与清理
进程管理器pm2运维小结

@SunXinFei SunXinFei changed the title Docker + Node + Pm2服务 Docker + Node + Pm2 + redis Aug 9, 2019
@SunXinFei SunXinFei changed the title Docker + Node + Pm2 + redis Docker + Node + Pm2 + Redis Aug 9, 2019
@SunXinFei
Copy link
Owner Author

SunXinFei commented Aug 9, 2019

Nodejs + Redis

【issue】 项目出现被调用者频繁地调用,为了有效控制执行的速度,将接口修改为调用者调用后,将参数存储到队列中,node服务定时去获取队列,执行完成之后去消耗队列,这里可以是消耗kafka或者存放redis,kafka相对太重,故使用了后者

redis

通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

Redis 优势
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

Redis与其他key-value存储有什么不同?
Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,应为数据量不能大于硬件内存。在内存数据库方面的另一个优点是, 相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。 同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。

mac中Redis环境搭建(本地调试使用)

  • redis官网下载压缩包并解压
  • 进入该解压的redis文件夹
  • 编译测试 sudo make test
  • 编译安装 sudo make install
  • 服务启动 redis-server
    image

使用redis-cli

请开启终端,进入redis文件夹,输入redis-cli
redis-cli中常用命令如下:

命令 描述
-h 域名 -p 端口号 链接远程redis服务
GET key 获取 key 的值
EXITS key 查看此 key 是否存在
KEYS * 查看所有的 key (线上不允许使用)
FLUSHALLl 消除所有的 key
TTL key 查看该key过期时间
DEL key 删除该key
列表 一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
LLEN key 查看key下的列表长度;key不存在则为0,如果列表为空则会被redis删除该key
LRANGE key start stop 获取列表指定范围内的元素;start=0, end = -1 表示查看列表从开始到结束的值,start=0, end = 0 表示查看列表第一个元素
RPUSH key value1 [value2] 在列表中添加一个或多个值
LPOP key 移出并获取列表的第一个元素;列表为空则返回null
集合 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
SADD key member1 [member2] 向集合添加一个或多个成员;集合不允许重复key,重复添加则返回0
SMEMBERS key 查看key下的集合所有的值
SREM key member1 [member2] 移除集合中一个或多个成员

node中的使用redis

  1. 安装ioredis;redis client的node包有很多,这里放弃使用redis的原因是,get和set其实都是异步调用,ioredis直接就可以使用await/async,而redis虽然支持二次封装,但是略微麻烦了一点点。
  2. node中的redis 是直接支持redis的方法的,所以直接看Redis命令教程,找到符合自己需要并且复杂度不高的方法即可
  3. 在node工程的config中创建redis.js,如下
const Redis = require('ioredis')
const options = {
  keyPrefix: 'demo-', //存诸前缀
  db: 0
}
const newRedis = new Redis(6379, '127.0.0.1', options);
module.exports = newRedis
const redis = require("../config/redis") // redis配置文件路径
let timeId = null;


async function intervalGetRedis() {
  let targetUrl = await redis.lpop('app-urls-list');
  if (targetUrl) {//如果不为null
    clearInterval(timeId);
    timeId = null;
    //同步app-urls-set-collect
    await redis.srem('app-urls-set-collect', targetUrl);
    //获取app
    getAppAttr(targetUrl);
  } else {
    if(!timeId){
      timeId = setInterval(async () => {
        console.log('interval');
        intervalGetRedis();
      }, 1000 * 60 * 20);//20分钟 1000 * 60 * 20
    }
  }
}

intervalGetRedis();

 //判读length长度
    let redisArrLen = await redis.llen('app-urls-list');
    if (redisArrLen > 100000) { //大于10w则清除key
      await redis.del('app-urls-list');
      await redis.del('app-urls-set-collect');
    }
    //sadd如果Set集合已存在该元素则为0
    let addRedis = await redis.sadd('app-urls-set-collect', url);
    if (addRedis !== 0) {//url在队列中不存在
      await redis.rpush('app-urls-list', url);
    }

@SunXinFei
Copy link
Owner Author

SunXinFei commented Aug 13, 2019

pm2日志清理

【issue】 在我们的node应用中,实际出现了pm2日志体积过大的情况,默认情况下pm2的日志是一个文件不会被拆分为多个,不方便清理,所以这里我们使用pm2-logrotate进行日志切割。
默认设置即可只需要执行配置这一句参数
pm2 set pm2-logrotate:retain 7

常用的pm2日志命令
pm2 logs  # 显示所有应用程序的日志
pm2 logs [app-name]  # 显示指定应用程序的日志
pm2 logs [--raw]   #Display all processes logs in streaming
pm2 flush              #Empty all log file
pm2 reloadLogs    #Reload all logs
实时监控:
pm2 monit //监控当前所有的进程
pm2 monit 0 //监控批评行编号为0的进程
pm2 monit server.js //监控名称为server.js的进程

@SunXinFei SunXinFei changed the title Docker + Node + Pm2 + Redis Docker + Node + Pm2 + Redis + Puppeteer Aug 13, 2019
@SunXinFei
Copy link
Owner Author

SunXinFei commented Aug 13, 2019

Puppeteer/chrome-remote-interface与Docker

【issue】 在node后台是放置在docker中执行的,调用之后,过了一段时间使用top命令发现了很多的僵尸进程chrome没有被回收
image

Puppeteer Troubleshooting

  1. 首先这个里面讲解了puppeteer在docker中安装过程以及运行中的常见问题,其中就包括运行进程崩溃的问题

docker中的/dev/shm默认64m,不足以运行chrome进程开启很多标签页,所以需要通过--shm-size=1gb去重新设置大小

这里要说明一下chrome-launcher的chromeFlags参数已经支持了--disable-dev-shm-usage去禁用,项目issue为puppeteer/puppeteer#1834

  1. 在node服务代码中,我们默认的try catch/finally 并不会捕获到page.on('error')的出错的事件,导致浏览器没有被关闭,这里根据puppeteer的issue,有人使用node的fork process进行管理回收未被关闭的chrome,不过更为大多数人认同的是使用dumb-init处理僵尸进程

我们为什么需要init系统

  1. 通常,当你启动 Docker 容器时,你正在执行的进程将变成 PID 1,给出容器的init系统。
    这里有两个常见的问题:
    在大多数情况下,信号不会被正确处理。
    Linux内核将特殊信号处理应用于作为 PID 1运行的进程。
    当进程发送一个普通的Linux系统信号时,内核会首先检查该进程已经注册的任何自定义处理器,否则回退到默认行为。
    但是,如果接收信号的过程为 PID 1,则内核将获得特殊处理,如果没有为信号注册处理程序。 换句话说,如果你的进程没有显式处理这些信号,则发送 SIGTERM 将无效。

  2. dumb-init 作为 PID 1运行,像一个简单的init系统。 它启动一个进程,然后将所有接收到的信号代理到一个根进程的根进程。
    由于你的实际进程不再是 PID 1,当它接收来自 dumb-init的信号时,将应用默认的信号处理程序。 如果你的流程死了,dumb-init 也会死掉,以便清理它的他可以能仍然保持不变的进程。

  3. 解决后的进程
    image

参考文档:
Puppeteer Troubleshooting
Zombie Process problem
instance.close() let zombie process
dumb-init
dumb-init:一个 Docker 容器初始化系统
深入理解docker信号机制以及dumb-init的使用
dumb-init, 用于Linux容器的最小初始化系统
docker- Specify an init process
How to use --init parameter in docker run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant