Supervisor官网

安装

yum install -y supervisor

启用

systemctl enable supervisord.service
systemctl daemon-reload
systemctl start supervisord

开启WEB管理

#编辑配置文件
vim /etc/supervisord.conf 
#去掉注释 port=127.0.0.1:9001 修改为 0.0.0.0:9001
 
[inet_http_server]         ; inet (TCP) server disabled by default
port=0.0.0.0: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))
#添加配置目录
#files = supervisord.d/*.ini 修改成 files = supervisord.d/*.ini /opt/*/*.conf
files = supervisord.d/*.ini  /opt/*/*.conf


 

配置监听程序

[program:nginx]
;directory = /www/lanmps/bin ; 程序的启动目录
command = /www/lanmps/bin/nginx start ; 启动命令,可以看出与手动在命令行启动的命令是一样的
autostart = true ; 在 supervisord 启动的时候也自动启动
startsecs = 5 ; 启动 5 秒后没有异常退出,就当作已经正常启动了
autorestart = true ; 程序异常退出后自动重启
startretries = 3 ; 启动失败自动重试次数,默认是 3
user = www ; 用哪个用户启动
redirect_stderr = true ; 把 stderr 重定向到 stdout,默认 false
stdout_logfile_maxbytes = 20MB ; stdout 日志文件大小,默认 50MB
stdout_logfile_backups = 20 ; stdout 日志文件备份数
; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)
stdout_logfile = /www/logs/usercenter_stdout.log
stopasgroup=false  ;默认为false,进程被杀死时,是否向这个进程组发送stop信号,包括子进程
killasgroup=false ;默认为false,向进程组发送kill信号,包括子进程
 
; 可以通过 environment 来添加需要的环境变量,一种常见的用法是修改 PYTHONPATH
; environment=PYTHONPATH=$PYTHONPATH:/path/to/somewhere

 

supervisord 客户端管理命令

supervisorctl status                            # 状态
supervisorctl stop nginx                #关闭 nginx
supervisorctl start nginx               #启动 nginx
supervisorctl restart nginx             #重启 nginx
supervisorctl reread
supervisorctl update                    #更新新的配置
supervisorctl reload                    #重载配置

 

利用Supervisord进行进程监控和报警

利用Supervisord进行进程监控和报警主要的利用Supervisord的Event特性,编写一个listener,监控进程状态的改变,然后执行指定的代码。

Supervisord支持的Event有:详细的Event特性请参考: http://supervisord.org/events.html

Supervisord支持的Event有:

PROCESS_STATE 进程状态发生改变
PROCESS_STATE_STARTING 进程状态从其他状态转换为正在启动
(Supervisord的配置项中有startsecs配置项,
是指程序启动时需要程序至少稳定运行x秒才认为程序运行正常,在这x秒中程序状态为正在启动)
PROCESS_STATE_RUNNING 进程状态由正在启动转换为正在运行
PROCESS_STATE_BACKOFF 进程状态由正在启动转换为失败
PROCESS_STATE_STOPPING 进程状态由正在运行转换为正在停止
PROCESS_STATE_EXITED 进程状态由正在运行转换为退出
PROCESS_STATE_STOPPED 进程状态由正在停止转换为已经停止
(exited和stopped的区别是exited是程序自行退出,而stopped为人为控制其退出)
PROCESS_STATE_FATAL 进程状态由正在运行转换为失败
PROCESS_STATE_UNKNOWN 未知的进程状态
REMOTE_COMMUNICATION 使用Supervisord的RPC接口与Supervisord进行通信
PROCESS_LOG 进程产生日志输出,包括标准输出和标准错误输出
PROCESS_LOG_STDOUT 进程产生标准输出
PROCESS_LOG_STDERR 进程产生标准错误输出
PROCESS_COMMUNICATION 进程的日志输出包含 和
PROCESS_COMMUNICATION_STDOUT 进程的标准输出包含 和
PROCESS_COMMUNICATION_STDERR 进程的标准错误输出包含 和
SUPERVISOR_STATE_CHANGE_RUNNING Supervisord启动
SUPERVISOR_STATE_CHANGE_STOPPING Supervisord停止
TICK_5 每隔5秒触发
TICK_60 每隔60秒触发
TICK_3600 每隔3600触发
PROCESS_GROUP Supervisord的进程组发生变化
PROCESS_GROUP_ADDED 新增了Supervisord的进程组
PROCESS_GROUP_REMOVED 删除了Supervisord的进程组

编写程序,示例程序监控进程的异常退出和错误日志输出:
我们可以利用Supervisord的特性监控进程并报警,如当进程异常退出时报警,或当进程产生错误输出是报警。

报警示例


#!/usr/bin/env python
#coding=utf-8
'''
    Suprevisord Listener example.
'''
 
import sys
import os
 
def write_stdout(s):
    sys.stdout.write(s)
    sys.stdout.flush()
 
def write_stderr(s):
    sys.stderr.write(s)
    sys.stderr.flush()
 
def baojing(msg=None, data=None):
    if msg == None and data == None:
        return
    # alert
 
def parseData(data):
    tmp = data.split('\n')
    pheaders = dict([ x.split(':') for x in tmp[0].split() ])
    pdata = None
    if len(tmp) > 1:
        pdata = tmp[1]
    return pheaders, pdata
 
def main():
    #Only supervisord can run this listener, otherwise exit.
    if not 'SUPERVISOR_SERVER_URL' in os.environ:
        print "%s must be run as a supervisor listener." % sys.argv[0]
        return
 
    while True:
        #echo 'READY' and wait for event for stdin.
        write_stdout('READY\n')
        line = sys.stdin.readline()  # read header line from stdin
        headers = dict([ x.split(':') for x in line.split() ])
        data = sys.stdin.read(int(headers['len'])) # read the event payload
 
        if headers['eventname'] == 'PROCESS_STATE_EXITED' or\
           headers['eventname'] == 'PROCESS_STATE_FATAL' or\
           headers['eventname'] == 'PROCESS_STATE_STOPPED':
            pheaders, pdata = parseData(data)
            from_state = pheaders['from_state']
            process_name = pheaders['processname']
            if headers['eventname'] == 'PROCESS_STATE_EXITED' and\
                not int(pheaders['expected']):
                msg = '进程%s(PID: %s)异常退出,请检查进程状态.'\
                    % (process_name, pheaders['pid'])
                baojing(msg=msg)
            if headers['eventname'] == 'PROCESS_STATE_FATAL':
                msg = '进程%s启动失败,请检查进程状态.'\
                    % (process_name)
                baojing(msg=msg)
        elif headers['eventname'] == 'PROCESS_LOG_STDERR':
            pheaders, pdata = parseData(data)
            process_name = pheaders['processname']
            pid = pheaders['pid']
            msg = '进程%s(PID: %s)错误输出,请检查进程状态,错误输出信息: %s.' \
                % (process_name, pid, pdata)
            baojing(msg=msg)
        #echo RESULT
        write_stdout('RESULT 2\nOK') # transition from READY to ACKNOWLEDGED
 
if __name__ == '__main__':
    main()

 

配置 listener

[eventlistener:listener]
command = /usr/bin/python /path/to/listener.py ;报警脚本
process_name = %(program_name)s
autostart = true
autorestart = unexpected
startretries = 10
startsecs = 10
stopwaitsecs = 120
user = zhyaof
events = PROCESS_STATE_EXITED,PROCESS_STATE_STOPPED,PROCESS_STATE_FATAL,PROCESS_LOG_STDERR ;;报警事件
stopsignal = TERM
stdout_logfile_maxbytes = 50MB
stdout_logfile_backups = 5
stdout_logfile = /home/ma45/var/log/supervisord/listener.stdout.log
stderr_logfile_maxbytes = 50MB
stderr_logfile_backups = 5
stderr_logfile = /home/ma45/var/log/supervisord/listener.stderr.log

注意:

如果需要监听进程的日志输出,包括标准输出和标准错误输出,需要在被监控程序的配置项中添加:

stdout_events_enabled = true
stderr_events_enabled = true

如果需要监听进程的PROCESS_COMMUNICATION时间,包括PROCESS_COMMUNICATION_STDOUT和PROCESS_COMMUNICATION_STDERR,需要在被监控程序的配置项中添加:

stdout_capture_maxbytes = 1MB
​​​​​​​stderr_capture_maxbytes = 1MB

其中1MB的大小可以根据实际情况进行改动。