使用supervisor管理进程

使用supervisor管理进程

supervisor的好处

很多程序比较简单,它本身并没有start/stop/restart这些命令,start的话就是直接运行(比如./test),并且它也可能不会后台运行,如果要让它在后台运行,通常需要在最后加个&符或者在前面加个nohup等等,stop的话也没有stop命令,只能直接用杀死进程的方法(“kill -9”)来停止,而重启,其实就是杀死进程后再启动,而且经常有可能在运行的过程中意外退出,这时你必须手动去启动写监控脚本。很多时候,为了方便start/stop/restart这些命令,需要手写一个shell脚本用来完成start/stop/restart这些操作,但这样你就要写这个脚本,比较麻烦,而supervisor就能解决这个问题。

supervisor有一个后台进程叫supervisord,所有supervisor控制的程序都是以supervisord的子进程的方式启动的,如果该程序中途退出,它很容易就能监控到并进行重启,从而保证不需要手动去启动,并且可以通过supervisorctl命令很方便的实现start/stop/restart/status这些操作,而且还可以通过网页的方式来用鼠标点一下就能start/stop/restart,以及查看它的当前状态:
Xnip2019-03-15_02-48-41.jpg


安装supervisor

supervisor是python写的开源程序,源码github地址:https://github.com/Supervisor/supervisor

因为是python写的,所以你自然可以用pip/easy_install来安装,另外你也可以用linux系统的常用包管理命令来安装,比如yum/apt-get等等,当然你喜欢的话也可以手动安装,这里因为我是CentOS7系统,所以我直接用yum安装:

yum -y install supervisor

安装好之后,它有三个命令:echo_supervisord_conf/supervisorctl/supervisord(yum安装的是在/usr/bin下面)。

echo_supervisord_conf:用于输出默认配置文件,但是用yum安装的时候,它自动就给你输出了默认配置文件,所以这个命令可以说没什么用,除非你把默认配置文件搞乱了,想重新弄一份原始的默认配置文件,使用方法很简单:

echo_supervisord_conf > /etc/supervisord.conf

supervisord:有点linux基础的童鞋应该都知道,带d结尾的一般都叫“守护进程/后台进程”,ddaemon的意思,没错supervisord用于后台运行,提供服务,它的配置文件,就是前面echo_supervisord_conf输出的配置文件,yum安装默认在/etc/supervisord.conf

supervisorctl:最后是ctl,即control,控制的意思,没错,该程序是用于控制supervisord所管理的程序的。

所以使用supervisor的步骤就是,编辑supervisord的配置文件,启动supervisord,然后用supervisorctl来管理(当然还有web管理方式)。


supervisord.conf详解

该配置文件格式为标准的ini配置文件格式,即使用中括号指定模块,然后该模块下用key=value的方式进行配置,分号表示注释,根据这个规则,你会发现supervisord.conf中默认有[unix_http_server]、[inet_http_server]、[supervisord]、[rpcinterface:supervisor]、[supervisorctl]、;[program:theprogramname]、;[eventlistener:theeventlistenername]、;[group:thegroupname]、;[include]共9个模块,下面一个一个来讲解:


[unix_http_server]:该模块用于创建一个基于unix socket的http服务器,既然是http服务器,它不监听端口反而监听UNIX socket文件,这是什么道理?这样明显无法通过浏览器访问ip和端口的方式来访问它啊,对,确实是这样的,它并不是给浏览器用的,它是给supervisorctl用的,supervisorctl命令通过XMLRPC的方式与supervisord进行通信,给它发送指令,比如启动/停止/重启某个进程等等。它有以下几个配置项:

file=/tmp/supervisor.sock   ; 指定socket文件路径
;chmod=0700                 ; 指定socket文件权限(默认即可)
;chown=nobody:nogroup       ; 设置socket文件的所属用户和所属组(默认即可)
;username=user              ; 用户名(默认不设置)
;password=123               ; 密码(默认不设置)

这些配置项保持默认即可,因为是在本机才能连接,所以无需设置什么账号密码。


[inet_http_server]:前面的模块没有监听端口,但这个监听了,没错,该模块正是用于配置一个http服务器,让浏览器可以通过ip+端口的方式来访问supervisor的web管理页面。它有三个配置项:

;port=127.0.0.1:9001        ; (ip_address:port specifier, *:port for all iface)
;username=user              ; (default is no username (open server))
;password=123

把它们前面的;号去掉即可启用对应的配置项(因为;号表示注释),我把它改成一个实用例子你可能会明白点:

port=12.34.56.78:9001   ;服务器ip和端口(ip可写成“*”,如port=*:9001)
username=zhangsan   ;用户名
password=123456     ;密码

username和password很好理解,你访问网页后,要输入这里设置的账号和密码才能登录查看。而port除了指定监控的端口外,还指定监控的ip,一般情况下有两种写法,port=12.34.56.78:9001port=*:9001,第一种写法要把12.34.56.78改成你服务器的ip,第二种写法表示监听所有网卡,假设你服务器有两个网卡(每个网卡一个ip),那么你通过这两个ip都可以访问到supervisor的网页版控制台,或者你服务器原来的ip突然改了,那么第二种写法也可以不需要修改配置文件的情况下,浏览器依然可以正常访问网页控制台,而第一种写法就必须把新ip写到里面并重启supervisord才行,通过以上配置,你就可以在浏览器里通过访问http://服务器ip:9001来查看网页控制台了。


[supervisord]:该模块是supervisord自己的配置,以下是它的配置项

[supervisord]
logfile=/tmp/supervisord.log ;日志路径
logfile_maxbytes=50MB        ; 日志最大大小,默认50MB,超过会重新创建另一个日志文件
logfile_backups=10           ; 日志备份数量,比如50MB一个,如果设置备份数量为10,那么超过10个之后,最先创建的那个日志将会被删除,以此类推(个人猜测的,未验证)
loglevel=info                ; 日志级别;默认为 info; 其他可选的有: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; pid文件路径
nodaemon=false               ; 不要后台运行,默认为false,即“不要 不要后台运行”,双重否定,意思就是后台运行。
minfds=1024                  ; 最少文件描述符数量,默认1024,我也不清楚这个有什么用
minprocs=200                 ; 最少可用进程数,默认200
;umask=022                   ; 指supervisor创建的文件拥有的权限
;user=chrism                 ; 指定运行的用户,默认是用当前用户运行supervisord,如果你当前用户是root,那么你可以在这里指定你要以哪个用户运行(当然指定的用户必须是存在的用户)。
;identifier=supervisor       ; (supervisord 标识符, 默认就是 'supervisor')
;directory=/tmp              ; 工作目录
;nocleanup=true              ; 启动时不要清除临时文件,默认是false,也就是启动的时候会清除临时文件
;childlogdir=/tmp            ; 子进程日志目录,默认为$TEMP
;environment=KEY="value"     ;可添加到环境变量中的键值对
;strip_ansi=false            ; (在日志中删除ansi转义码; 默认是 false)

[rpcinterface:supervisor]:下面的英文注释也说了,这是rpc接口,如果要从web中启动或停止supervisor管理的服务,就要通过这个,另外supervisorctl控制服务也会用到这个,这是必需的,它只有一个选项,而且我们不用更改它,保持默认即可:

; 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

supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]:这一项是设置supervisorctl的相关设置,见下面各配置项的解释。

serverurl=unix:///tmp/supervisor.sock ; 指向unix_http_server的socket文件,但要用“unix://”开头,它的主要作用是让你可以在服务器上输入“supervisorctl”后直接回车,进入supervisorctl的控制台,这样在你启动,停止服务,查看服务状态的时候,不需要每次在前面加上supervisorctl了。
;serverurl=http://127.0.0.1:9001 ; 指向“inet_http_server”的ip和端口,注意如果在服务器上,这不需要设置,这个选项主要是你在你自己的电脑上安装了supervisor,想通过你本地的“supervisorctl”去管理远程服务器上的“supervisord”的时候,才需要用到
;username=chris              ; serverurl没有设置的时候,这个自然也不需要设置,因为这也是用于登录远程supervisord用的。如果使用了serverurl,那这个密码就必须与“inet_http_server”的账号相同。
;password=123                ; 与username同理,如果要设置,那就必须与“inet_http_server”的password相同
;prompt=mysupervisor         ; prompt是提示的意思,因为“supervisorctl”有两种用法,比如你可以“supervisorctl status redis”,也可以单独输入“supervisorctl”回车进入“supervisorctl”后,再直接输入“status redis”,而这个prompt就是你回车后,显示的指示符,默认值是“supervisor”。
;history_file=~/.sc_history  ; 操作历史

[program:theprogramname]:这是才是真正的程序示例,比如我要用supervisor来管理redis,我就可以写成“[program:redis]”,当然这个名称是你自己取的,只要你喜欢,你写成“abcd”都可以,因为这只是一个用于显示的名称,并不是用它来指定运行哪个程序,具体运行的程序用command配置项来指定(具体看下边的配置项解释)。该模块有以下配置顶:

; 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]
;command=/bin/cat              ; 指定要运行的命令
;process_name=%(program_name)s ; 指定进程名称(默认是 %(program_name)s),这个名称与“numprocs”的值有关,如果“numprocs=1”说明只给它开一个进程,一个进程的名称直接跟“program_name”相同即可(program_name就是中括号中“program:”后面那个名称),但如果“numprocs”不等于1,也就是进程不只有一个,那就不能写“%(program_name)s ”了,因为多个进程的名称明显不能相同。具体怎样写,由于我刚开始用,还没研究。
;numprocs=1                    ; 指定进程数,默认为1
;directory=/tmp                ; 执行程序前要切换到的目录
;umask=022                     ; 进程创建的文件拥有的权限(注意不是进程本身的权限,而是进程生成的文件,比如进程会产生log文件,这个umask的值就是这个log文件的权限)
;priority=999                  ; 优先级,因为supervisor可以管理很多程序,即我们可以配置先多个[program:redis],[program:nginx]等等,因为有多个,那么程序启动就要分先后,这个priority就是指定先后的,数字越小,优先级越高。 (默认 999)
;autostart=true                ; 是否自动启动?自动启动的意思是当启动supervisord守护进程的时候,是否要启动该程序 (默认: true)
;autorestart=true              ; 如果程序运行过程中意外退出了,是否要自动重启它? (默认: true)
;startsecs=10                  ; 指定程序启动后要保持启动状态几秒才能认为它确实已经启动成功了 (默认1秒),因为有些程序可能由于某些原因,启动两三秒后又突然退出,这显然不能认为已经启动了。
;startretries=3                ; 如果启动失败重试的次数 (默认 3次)
;exitcodes=0,2                 ; '期望的' 进程退出码 (默认为 0,2)
;stopsignal=QUIT               ; 进程停止信号,可以为TERM, HUP, INT, QUIT, KILL, USR1, or USR2等信号(Linux的知识),默认为TERM ,当用设定的信号去干掉进程,退出码会被认为是expected的,即会被认为是正常退出(比如你执行了stop),而不是意外退出。 (默认 TERM)
;stopwaitsecs=10               ; 是当我们向子进程发送stopsignal信号后,到系统返回信息给supervisord,所等待的最大时间。 超过这个时间,supervisord会向该子进程发送一个强制kill的信号。 (默认10秒)
;user=chrism                   ; 设置以哪个用户去运行,这个我猜你必须要用root权限运行supervisord,这里的设置才有效
;redirect_stderr=true          ; 重定向标准错误输出到标准输出流 (默认 false)
;stdout_logfile=/a/path        ; 标准输出日志路径, 如果设置为“NONE”表示不指定,默认是“AUTO”
;stdout_logfile_maxbytes=1MB   ; 标准输出日志文件最大值,超过后会重新创建一个新的文件 (默认 50MB)
;stdout_logfile_backups=10     ; # 因为日志一直在输出,单个日志超过stdout_logfile_maxbytes设置的大小后,将会创建一个新文件接收日志,这样日志肯定会越来越多, stdout_logfile_backups配置项就是用于设置最多能有多少个日志文件,如果超出了,那么最先生成的文件将会被删除(默认 10)
;stdout_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
;stdout_events_enabled=false   ; 当设置为ture的时候,当子进程由stdout向文件描述符中写日志的时候,将触发supervisord发送PROCESS_LOG_STDOUT类型的event。 (默认 false)
;stderr_logfile=/a/path        ; 与stdout_logfile类似
;stderr_logfile_maxbytes=1MB   ; 与stdout_logfile_maxbytes类似 (默认 50MB)
;stderr_logfile_backups=10     ; # 与stdout_logfile_backups类似 (默认 10)
;stderr_capture_maxbytes=1MB   ;  这个东西是设定capture管道的大小,当值不为0的时候,子进程可以从stdout发送信息,而supervisor可以根据信息,发送相应的event。(默认 0,表示关闭管道)
;stderr_events_enabled=false   ; 设置为ture的时候,当子进程由stdout向文件描述符中写日志的时候,将触发supervisord发送PROCESS_LOG_STDOUT类型的event。(默认 false)
;environment=A=1,B=2           ; 这个是该子进程的环境变量,和别的子进程是不共享的 (默认无)
;serverurl=AUTO                ; override serverurl computation (childutils)

这里要说明一下执行前要切换到的目录,即directory参数,比如我要运行redis,假设我直接运行的命令是这样的:

/usr/local/redis/redis-server /usr/local/redis/redis.conf

如果设置了directory=/usr/local/redis/,那么就可以这样运行,也就是配置文件可以写相对路径了:

/usr/local/redis/redis-server ./redis.conf

但是,命令本身还是不能写相对路径,比如你不能这么写:

./redis-server ./redis.conf

[eventlistener:theeventlistenername]:与“[program:theprogramname]”类似,“theeventlistenername”是一个自定义名称,这个东西其实和program的地位是一样的,也是suopervisor启动的子进程,不过它干的活是订阅supervisord发送的event。他的名字就叫listener了。我们可以在listener里面做一系列处理,比如报警等等。这个模块大部分配置项与上边的“program”的相同,直接参照上边的解释即可。

; The below sample eventlistener section shows all possible
; eventlistener subsection values, create one or more 'real'
; eventlistener: sections to be able to handle event notifications
; sent by supervisor.

;[eventlistener:theeventlistenername]
;command=/bin/eventlistener    ; 和上面的program一样,表示listener的可执行文件的路径,可带参数
;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
;numprocs=1                    ; number of processes copies to start (def 1)
;events=EVENT                  ; event事件的类型,也就是说,只有写在这个地方的事件类型才会被发送
;buffer_size=10                ; 这个是event队列缓存大小,单位我猜是“个”。当buffer超过buffer_size个的时候,最旧的event将会被清除,并把新的event放进去。 (默认 10)
;directory=/tmp                ; directory to cwd to before exec (def no cwd)
;umask=022                     ; umask for process (default None)
;priority=-1                   ; the relative start priority (default -1)
;autostart=true                ; start at supervisord start (default: true)
;autorestart=unexpected        ; restart at unexpected quit (default: unexpected)
;startsecs=10                  ; 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)
;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_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        ; # of stderr logfile backups (default 10)
;stderr_events_enabled=false   ; emit events on stderr writes (default false)
;environment=A=1,B=2           ; process environment additions
;serverurl=AUTO                ; override serverurl computation (childutils)

[group:thegroupname]:定义一个组,比如我有一个php网站开发环境,这个环境包括:php-fpm/nginx/redis/mysql,那我就可以定义一个组,把它些程序都放在组里,我启动这个组就会把这组里包含的所有程序都启动。

; The below sample group section shows all possible group values,
; create one or more 'real' group: sections to create "heterogeneous"
; process groups.

;[group:thegroupname]
;programs=php-fpm,nginx,redis,mysql  ; 定义该组有哪些程序,程序之间用逗号分隔,注意,这些程序必须在前面用“[program:theprogramname]”模块定义过。
;priority=999                  ; 启动优先级,假设有多个组,每个组一个优先级,越小越优先执行 (默认 999)

[include]:这是include模块,会自动包括当前配置文件目录下的“supervisord.d”目录里面的“xxxx.ini”文件,以上的program模块、eventlistener模块、group模块都可以写多个的,建议这些模块都不要在supervisord.conf文件中写,而是写到“supervisord.d”目录下,一个模块一个文件,这样结构就会非常清晰。

; The [include] section can just contain the "files" setting.  This
; setting can list multiple files (separated by whitespace or
; newlines).  It can also contain wildcards.  The filenames are
; interpreted as relative to this file.  Included files *cannot*
; include files themselves.

[include]
files = supervisord.d/*.ini

示例配置

以上解释了这么多,这里来一个示例吧,“supervisord.conf”我基本上没修改,只修改以下这个http服务器的配置,以及修改了一下supervisord的日志路径:

[inet_http_server]
port=*:9001
username=zhangsan
password=192837465

[supervisord]
logfile=/var/log/supervisor/supervisord.log

另外在“supervisord.d”中添加了一个文件“ankisyncd.ini”,配置如下,很多配置我都没修改,只改了必须要改的:

[program:ankisyncd]
command=/bin/python36 -m ankisyncd              ; 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=/usr/local/anki-sync-server/                ; 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=true              ; retstart at unexpected quit (default: true)
startsecs=3                  ; 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)
;user=chrism                   ; setuid to this UNIX account to run the program
;redirect_stderr=true          ; redirect proc stderr to stdout (default false)
stdout_logfile=/var/log/ankisyncd.log        ; 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=/var/log/ankisyncd.error.log    ; 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)

启动supervisord守护进程

启动它非常简单,直接这样:

supervisord

但这样启动会有一串类似“报错”的内容输出(但事实上并非报错):

/usr/lib/python2.7/site-packages/supervisor/options.py:296: UserWarning: Supervisord is running as root and it is searching for its configuration file in default locations (including its current working directory); you probably want to specify a "-c" argument specifying an absolute path to a configuration file for improved security.
  'Supervisord is running as root and it is searching '

事实上并不是报错,而是在自动找配置文件,它会先找“./supervisord.conf”,找不到再找“./etc/supervisord.conf”目录中找,还找不到就会去“/etc/supervisord.conf”找,由于我们的配置文件默认就在“/etc/supervisord.conf”,所以它能找到,如果你要自己指定,用-c选项:

supervisord -c /path/to/supervisord2.conf

Web控制台使用

由于前面配置过“inet_http_server”模块,所以你直接访问“http://你的服务器ip:端口”即可访问到Web控制台,当然要求你输入账号密码,输入你在“inet_http_server”模块中设置的账号密码即可进入:
Xnip2019-03-15_02-48-41.jpg
在这里你可以用鼠标点击启动、停止、重启相应的进程。

supervisorctl的使用

查看帮助:

supervisorctl help

查看某个动作的帮助,如查看restart这个动作怎么用:

supervisorctl help restart

查看所有进程的状态:

supervisorctl status

查看“ankisyncd”这个进程的状态(“ankisyncd”这个名称,是前面配置文件中以“[program:ankisyncd]”这样的形式定义的):

supervisorctl status ankisyncd

启动“ankisyncd”进程:

supervisorctl start ankisyncd

停止“ankisyncd”进程:

supervisorctl stop ankisyncd

重启“ankisyncd”进程:

supervisorctl restart ankisyncd

重启supervisord服务本身:

supervisorctl reload

如果一个程序运行多个实例,修改了配置文件导致无法用start/stop/restart启动某几个,那就用这个reload。

重新读取配置文件(并不会使配置文件生效,只是让你查出哪个配置文件改变了,要让新配置文件生效得用上边的reload):

supervisorctl reread

另外,你可以直接输入supervisorctl,按回车,进入supervisorctl控制台,这样你执行以上操作的时候,就不用每次在开头使用“supervisorctl”了,比如要查看状态,直接输入status即可,要启动停止重启进程,直接“start xxxx”,“stop xxxx”,“restart xxx”即可:
Xnip2019-03-15_17-08-54.jpg

本文部分内容参考:https://www.cnblogs.com/ajianbeyourself/p/5534737.html

打赏

Leave a Reply

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
Notify of

扫码在手机查看
iPhone请用自带相机扫
安卓用UC/QQ浏览器扫

使用supervisor管理进程