supervisord、pm2 都属于用户态进程管理器,一般业务层用。容器化之后(docker/k8s),pm2/supervisord 一般就不用了,容器本身负责进程托管。
PM2(Process Manager 2)
PM2 是 Node.js 生态最主流的【生产环境进程守护 / 进程管理器】,开源、命令行工具,Linux/macOS/Windows 都能用;不光可以管 Node 服务,也能托管 Java、Python、Go、RPC 这类后台程序(比如你刚才的 outbound-rpc)npm
简单一句话: 直接
node app.js跑服务,一关终端程序就死、崩溃了不会自动重启;pm2 帮你把程序放到后台常驻、挂了自动拉起、统一管理、看日志、监控。
✅ 核心能力(你日常开发运维最常用)
- 崩溃自动重启:代码异常、OOM、被杀掉,pm2 自动把进程拉起来,保证服务尽量在线
- 后台守护(daemon):关掉 ssh 终端、退出会话,服务继续跑(这是最基础价值)
- 集群模式 cluster:利用多核 CPU,多实例 + 内置负载均衡(Node 服务高频使用)
- 0 停机重载 reload:更新代码平滑重启,不中断请求(和
restart不一样) - 日志收集:自动输出 stdout/stderr 日志,
pm2 logs直接看,支持日志分割 - 开机自启:
pm2 startup+pm2 save,服务器重启之后自动把所有托管服务拉起来 - 监控面板:
pm2 monit,终端里看每个进程的 CPU、内存
一句话核心差异
- PM2:Node.js 原生,主打 Node 服务;自带 Cluster 集群、0 停机 reload、内存超限自动重启、pm2 monit 监控面板,CLI 简单顺手PM2
- Supervisord:通用进程管理器,Python 写,不限语言(Java/Go/Python/RPC 都能管);没有内置集群、没有原生 0 秒平滑 reload;INI 配置文件,稳定保守,现在基本进入维护模式
supervisor管理进程,是通过fork/exec的方式将这些被管理的进程当作supervisor的子进程来启动,所以我们只需要将要管理进程的可执行文件的路径添加到supervisor的配置文件中就好了。此时被管理进程被视为supervisor的子进程,若该子进程异常中断,则父进程可以准确的获取子进程异常中断的信息,可以选择是否自己启动和报警。通过在配置文件中设置autostart=true,可以实现对异常中断的子进程的自动重启。基于Python语言开发,比如rabbitmq消费程序需要配合supervisor实现常驻进程
#!/bin/bash #开启消费进程 supervisorctl start bill_orders: #检测消费进程并开启生产者 procNumber=`ps -ef |grep -w "/var/www/project/bin/console consumer:rebill"|grep -v grep|wc -l` if [ $procNumber -le 0 ];then echo 'supervisorctl 启动rabbitmq消费进程失败!' else php /var/www/project/bin/console more:win:to:mq --ex_name=orders.call --route_key=zx.cdr --mq_config=rabbitmq fi #todo 检查消费完毕 #消费完毕后,关闭消费者 supervisorctl stop bill_orders:安装supervisor
#检查是否已经安装 supervisord --version #安装pip yum install -y epel-release yum install -y python-pip #pip安装supervisor pip install supervisor 在末尾加入 echo 'export PATH="/usr/local/bin:$PATH"' >> /etc/profile 保存修改后,使用source命令重新加载配置文件: source /etc/profile配置Supervisor
#创建supervisor所需目录 mkdir /etc/supervisor mkdir /etc/supervisor/supervisord.d #创建supervisor配置文件 echo_supervisord_conf > /etc/supervisor/supervisord.conf默认地已经有/etc/supervisor/supervisord.conf这么个配置文件。并且最后的自定义配置文件已经包括了。去掉;
[include] files = supervisord.d/*.ini file=/tmp/supervisor.sock 改成 file=/var/run/supervisor.sock serverurl=unix:///tmp/supervisor.sock 改成 serverurl=unix:///var/run/supervisor.sock, logfile=/tmp/supervisord.log 改成 logfile=/var/log/supervisor.log pidfile=/tmp/supervisord.pid 改成 pidfile=/var/run/supervisor.pid创建文件目录并授权
sudo chmod 777 /var/log sudo touch /var/run/supervisor.sock sudo chmod 777 /var/run/supervisor.sock常用应用让supervisor来管理它,放在/etc/supervisor/supervisord.d/目录下,以.ini作为扩展名
vim /etc/supervisor/supervisord.d/nginx.ini [program:nginx] command = /usr/sbin/nginx -g 'daemon off;' startsecs=0 autostart=true autorestart=true stdout_logfile=/var/log/nginx_sup.log stopasgroup=true killasgroup=true vim /etc/supervisor/supervisord.d/elasticsearch.ini [program:elasticsearch] command = su - elasticsearch -c "/data/product/service/elasticsearch-5.4.2/bin/elasticsearch -d" autostart = true autorestart = true stopwaitsecs=1 startsecs=1 user=elasticsearch stopasgroup = true stdout_logfile = /var/log/elasticsearch.log stderr_logfile = /var/log/redis/elasticsearcherr.log vim /etc/supervisor/supervisord.d/kafka.ini [program:kafka] command=/data/product/service/kafka_2.11-0.8.2.2/bin/kafka-server-start.sh /data/product/service/kafka_2.11-0.8.2.2/config/server.properties autostart=true autorestart=true stopwaitsecs=1 stopasgroup=true startsecs=50 user=root stderr_logfile=/var/log/kafkaerr.log stdout_logfile=/var/log/kafka.log vim /etc/supervisor/supervisord.d/mysql.ini [program:mysql] command=/usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/log/mysqld.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/lib/mysql/mysql.sock autorestart=unexpected stopwaitsecs=1 stopasgroup=true user=mysql stderr_logfile=/tmp/err.log std_logfile=/tmp/out.log vim /etc/supervisor/supervisord.d/redis.ini [program:redis] command=/usr/bin/redis-server autostart=true autorestart=true stopwaitsecs=1 startsecs=3 user=root stopasgroup=true stdout_logfile=/var/log/redis/redis.log stderr_logfile=/var/log/redis/redis.log vim /etc/supervisor/supervisord.d/zookeeper.ini [program:zookeeper] command=/data/product/service/zookeeper-3.4.10/bin/zkServer.sh start-foreground zoo-1.cfg autorestart=unexpected stopwaitsecs=1 stopasgroup=true stderr_logfile=/var/log/zookeeper.log std_logfile=/var/log/zookeepererr.log给自己需要的脚本程序编写一个子进程配置文件
#rabbitmq 10个常驻内存消费者 [program:consumer_cdr] command=php /data/www/bin/console app:consumer-mq-queue process_name=%(program_name)s_%(process_num)02d ; 多进程名称肯定不同,匹配多个 (default %(program_name)s) numprocs=10 ; 启动几个进程 (def 1) autostart=true ; 随着supervisord的启动而启动 (default: true) autorestart=unexpected ; 自动重启。 (default: unexpected) startsecs=1 ; 这个选项是子进程启动多少秒之后,此时状态如果是running,则我们认为启动成功了(def. 1) startretries=3 ; 启动失败时的最多重试次数 (default 3) [group:consumercdr] programs=consumer_cdr priority=999 ;数字越高,优先级越高supervisorctl命令
Supervisor 是一个 C/S 模型的程序,supervisord是 server 端,supervisorctl是 client 端。Supervisor 同时还提供了另外一种进程组的管理方式,通过这种方式,可以使用 supervisorctl 命令来管理一组进程
基本命令(-C 可省略)
supervisord -c /etc/supervisor/supervisord.conf 通过配置文件启动supervisor sudo supervisorctl status 查看状态 supervisorctl -c /etc/supervisor/supervisord.conf reload 重新载入配置文件 supervisorctl start [all]|[x] 启动所有/指定的程序进程 supervisorctl stop [all]|[x] 关闭所有/指定的程序进程重启服务:载入最新的配置文件并启动所有进程(新+老)
supervisorctl reload更新新的配置到supervisord,不改变supervisorctl原有的进程状态,新加入的配置文件进程开启
supervisorctl update配置
[group:thegroupname] programs=progname1,progname2 ; each refers to 'x' in [program:x] definitions priority=999 ; 数字越高,优先级越高(default 999)启动/关闭
以后执行supervisorctl stop thegroupname:就能同时结束 progname1 和 progname2,执行 supervisorctl stop thegroupname:progname1 就能结束 progname1。
supervisorctl stop thegroupname: supervisorctl start zhoujy开启web界面操作
[inet_http_server] ; inet (TCP) server disabled by default port=127.0.0.1:9001 ; (ip_address:port specifier, *:port for all iface) username=user ; (default is no username (open server)) password=123 ; (default is no password (open server))通过http://localhost:9001访问如下图
运行状态,RUNNING表示运行中FATAL表示运行失败STARTING表示正在启动STOPED表示任务已停止
1、日志权限错误
IOError: [Errno 13] Permission denied: '/var/log/supervisor/supervisord.log'原因,/var/log/supervisor/supervisord.log没有写权限,赋予权限即可:
sudo chmod -R 777 /var/log/supervisor/supervisord.log2、开启HTTP Server错误
Error: Cannot open an HTTP server: socket.error reported errno.EACCES (13)配置文件中 /var/run 文件夹,没有授予启动 supervisord 的相应用户的写权限。/var/run 文件夹实际上是链接到 /run,因此我们修改 /run 的权限。
sudo chmod 777 /run一般情况下,我们可以用 root 用户启动 supervisord 进程,然后在其所管理的进程中,再具体指定需要以那个用户启动这些进程。3、日志权限问题
'INFO spawnerr: unknown error making dispatchers for 'app_name': EACCES'修改日志文件的权限
sudo chmod 777 /usr/log/supervisor/youAppName.log启动某应用的时候,发现supervisord会报错「spawn error」,开始以为是进程问题
supervisor: couldn't setuid to 0: Can't drop privilege as nonroot user supervisor: child process was not spawned所以,将配置文件/etc/supervisor/supervisord.conf 的user改为root
[supervisord] user=root如果不安装Supervisor,可以借助Linux系统crontab实现类似功能bash monitor.sh rabbitmq-server
#!/bin/sh process_name=$1 #process_name=$(echo $process_path | awk -F / '{print $NF}') script_name=monitor.sh check_process() { process_exists=$(ps -ef | grep $process_name | grep -v grep|grep -v $script_name | wc -l) echo "进程数量:" $process_exists if [ $process_exists -eq 0 ];then return 0 else return 1 fi } startMonitor() { check_process if [ $? -eq 0 ]; then echo "进程未启动,现在开始启动!" #sudo systemctl start $process_name #centos sudo service $process_name start #ubuntu else echo "进程已经启动" fi } startMonitor