Linux的后台进程运行有好几种方法,例如:nohup等。但是要做到可靠地在后台运行,我们就需要把它做成daemon,最好还能监控进程状态,在意外结束时能自动重启。
supervisor就是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。supervisord 要求管理的程序是非 daemon 程序,supervisord 会帮你把它转成 daemon 程序。例如:管理nginx 的话,必须在 nginx 的配置文件里(nginx.conf)添加一行设置 daemon off 让 nginx 以非 daemon 方式启动。
1、supervisor安装:
# yum install 的方式
yum install -y supervisor
或者:
wget https://pypi.python.org/packages/source/s/supervisor/supervisor-3.1.3.tar.gz --no-check-certificat
tar -zxvf supervisor-3.1.3.tar.gz
cd supervisor-3.1.3
sudo python setup.py install
#查看版本
supervisord -v
Supervisord 安装完成后,默认会生成/etc/supervisord.conf配置文件和目录/etc/supervisord.d(如果没有则自行创建)。并且,安装成功后会有两个可用的命令行 supervisord 和 supervisorctl
1)supervisord:
使用该命令可以启动supervisor服务,即:一个 supervisord进程,它负责启动所管理的进程,并将所管理的进程作为自己的子进程来启动,而且可以在所管理的进程出现崩溃时自动重启。启动supervisor:
# 使用默认的配置文件 /etc/supervisord.conf
supervisord
# 明确指定配置文件
supervisord -c /etc/supervisord.conf
# 使用 user 用户启动 supervisord
supervisord -u user
2)supervisorctl:
是命令行管理工具,可以用来执行 stop、start、restart 等命令,来对这些子进程进行管理。例如:
- supervisorctl stop program_name #停止某个进程,program_name 为 [program:x] 里的 x
- supervisorctl start program_name #启动某个进程
- supervisorctl restart program_name # 重启某个进程
- supervisorctl stop groupworker: # 结束所有属于名为 groupworker 这个分组的所有进程 (start,restart 同理)
- supervisorctl stop groupworker:name1 # 结束 groupworker:name1 这个进程 (start,restart 同理)
- supervisorctl stop all #停止全部进程,注:start/restart/stop都不会载入最新的配置文件;
- supervisorctl reload #载入最新的配置文件,停止原有进程并按新配置启动、管理所有进程
- supervisorctl update #根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启
此外,supervisor还提供了一个操作界面,通过界面对进程进行管理。
3)开机自启动supervisor:
Supervisord 默认情况下并没有被安装成服务,它本身也是一个进程。所以,我们一般情况要给supervisor配置成开机自启动。
最简单的方法是,修改/etc/rc.local脚本,添加如下内容:
supervisord -c /etc/supervisord.conf
2、配置:
我们先来看下配置文件内容
;[unix_http_server]
;file=/var/run/supervisor/supervisor.sock ; (the path to the socket file)
; Sample supervisor config file.
; Sample supervisor config file.
;[unix_http_server]
;file=/var/run/supervisor/supervisor.sock ; (the path to the socket file)
;chmod=0700 ; sockef 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=*: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=/data/logs/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=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false ; (start in foreground if true;default false)
minfds=1024 ; (min. avail startup file descriptors;default 1024)
minprocs=200 ; (min. avail process descriptors;default 200)
;umask=022 ; (process file creation umask;default 022)
;user=chrism ; (default is current user, required if root)
;identifier=supervisor ; (supervisord identifier, default is 'supervisor')
;directory=/tmp ; (default is not to cd during start)
;nocleanup=true ; (don't clean up tempfiles at start;default false)
;childlogdir=/tmp ; ('AUTO' child log dir, default $TEMP)
;environment=KEY=value ; (key value pairs to add to environment)
;strip_ansi=false ; (strip ansi escape codes in logs; def. false)
; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
;serverurl=unix:///var/run/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
;prompt=mysupervisor ; cmd line prompt (default "supervisor")
;history_file=~/.sc_history ; use readline history if available
; The below sample program section shows all possible program subsection values,
; create one or more 'real' program: sections to be able to control them under
; supervisor.
;[program:theprogramname]
;.....
;[eventlistener:theeventlistenername]
;......
;[group:thegroupname]
;programs=progname1,progname2 ; each refers to 'x' in [program:x] definitions
;priority=999 ; the relative start priority (default 999)
[include]
files = supervisord.d/*.ini
1)[unix_http_server]和[inet_http_server]配置:
supervisor是服务器端和客服端模式运行的,客服端负责向服务器端发送命令。服务器端负责根据客服端的命令和配置文件对进程进程调度。而服务器端和客服端有两种通信模式:
- 一种是通过上述unix_http_server 配置的tcp套接字通信;
- 一种是通过inet_http_server配置的端口通信模式。
这两个配置一个即可,通常会配置inet_http_server,这样我们可以通过http://hostname:9001的网页方式对进程进行管理了。如下:
2)[supervisord]配置:
指定日志大小、位置以及pid等信息...
3)[supervisorctl]配置:
这里把serverurl配置成http的方式;
3)[include]配置:
最后,为了管理清晰,我们通常把进程的配置放到单独的一个文件,然后使用include引入(类似nginx一样)。
4)[program]配置:
一个 [program:x] 实际上是表示一组同类的进程组,也就是说一个 [program:x] 可以启动多个进程。这组进程的成员是通过 numprocs 和 process_name 这两个参数来确定的。举例:
[program:foo]
command=python server.py --port=9002
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/supervisor/tornado_server.log
loglevel=info
此外,还有一些常用的配置:
- startsecs = 5 ; 启动 5 秒后没有异常退出,就当作已经正常启动了
- startretries = 3 ; 启动失败自动重试次数,默认是 3
5)[group]配置:
Supervisor 同时还提供了另外一种进程组的管理方式,将多个进程按组管理。例如:
[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。