Open SunXinFei opened 5 years ago
【issue】 上面全局安装这种会造成全局环境污染,这里我们使用docker部署相关服务。 Docker中一些概念这里就不再赘述了,重要概念提一下分别是镜像和容器, 镜像可以用远端下载的也可以自己生成,容器是运行起来的环境,可以从主机进入容器内部运行命令。
常用docker命令:
service docker restart
#docker服务启动service docker stop
#docker服务停止docker images
#查看已有镜像docker ps
#查看活动中的容器docker ps -a
#查看所有容器docker rm 容器id/容器名称
#移除容器,注:需要先stopdocker 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 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
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
# 在主机/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`
}
}
network_mode: host
配置,那么可以写为proxypass http://127.0.0.1:3200docker run -it --restart=always -v /data0/www/crawler:/data0/vad/jucai/crawler.com -p 3200:3200 --name crawler-node -d 远端地址crawler:v0.2
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
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:
- 宿主机地址:容器内地址
docker-compose up -d
-d
参数表示后台运行
【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 自动清理说明:
该指令默认会清除所有如下资源:
该指令默认只会清除悬空镜像,未被使用的镜像不会被删除。 添加 -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 表示没有被任何容器引用的卷。
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运维小结
【issue】 项目出现被调用者频繁地调用,为了有效控制执行的速度,将接口修改为调用者调用后,将参数存储到队列中,node服务定时去获取队列,执行完成之后去消耗队列,这里可以是消耗kafka或者存放redis,kafka相对太重,故使用了后者
通常被称为数据结构服务器,因为值(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可以做很多内部复杂性很强的事情。 同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
请开启终端,进入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] | 移除集合中一个或多个成员 |
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); }
【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的进程
【issue】 在node后台是放置在docker中执行的,调用之后,过了一段时间使用top命令发现了很多的僵尸进程chrome没有被回收
docker中的
/dev/shm
默认64m,不足以运行chrome进程开启很多标签页,所以需要通过--shm-size=1gb
去重新设置大小
这里要说明一下chrome-launcher的chromeFlags参数已经支持了--disable-dev-shm-usage去禁用,项目issue为https://github.com/GoogleChrome/puppeteer/issues/1834
在node服务代码中,我们默认的try catch/finally 并不会捕获到page.on('error')的出错的事件,导致浏览器没有被关闭,这里根据puppeteer的issue,有人使用node的fork process进行管理回收未被关闭的chrome,不过更为大多数人认同的是使用dumb-init处理僵尸进程
通常,当你启动 Docker 容器时,你正在执行的进程将变成 PID 1,给出容器的init系统。 这里有两个常见的问题: 在大多数情况下,信号不会被正确处理。 Linux内核将特殊信号处理应用于作为 PID 1运行的进程。 当进程发送一个普通的Linux系统信号时,内核会首先检查该进程已经注册的任何自定义处理器,否则回退到默认行为。 但是,如果接收信号的过程为 PID 1,则内核将获得特殊处理,如果没有为信号注册处理程序。 换句话说,如果你的进程没有显式处理这些信号,则发送 SIGTERM 将无效。
dumb-init 作为 PID 1运行,像一个简单的init系统。 它启动一个进程,然后将所有接收到的信号代理到一个根进程的根进程。 由于你的实际进程不再是 PID 1,当它接收来自 dumb-init的信号时,将应用默认的信号处理程序。 如果你的流程死了,dumb-init 也会死掉,以便清理它的他可以能仍然保持不变的进程。
解决后的进程
参考文档: 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
centOS7安装node+pm2+chrome步骤
Selenium在MAC中的环境搭建
Selenium由于不再迭代,现在主流爬虫都使用的--Puppeteer 由于mac中自带python,那么我们可以避开python的安装。
sudo easy_install pip
安装pip;sudo pip install selenium
安装selenium;brew install chromedriver
; 注意:如果运行该命令报错:则改为如下命令去执行:新建test.py,粘贴下面的内容;保存之后使用
python test.py
执行,即可得到运行结果与爬虫截图。输入框重新输入内容
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()