这篇文章主要是自己对supervisord学习的一个总结。可能总结有不对的地方,希望大家留言指正。
supervisord主要是用于管理服务进程。可以把那些运行在后台的服务放在supervisor中进行管理,监控和报警。
安装supervisor
# 安装pip,可以用源码安装(推荐),也可以用镜像源安装。
yum install -y python-pip
# 安装supervisord
pip install supervisord
# 生成supervisord配置文件
echo_supervisord_conf >> /etc/supervisord.conf
# 启动supervisord服务
supervisord -c /etc/supervisord.conf
# 设定扩展目录
echo "files = /etc/supervisord.conf.d/*.conf" >> /etc/supervisord.conf
配置supervisord服务配置demo
;[program:theprogramname]
;command=/bin/cat ; # 运行的程序命令
;process_name=%(program_name)s ; # 程序进程
;numprocs=1 ; # 启动线程,默认1.有些自动以服务支持多个线程。
;directory=/tmp ; # 执行前切换的工作目录
;umask=022 ; # umask for process (default None)
;priority=999 ; # the relative start priority (default 999)
;autostart=true ; # supervisord启动时候自启动
;startsecs=1 ; # 启动时候延迟秒数
;startretries=3 ; # 失败时尝试重启次数
;autorestart=true ; # 自动重启
;exitcodes=0,2 ; # 捕捉退出信号ID后重启
;stopsignal=QUIT ; # 停止方式
;stopwaitsecs=10 ; # 设置停止超时时间
;stopasgroup=false ; # send stop signal to the UNIX process group (default false)
;killasgroup=false ; # SIGKILL the UNIX process group (def false)
;user=chrism ; # setuid to this UNIX account to run the program
;redirect_stderr=true ; # 重定向stderr
;stdout_logfile=/a/path ; # stdout重定向日志
;stdout_logfile_maxbytes=1MB ; # 设定单个日志文件大小。满容后自动切换备份
;stdout_logfile_backups=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=/a/path ; 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停止/重启服务问题整理
1,在配置文件指定stopsignal方式。常见默认有 QUIT,TERM,INT。supervisorctl模拟kill命令结束服务,kill -l命令可以查看kill的方式,kill -l INT 查看退出时发出的信号ID,exitcodes会捕捉这些结束ID判断是否重启
2,在supervisor停止时候,只会对当前的命令或脚本发出结束信号。如果脚本A或命令A新运行了进程B。那么A的父ID是supervisor,B的父ID是A。supervisord只能停止A,B需要由A发出信号停止。反之B因为A结束而找不到父ID,B的父ID则变更为1(root)
supervisor监控的报警
在配置文件中可以找到[eventlistener:x]部分实现事件侦听器。
eventlistener监听program的stdin数据流信息获取program的相关程序状态。
eventlistener监听eventlistener的stdin数据流信息来确定自己当前监听状态。监听状态:ACKNOWLEDGED,READY,BUSY。
supervisord整理program的输出。
supervisord监控program的输出状态。
supervisor的eventlistener配置。整体与program是一样
[eventlistener:listenername] ; # 监控事件别名
command=/bin/eventlistener ; # 运行的命令
process_name=%(program_name)s ; # 运行的program名
directory=/ ; # 运行的脚本路径
events=PROCESS_STATE_EXITED,PROCESS_STATE_RUNNING,PROCESS_STATE_FATAL ; # 需要监控的状态,具体在supervisord查找
简单的日志整理报警脚本
#!/usr/bin/env python
# 监控的状态
EVENT_STATE = {
'PROCESS_STATE_EXITED' : 'EXITED',
'PROCESS_STATE_RUNNING': 'RUNNING',
'PROCESS_STATE_FATAL' : 'FATAL'
}
# stdout输出
def write_stdout(s):
sys.stdout.write(s)
sys.stdout.flush()
# stdin输出
def write_stderr(s):
sys.stderr.write(s+"\n")
sys.stderr.flush()
def main():
# 判断是否由supervisor运行
if not 'SUPERVISOR_SERVER_URL' in os.environ:
print "%s must be run as a supervisor listener." % sys.argv[0]
return
# 死循环监控。
while True:
# 告诉supervisor进入ready状态。
write_stdout('READY\n')
# 获取supervisor捕捉并整理的数据
line = sys.stdin.readline()
# 获取当前信息的基本信息。
eventname:PROCESS_STATE_RUNNING len:67
headers = dict([ x.split(':') for x in line.split() ]) # ex: ver:3.0 server:supervisor serial:2951 pool:listener poolserial:261
# 获取准确完整的数据,从headers得到长度并切割
process_data = sys.stdin.read(int(headers['len'])) # processname:mysql groupname:mysql from_state:RUNNING expected:0 pid:29110
process_info = dict([ x.split(':') for x in process_data.split()])
# 这里可以输出到event监听事件的日志里。使用stderr输出到日志
write_stderr(str(process_info))
# 整理数据
send_data = [
"服务器%s进程" %(HOSTNAME),
"运行服务:%s,pid:%d" %(process_info['processname'], int(process_info.get('pid',0))),
"上次状态:%s" %(process_info['from_state']),
"当前状态:%s" %(EVENT_STATE[headers['eventname']]),
"发生时间:%s" %(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
]
# 讲数据发出去
rs = send_msg("\n".join(send_data).encode('utf-8'))
# 发出结果记录到eventlisten日志进行查看
write_stderr(rs)
# 告诉supervisor已经整理完。并进入下一个循环
write_stdout('RESULT 2\nOK')
if __name__ == '__main__':
main()