当你的系统上有许多工作进程在跑,你想要一个统一的入口来管理这些进程,包括状态检查,启动和关闭,出错时警告,及自动重启等。那你就需要一个进程管理工具来帮助你。Supervisor就是其中一个简单而又强大的工具。虽说标题写了Linux,其实它可以用在大部分Unix衍生出来的系统,比如Linux, Mac OS X, Solaris和FreeBSD。
一. Supervisor组件
Supervisor有四个组件:
1. supervisord
运行Supervisor的后台服务,它用来启动和管理那些你需要Supervisor管理的子进程,响应客户端发来的请求,重启意外退出的子进程,将子进程的stdout和stderr写入日志,响应事件等。它是Supervisor最核心的部分。
2. supervisorctl
相当于supervisord的客户端,它是一个命令行工具,用户可以通过它向supervisord服务发指令,比如查看子进程状态,启动或关闭子进程。它可以连接不同的supervisord服务,包括远程机上的服务。
3. Web服务器
这是supervisord的Web客户端,用户可以在Web页面上完成类似于supervisorctl的功能。
4. 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启动成功了。
2016-11-27 15:03:06,830 INFO RPC interface 'supervisor' initialized
2016-11-27 15:03:06,830 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2016-11-27 15:03:06,832 INFO daemonizing the supervisord process
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 Start a process
start :* Start all processes in a group
start 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:*”来执行的命令就对进程组内所有的子进程起效了,果然很方便。