基于Python 的进程管理工具supervisor使用指南


Posted in Python onSeptember 18, 2016

Supervisor 是基于 Python 的进程管理工具,只能运行在 Unix-Like 的系统上,也就是无法运行在 Windows 上。Supervisor 官方版目前只能运行在 Python 2.4 以上版本,但是还无法运行在 Python 3 上,不过已经有一个 Python 3 的移植版 supervisor-py3k。

什么情况下我们需要进程管理呢?就是执行一些需要以守护进程方式执行的程序,比如一个后台任务,我最常用的是用来启动和管理基于 Tornado 写的 Web 程序。

除此之外,Supervisor 还能很友好的管理程序在命令行上输出的日志,可以将日志重定向到自定义的日志文件中,还能按文件大小对日志进行分割。

Supervisor 有两个主要的组成部分:

  1. supervisord,运行 Supervisor 时会启动一个进程 supervisord,它负责启动所管理的进程,并将所管理的进程作为自己的子进程来启动,而且可以在所管理的进程出现崩溃时自动重启。
  2. supervisorctl,是命令行管理工具,可以用来执行 stop、start、restart 等命令,来对这些子进程进行管理。

安装

sudo pip install supervisor

创建配置文件

echo_supervisord_conf > /etc/supervisord.conf

如果出现没有权限的问题,可以使用这条命令

sudo su - root -c "echo_supervisord_conf > /etc/supervisord.conf"

配置文件说明

想要了解怎么配置需要管理的进程,只要打开 supervisord.conf 就可以了,里面有很详细的注释信息。

打开配置文件

vim /etc/supervisord.conf

默认的配置文件是下面这样的,但是这里有个坑需要注意,supervisord.pid 以及 supervisor.sock 是放在 /tmp 目录下,但是 /tmp 目录是存放临时文件,里面的文件是会被 Linux 系统删除的,一旦这些文件丢失,就无法再通过 supervisorctl 来执行 restart 和 stop 命令了,将只会得到 unix:///tmp/supervisor.sock 不存在的错误 。

[unix_http_server]
;file=/tmp/supervisor.sock  ; (the path to the socket file)
;修改为 /var/run 目录,避免被系统删除
file=/var/run/supervisor.sock  ; (the path to the socket file)
;chmod=0700         ; socket file mode (default 0700)
;chown=nobody:nogroup    ; socket file uid:gid owner
;username=user       ; (default is no username (open server))
;password=123        ; (default is no password (open server))

;[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))
...

[supervisord]
;logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
;修改为 /var/log 目录,避免被系统删除
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB    ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10      ; (num of main logfile rotation backups;default 10)
loglevel=info        ; (log level;default info; others: debug,warn,trace)
;pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
;修改为 /var/run 目录,避免被系统删除
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
...
;设置启动supervisord的用户,一般情况下不要轻易用root用户来启动,除非你真的确定要这么做
;user=chrism         ; (default is current user, required if root)
...

[supervisorctl]
; 必须和'unix_http_server'里面的设定匹配
;serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
;修改为 /var/run 目录,避免被系统删除
serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket
;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
;username=chris       ; should be same as http_username if set
;password=123        ; should be same as http_password if set
...

默认情况下,进程的日志文件达到50MB时,将进行分割,最多保留10个文件,当然这些配置也可以对每个进程单独配置。

权限问题

设置好配置文件后,应先创建上述配置文件中新增的文件夹。如果指定了启动用户 user,这里以 oxygen 为例,那么应注意相关文件的权限问题,包括日志文件,否则会出现没有权限的错误。例如设置了启动用户 oxygen,然后启动 supervisord 出现错误

Error: Cannot open an HTTP server: socket.error reported errno.EACCES (13)

就是由于上面的配置文件中 /var/run 文件夹,没有授予启动 supervisord 的用户 oxygen 的写权限。/var/run 文件夹实际上是链接到 /run,因此我们修改 /run 的权限。

sudo chmod 777 /run

这样有点简单粗暴,也可以考虑把上述配置文件中 .sock,.pid 等文件修改到其他文件夹中,并确保有相应的权限即可。一般情况下,我们可以用 root 用户启动 supervisord 进程,然后在其所管理的进程中,再具体指定需要以那个用户启动这些进程。

使用浏览器来管理

supervisor 同时提供了通过浏览器来管理进程的方法,只需要注释掉如下几行就可以了。

;[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))

[supervisorctl]
...
;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
;username=chris       ; should be same as http_username if set
;password=123        ; should be same as http_password if set

 基于Python 的进程管理工具supervisor使用指南

使用 include

在配置文件的最后,有一个 [include] 的配置项,跟 Nginx 一样,可以 include 某个文件夹下的所有配置文件,这样我们就可以为每个进程或相关的几个进程的配置单独写成一个文件。

[include]
files = /etc/supervisord.d/*.ini

进程的配置样例

一个简单的例子如下

; 设置进程的名称,使用 supervisorctl 来管理进程时需要使用该进程名
[program:your_program_name] 
command=python server.py --port=9000
;numprocs=1         ; 默认为1
;process_name=%(program_name)s  ; 默认为 %(program_name)s,即 [program:x] 中的 x
directory=/home/python/tornado_server ; 执行 command 之前,先切换到工作目录
user=oxygen         ; 使用 oxygen 用户来启动该进程
; 程序崩溃时自动重启,重启次数是有限制的,默认为3次
autorestart=true      
redirect_stderr=true    ; 重定向输出的日志
stdout_logfile = /var/log/supervisord/tornado_server.log
loglevel=info

设置日志级别

loglevel 指定了日志的级别,用 Python 的 print 语句输出的日志是不会被记录到日志文件中的,需要搭配 Python 的 logging 模块来输出有指定级别的日志。

多个进程

按照官方文档的定义,一个 [program:x] 实际上是表示一组相同特征或同类的进程组,也就是说一个 [program:x] 可以启动多个进程。这组进程的成员是通过 numprocs 和 process_name 这两个参数来确定的,这句话什么意思呢,我们来看这个例子。

; 设置进程的名称,使用 supervisorctl 来管理进程时需要使用该进程名
[program:foo] 
; 可以在 command 这里用 python 表达式传递不同的参数给每个进程
command=python server.py --port=90%(process_num)02d
directory=/home/python/tornado_server ; 执行 command 之前,先切换到工作目录
; 若 numprocs 不为1,process_name 的表达式中一定要包含 process_num 来区分不同的进程
numprocs=2          
process_name=%(program_name)s_%(process_num)02d; 
user=oxygen         ; 使用 oxygen 用户来启动该进程
autorestart=true      ; 程序崩溃时自动重启
redirect_stderr=true    ; 重定向输出的日志
stdout_logfile = /var/log/supervisord/tornado_server.log
loglevel=info

上面这个例子会启动两个进程,process_name 分别为 foo:foo_01 和 foo:foo_02。通过这样一种方式,就可以用一个 [program:x] 配置项,来启动一组非常类似的进程。

再介绍两个配置项 stopasgroup 和 killasgroup

; 默认为 false,如果设置为 true,当进程收到 stop 信号时,会自动将该信号发给该进程的子进程。如果这个配置项为 true,那么也隐含 killasgroup 为 true。例如在 Debug 模式使用 Flask 时,Flask 不会将接收到的 stop 信号也传递给它的子进程,因此就需要设置这个配置项。

stopasgroup=false       ; send stop signal to the UNIX process 

; 默认为 false,如果设置为 true,当进程收到 kill 信号时,会自动将该信号发给该进程的子进程。如果这个程序使用了 python 的 multiprocessing 时,就能自动停止它的子线程。
killasgroup=false       ; SIGKILL the UNIX process group (def false)

更详细的配置例子,可以参考如下,官方文档在这里

;[program:theprogramname]
;command=/bin/cat       ; the program (relative uses PATH, can take args)
;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
;numprocs=1          ; number of processes copies to start (def 1)
;directory=/tmp        ; directory to cwd to before exec (def no cwd)
;umask=022           ; umask for process (default None)
;priority=999         ; the relative start priority (default 999)
;autostart=true        ; start at supervisord start (default: true)
;autorestart=unexpected    ; whether/when to restart (default: unexpected)
;startsecs=1          ; number of secs prog must stay running (def. 1)
;startretries=3        ; max # of serial start failures (default 3)
;exitcodes=0,2         ; 'expected' exit codes for process (default 0,2)
;stopsignal=QUIT        ; signal used to kill process (default TERM)
;stopwaitsecs=10        ; max num secs to wait b4 SIGKILL (default 10)
;stopasgroup=false       ; send stop signal to the UNIX process group (default false)
;killasgroup=false       ; SIGKILL the UNIX process group (def false)
;user=chrism          ; setuid to this UNIX account to run the program
;redirect_stderr=true     ; redirect proc stderr to stdout (default false)
;stdout_logfile=/a/path    ; stdout log path, NONE for none; default AUTO
;stdout_logfile_maxbytes=1MB  ; max # logfile bytes b4 rotation (default 50MB)
;stdout_logfile_backups=10   ; # of stdout logfile backups (default 10)
;stdout_capture_maxbytes=1MB  ; number of bytes in 'capturemode' (default 0)
;stdout_events_enabled=false  ; emit events on stdout writes (default false)
;stderr_logfile=/a/path    ; stderr log path, NONE for none; default AUTO
;stderr_logfile_maxbytes=1MB  ; max # logfile bytes b4 rotation (default 50MB)
;stderr_logfile_backups=10   ; # of stderr logfile backups (default 10)
;stderr_capture_maxbytes=1MB  ; number of bytes in 'capturemode' (default 0)
;stderr_events_enabled=false  ; emit events on stderr writes (default false)
;environment=A="1",B="2"    ; process environment additions (def no adds)
;serverurl=AUTO        ; override serverurl computation (childutils)

将多个进程按组管理

Supervisor 同时还提供了另外一种进程组的管理方式,通过这种方式,可以使用 supervisorctl 命令来管理一组进程。跟 [program:x] 的进程组不同的是,这里的进程是一个个的 [program:x] 。

[group:thegroupname]
programs=progname1,progname2 ; each refers to 'x' in [program:x] definitions
priority=999         ; the relative start priority (default 999)

当添加了上述配置后,progname1 和 progname2 的进程名就会变成 thegroupname:progname1 和 thegroupname:progname2 以后就要用这个名字来管理进程了,而不是之前的 progname1。

以后执行 supervisorctl stop thegroupname: 就能同时结束 progname1 和 progname2,执行 supervisorctl stop thegroupname:progname1 就能结束 progname1。supervisorctl 的命令我们稍后介绍。

启动 supervisord

执行 supervisord 命令,将会启动 supervisord 进程,同时我们在配置文件中设置的进程也会相应启动。

# 使用默认的配置文件 /etc/supervisord.conf
supervisord
# 明确指定配置文件
supervisord -c /etc/supervisord.conf
# 使用 user 用户启动 supervisord
supervisord -u user

更多参数请参考文档

supervisorctl 命令介绍

# 停止某一个进程,program_name 为 [program:x] 里的 x
supervisorctl stop program_name
# 启动某个进程
supervisorctl start program_name
# 重启某个进程
supervisorctl restart program_name
# 结束所有属于名为 groupworker 这个分组的进程 (start,restart 同理)
supervisorctl stop groupworker:
# 结束 groupworker:name1 这个进程 (start,restart 同理)
supervisorctl stop groupworker:name1
# 停止全部进程,注:start、restart、stop 都不会载入最新的配置文件
supervisorctl stop all
# 载入最新的配置文件,停止原有进程并按新的配置启动、管理所有进程
supervisorctl reload
# 根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启
supervisorctl update

注意:显示用 stop 停止掉的进程,用 reload 或者 update 都不会自动重启。也可以参考这里

开机自动启动 Supervisord

Supervisord 默认情况下并没有被安装成服务,它本身也是一个进程。官方已经给出了脚本可以将 Supervisord 安装成服务,可以参考这里查看各种操作系统的安装脚本,但是我用官方这里给的 Ubuntu 脚本却无法运行。

安装方法可以参考 serverfault 上的回答。

比如我是 Ubuntu 系统,可以这么安装,这里选择了另外一个脚本

# 下载脚本
sudo su - root -c "sudo curl https://gist.githubusercontent.com/howthebodyworks/176149/raw/d60b505a585dda836fadecca8f6b03884153196b/supervisord.sh > /etc/init.d/supervisord"
# 设置该脚本为可以执行
sudo chmod +x /etc/init.d/supervisord
# 设置为开机自动运行
sudo update-rc.d supervisord defaults
# 试一下,是否工作正常
service supervisord stop
service supervisord start

注意:这个脚本下载下来后,还需检查一下与我们的配置是否相符合,比如默认的配置文件路径,pid 文件路径等,如果存在不同则需要进行一些修改。

其实还有一个简单的方法,因为 Linux 在启动的时候会执行 /etc/rc.local 里面的脚本,所以只要在这里添加执行命令就可以

# 如果是 Ubuntu 添加以下内容
/usr/local/bin/supervisord -c /etc/supervisord.conf

# 如果是 Centos 添加以下内容
/usr/bin/supervisord -c /etc/supervisord.conf

以上内容需要添加在 exit 命令前,而且由于在执行 rc.local 脚本时,PATH 环境变量未全部初始化,因此命令需要使用绝对路径。

在添加前,先在终端测试一下命令是否能正常执行,如果找不到 supervisord,可以用如下命令找到

sudo find / -name supervisord

Python 相关文章推荐
Python常用内置模块之xml模块(详解)
May 23 Python
使用python获取csv文本的某行或某列数据的实例
Apr 03 Python
django2+uwsgi+nginx上线部署到服务器Ubuntu16.04
Jun 26 Python
python实现简单名片管理系统
Nov 30 Python
pytorch 模型可视化的例子
Aug 17 Python
tensorflow使用freeze_graph.py将ckpt转为pb文件的方法
Apr 22 Python
jupyter notebook oepncv 显示一张图像的实现
Apr 24 Python
PyCharm MySQL可视化Database配置过程图解
Jun 09 Python
python如何代码集体右移
Jul 20 Python
django教程如何自学
Jul 31 Python
浅析python 字典嵌套
Sep 29 Python
python中的测试框架
Nov 13 Python
打包发布Python模块的方法详解
Sep 18 #Python
在python的类中动态添加属性与生成对象
Sep 17 #Python
Python中字符串的处理技巧分享
Sep 17 #Python
Python中对象迭代与反迭代的技巧总结
Sep 17 #Python
发布你的Python模块详解
Sep 15 #Python
Python selenium 三种等待方式解读
Sep 15 #Python
玩转python selenium鼠标键盘操作(ActionChains)
Apr 12 #Python
You might like
Discuz! Passport 通行证整合
2008/03/27 PHP
php数组函数序列之asort() - 对数组的元素值进行升序排序,保持索引关系
2011/11/02 PHP
PHP的array_diff()函数在处理大数组时的效率问题
2011/11/27 PHP
php5.3 注意事项说明
2013/07/01 PHP
IIS6.0 开启Gzip方法及PHP Gzip函数分享
2014/06/08 PHP
php+redis消息队列实现抢购功能
2018/02/08 PHP
PHP ADODB生成下拉列表框功能示例
2018/05/29 PHP
php二维数组按某个键值排序的实例讲解
2019/02/15 PHP
php使用pecl方式安装扩展操作示例
2019/08/12 PHP
jQuery中:first选择器用法实例
2014/12/30 Javascript
浅谈JavaScript 执行环境、作用域及垃圾回收
2016/05/31 Javascript
第一次接触神奇的Bootstrap
2016/10/14 Javascript
JS比较两个数值的大小实例
2016/11/25 Javascript
js实现漫天星星效果
2017/01/19 Javascript
vue的mixins属性详解
2018/03/14 Javascript
webpack4 + react 搭建多页面应用示例
2018/08/03 Javascript
对vux点击事件的优化详解
2018/08/28 Javascript
vue项目启动出现cannot GET /服务错误的解决方法
2020/04/26 Javascript
[01:28:44]DOTA2-DPC中国联赛定级赛 RNG vs iG BO3第一场 1月10日
2021/03/11 DOTA
Python中dictionary items()系列函数的用法实例
2014/08/21 Python
python遍历文件夹,指定遍历深度与忽略目录的方法
2018/07/11 Python
Python List cmp()知识点总结
2019/02/18 Python
PyCharm中如何直接使用Anaconda已安装的库
2020/05/28 Python
详解Python Celery和RabbitMQ实战教程
2021/01/20 Python
用CSS3打造HTML5的Logo(实现代码)
2016/06/16 HTML / CSS
KIKO美国官网:意大利的平价彩妆品牌
2017/05/16 全球购物
即兴演讲稿
2014/01/04 职场文书
函授本科自我鉴定
2014/02/04 职场文书
竞选村长演讲稿
2014/04/28 职场文书
融资租赁计划书
2014/04/29 职场文书
班级文化标语
2014/06/23 职场文书
教师党员学习群众路线心得体会
2014/11/04 职场文书
少先队中队工作总结2015
2015/07/23 职场文书
总经理聘用协议书
2015/09/21 职场文书
通知怎么写?
2019/04/17 职场文书
vue 把二维或多维数组转一维数组
2022/04/24 Vue.js