当你的系统上有许多工作进程在跑,你想要一个统一的入口来管理这些进程,包括状态检查,启动和关闭,出错时警告,及自动重启等。那你就需要一个进程管理工具来帮助你。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启动成功了。

  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 appstart 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 Web Client

进程组

当你要管理的子进程非常多时,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的官方文档学习下。