Python 进程管理工具 Supervisor 使用教程
Supervisor 是基于 Python 的进程管理工具,可以帮助我们更简单的启动、重启和停止服务器上的后台进程,是 Linux 服务器管理的效率工具。
什么情况下我们需要进程管理呢?就是执行一些需要以守护进程方式启动的程序,比如一个后台任务、一组 Web 服务的进程(说是一组,是因为经常用 Nginx 来做负载均衡),这些很可能是一些网站、REST API 的服务、消息推送的后台服务、日志数据的处理分析服务等等。
需要注意的是 Supervisor 是通用的进程管理工具,可以用来启动任意进程,不仅仅是用来管理 Python 进程。
Supervisor 经常被用来管理由 gunicorn 启动的 Django 或 Flask 等 Web 服务的进程。我最常用的是用来管理和启动一组 Tornado 进程来实现负载均衡。
除此之外,Supervisor 还能很友好的管理程序在命令行上输出的日志,可以将日志重定向到自定义的日志文件中,还能按文件大小对日志进行分割。
目前 Supervisor 只能运行在 Unix-Like 的系统上,也就是无法运行在 Windows 上。Supervisor 官方版目前只能运行在 Python 2.4 以上版本,但是还无法运行在 Python 3 上,不过已经有一个 Python 3 的移植版 supervisor-py3k。
Supervisor 有两个主要的组成部分:
supervisord,运行 Supervisor 时会启动一个进程 supervisord,它负责启动所管理的进程,并将所管理的进程作为自己的子进程来启动,而且可以在所管理的进程出现崩溃时自动重启。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 不存在的错误 。
因此可以单独建一个文件夹,来存放这些文件,比如放在 /home/supervisor/
创建文件夹
mkdir /home/supervisor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| [unix_http_server] ;file=/tmp/supervisor.sock ; (the path to the socket file) ;修改为 /home/supervisor 目录,避免被系统删除 file=/home/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) ;修改为 /home/supervisor 目录,避免被系统删除 pidfile=/home/supervisor/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 ;修改为 /home/supervisor 目录,避免被系统删除 serverurl=unix:///home/supervisor/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)
就是由于上面的配置文件中 /home/supervisor 文件夹,没有授予启动 supervisord 的用户 oxygen 的写权限,可以将这个文件夹的拥有者设置该该账号
sudo chown oxygen /home/supervisor
一般情况下,我们可以用 root 用户启动 supervisord 进程,然后在其所管理的进程中,再具体指定需要以那个用户启动这些进程。
使用浏览器来管理
supervisor 同时提供了通过浏览器来管理进程的方法,只需要注释掉如下几行就可以了。
1 2 3 4 5 6 7 8 9 10
| ;[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
|

使用 include
在配置文件的最后,有一个 [include] 的配置项,跟 Nginx 一样,可以 include 某个文件夹下的所有配置文件,这样我们就可以为每个进程或相关的几个进程的配置单独写成一个文件。
1 2
| [include] files = /etc/supervisord.d/*.ini
|
进程的配置样例
一个简单的例子如下
1 2 3 4 5 6 7 8 9 10 11 12
| [program:your_program_name] command=python server.py --port=9000 directory=/home/python/tornado_server ; 执行 command 之前,先切换到工作目录 user=oxygen ; 使用 oxygen 用户来启动该进程 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 这两个参数来确定的,这句话什么意思呢,我们来看这个例子。
1 2 3 4 5 6 7 8 9 10 11 12 13
| [program:foo] command=python server.py --port=90%(process_num)02d directory=/home/python/tornado_server ; 执行 command 之前,先切换到工作目录 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
1 2 3 4 5
| stopasgroup=false ; send stop signal to the UNIX process killasgroup=false ; SIGKILL the UNIX process group (def false)
|
更详细的配置例子,可以参考如下,官方文档在这里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
|
将多个进程按组管理
Supervisor 同时还提供了另外一种进程组的管理方式,通过这种方式,可以使用 supervisorctl 命令来管理一组进程。跟 [program:x] 的进程组不同的是,这里的进程是一个个的 [program:x] 。
1 2 3
| [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 进程,同时我们在配置文件中设置的进程也会相应启动。
1 2 3 4 5 6
| supervisord supervisord -c /etc/supervisord.conf supervisord -u user
|
更多参数请参考文档
supervisorctl 命令介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| supervisorctl stop program_name supervisorctl start program_name supervisorctl restart program_name supervisorctl stop groupworker: supervisorctl stop groupworker:name1 supervisorctl stop all supervisorctl reload supervisorctl update
|
注意:显示用 stop 停止掉的进程,用 reload 或者 update 都不会自动重启。也可以参考这里
开机自动启动 Supervisord
方法1
有一个简单的方法,因为 Linux 在启动的时候会执行 /etc/rc.local 里面的脚本,所以只要在这里添加执行命令就可以
1 2
| /usr/local/bin/supervisord -c /etc/supervisord.conf
|
1 2
| /usr/bin/supervisord -c /etc/supervisord.conf
|
以上内容需要添加在 exit 命令前,而且由于在执行 rc.local 脚本时,PATH 环境变量未全部初始化,因此命令需要使用绝对路径。可以用 which supervisord 查看一下 supervisord 所在的路径。
在添加前,先在终端测试一下命令是否能正常执行,如果找不到 supervisord,可以用如下命令找到
sudo find / -name supervisord
如果是 Ubuntu 16.04 以上,rc.local 被当成了服务,而且默认是不会启动,需要手动启用一下服务。
https://askubuntu.com/questions/765120/after-upgrade-to-16-04-lts-rc-local-not-executing-command
启用 rc.local 服务
sudo systemctl enable rc-local.service
方法2
Supervisord 默认情况下并没有被安装成服务,它本身也是一个进程。官方已经给出了脚本可以将 Supervisord 安装成服务,可以参考这里查看各种操作系统的安装脚本,但是我用官方这里给的 Ubuntu 脚本却无法运行。
安装方法可以参考 serverfault 上的回答。
比如我是 Ubuntu 系统,可以这么安装,这里选择了另外一个脚本
1 2 3 4 5 6 7 8 9
| 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 文件路径等,如果存在不同则需要进行一些修改。
评论列表