Python 进程管理工具 Supervisor 使用教程shell
Supervisor 是基于 Python 的进程管理工具,能够帮助咱们更简单的启动、重启和中止服务器上的后台进程,是 Linux 服务器管理的效率工具。ubuntu
什么状况下咱们须要进程管理呢?就是执行一些须要以守护进程方式启动的程序,好比一个后台任务、一组 Web 服务的进程(说是一组,是由于常常用 Nginx 来作负载均衡),这些极可能是一些网站、REST API 的服务、消息推送的后台服务、日志数据的处理分析服务等等。vim
须要注意的是 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
|
; 设置进程的名称,使用 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
这两个参数来肯定的,这句话什么意思呢,咱们来看这个例子。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
; 设置进程的名称,使用 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
1
2
3
4
5
|
; 默认为 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)
|
更详细的配置例子,能够参考以下,官方文档在这里
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
|
;[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] 。
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
|
# 使用默认的配置文件 /etc/supervisord.conf
supervisord
# 明确指定配置文件
supervisord -c /etc/supervisord.conf
# 使用 user 用户启动 supervisord
supervisord -u user
|
更多参数请参考文档
supervisorctl 命令介绍
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# 中止某一个进程,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
方法1
有一个简单的方法,由于 Linux 在启动的时候会执行 /etc/rc.local
里面的脚本,因此只要在这里添加执行命令就能够
1
2
|
# 若是是 Ubuntu 添加如下内容
/usr/
local/bin/supervisord -c /etc/supervisord.conf
|
1
2
|
# 若是是 Centos 添加如下内容
/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
|