Linux上的后台进程管理工具Supervisor
当你的系统上有许多工作进程在跑,你想要一个统一的入口来管理这些进程,包括状态检查,启动和关闭,出错时警告,及自动重启等。那你就需要一个进程管理工具来帮助你。Supervisor就是其中一个简单而又强大的工具。虽说标题写了Linux,其实它可以用在大部分Unix衍生出来的系统,比如Linux, Mac OS X, Solaris和FreeBSD。
Supervisor组件
Supervisor有四个组件:
supervisord 运行Supervisor的后台服务,它用来启动和管理那些你需要Supervisor管理的子进程,响应客户端发来的请求,重启意外退出的子进程,将子进程的stdout和stderr写入日志,响应事件等。它是Supervisor最核心的部分。
supervisorctl 相当于supervisord的客户端,它是一个命令行工具,用户可以通过它向supervisord服务发指令,比如查看子进程状态,启动或关闭子进程。它可以连接不同的supervisord服务,包括远程机上的服务。
Web服务器 这是supervisord的Web客户端,用户可以在Web页面上完成类似于supervisorctl的功能。
XML-RPC接口 这是留给第三方集成的接口,你的服务可以在远程调用这些XML-RPC接口来控制supervisord管理的子进程。上面的Web服务器其实也是通过这个XML-RPC接口实现的。
安装
Supervisor是由Python写的,源码托管在Github仓库里。如果你机上有Python环境,你可以clone一份源码,然后通过setup.py
来安装:
$ python setup.py install
也可以通过PyPI安装,这样方便许多:
$ pip install supervisor
另外,Supervisor在Ubuntu和CentOS上都有分发包,比如在Ubuntu上,你就可以用”apt-get”来安装:
$ apt-get install supervisor
注意,根据你系统权限的情况,以上这些命令可能会需要root权限来执行。
配置
安装完毕后,你可以执行下命令echo_supervisord_conf
,它会将一个配置样例打印在控制台上,如果你看到配置信息了,说明安装成功。现在让我们将配置样例保存在本地:
$ echo_supervisord_conf > supervisord.conf
官网上建议直接保存在/etc/supervisord.conf
文件中,可是大部分情况下我们是没有权限写/etc
目录的,所以要先保存下来,再通过root拷贝过去。Supervisor启动时会自动加载该配置文件。
让我们打开配置文件看看,这就是一个INI格式的配置文件啊,参数好多,而且大部分都注视掉了。这里就不一一介绍了,官方文档里有详细的。我们就介绍下最重要的部分,就是[program:theprogramname]
。
这个[program:xxx]
块定义了你要Supervisor管理的子进程,冒号后面是你可以起的名字。建议采用比较有意义的名字,因为[program:xxx]
块可以有多个,这样可以同时管理多个不同的子进程,名字起的好方便区分。让我们了解下这个配置块下几个主要的配置参数吧:
command
子进程的运行命令,比如你要监控一个Python程序app.py
的运行,你可以设置command=python /home/bjhee/myapp/app.py
。当supervisord服务启动时,该程序也会被自动启动,并作为子进程由supervisord管理。numprocs
同时启动的进程个数,用来实现并发,默认是1。注意如果该参数大于1的话,你必须同时配置process_name
参数,并且将%(process_num)s
变量放入process_name
中,防止多个进程同名导致启动出错。directory
如果配置了这个目录,那子进程运行前,会先切换到这个目录。user
运行该子进程的用户,默认同supervisord服务的启动用户。如果supervisord由root启动,而你又不想给子进程root,你可以配置这个参数。priority
该子进程优先级,决定了启动和关闭子进程的顺序,默认是最大值999
。autostart
启动supervisord时,子进程是否自动启动,默认是true
。autorestart
当子进程出错退出时,supervisord是否自动将其重启,默认是unexpected
,也就是不自动重启,你可以设为true
。stdout-logfile
由于子进程由supervisord启动,所以其stdout将无法输出到系统的标准输出上,所以你要将子进程的stdout写入到日志文件中。这个参数指定了该日志文件的位置。stderr-logfile
同上面的stdout-logfile,这里指定了stderr写入的日志文件位置。
启动Supervisor
我们就写个最简单的Web应用吧,可以从我的这篇文章里拷一个Flask的Hello World程序,假设保存在/home/billy/myapp/app.py
文件中。现在让我们在配置文件中,加入配置项:
[program:app]
command=python /home/billy/myapp/app.py
就这一个配置够了,然后让我们启动supervisord:
$ supervisord
注意,你可能要root来运行supervisord命令。运行该命令时,supervisord会自动在以下几个位置寻找配置文件:
- 当前目录下的supervisord.conf
- 当前目录下的etc/supervisord.conf
- /etc/supervisord.conf
- /etc/supervisor/supervisord.conf
当你的配置文件不在上述位置时,那就必须指定配置文件的位置,这时启动命令应改为:
$ supervisord -c /home/billy/supervisor/hello.conf
如果你的确想把配置文件放在自己的工作目录下,一个推荐的方式,是依然创建/etc/supervisord.conf
文件,并在该文件的最后加上[include]
配置块:
[include]
files = /home/billy/supervisor/*.conf
运行完supervisord命令后,你可以打开supervisord的日志文件,默认是/tmp/supervisord.log
,你也可以通过[supervisord]
配置块下的参数logfile
来指定。如果在日志文件中看到下面的信息,就说明Supervisor启动成功了。
1 2016-11-27 15:03:06,830 INFO RPC interface 'supervisor' initialized
2 2016-11-27 15:03:06,830 CRIT Server 'unix_http_server' running without any HTTP authentication checking
3 2016-11-27 15:03:06,832 INFO daemonizing the supervisord process
4 2016-11-27 15:03:06,833 INFO supervisord started with pid 73469
命令行客户端
让我们再打开一个控制台窗口,运行supervisorctl
命令,”supervisorctl”同”supervisord”一样会自动寻找”supervisord.conf“配置文件,如果不在默认位置的话,启动时须用参数-c
指定。
”supervisorctl“启动后,你将会看到一个类似Shell的交互窗口。在这个窗口中,你可以输入status
命令查看所有子进程的状态;可以输入stop app
或start app
命令来关闭或启动名称为”app”的子进程;如果此时你修改了配置文件,你可以输入reload
命令让”supervisord”服务重新加载配置文件。
supervisor> status
app RUNNING pid 74652, uptime 0:00:34
supervisor> stop app
app: stopped
supervisor> status app
app STOPPED Nov 27 10:35 PM
当你不清楚命令怎么用时,可以输入help
查看帮助,或者类似于help start
来查看start
命令的帮助。
supervisor> help start
start <name> Start a process
start <gname>:* Start all processes in a group
start <name> <name> Start multiple processes or groups
start all Start all processes
Web客户端
使用Web客户端前,你先要在配置文件中启用它,方法是添加下列配置项:
[inet_http_server]
port=*:9001
;username=user
;password=123
用户名密码是用于安全验证,不设的话任何人都可以访问。配置完毕启动”supervisord”服务,你就可以通过http://localhost:9001
来访问Web客户端了。下面是页面的示例,可以看到这里允许你从Web页面上查看/启动/关闭子进程。
进程组
当你要管理的子进程非常多时,Supervisor允许你将子进程分组,也就是多个子进程可以在同一组内统一管理。你只需要配置[group:thegroupname]
块就行了。
我们可以试下,再配置一个[program:hello]
子程序,然后加上[group:xxx]
配置:
[program:hello]
command=python /home/billy/myapp/hello.py
[group:appgroup]
programs=app,hello
现在我们将子进程”app”和”hello”都配置在进程组”appgroup”中,启动”supervisord”,让我们在”supervisorctl”客户端下试一试:
supervisor> status appgroup:*
appgroup:app RUNNING pid 75042, uptime 0:03:12
appgroup:hello RUNNING pid 75043, uptime 0:03:12
supervisor> stop appgroup:*
appgroup:app: stopped
appgroup:hello: stopped
supervisor> status appgroup:*
appgroup:app STOPPED Nov 27 11:05 PM
appgroup:hello STOPPED Nov 27 11:05 PM
子进程名前都加了前缀appgroup:
,这样我们使用通配符appgroup:*
来执行的命令就对进程组内所有的子进程起效了,果然很方便。
更多参考资料
Supervisor还有事件响应,写XML-RPC扩展等高级功能,这里就不细述了,大家可以去Supervisor的官方文档学习下。